docs(cannamanage): update wiki pages and sprint plans + brand pipeline doc
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**Phase 4a | Document 6 of 7**
|
||||
**Date:** 2026-04-06
|
||||
**Stack:** Spring Boot 3.x · PrimeFaces JSF · PostgreSQL
|
||||
**Stack:** Spring Boot 3.x · React/Vite SPA · PostgreSQL
|
||||
|
||||
---
|
||||
|
||||
@@ -45,24 +45,26 @@
|
||||
|
||||
### 1.3 Component Library
|
||||
|
||||
All UI components come from **PrimeFaces 13.x** (JSF-based). No external React/Angular dependencies in MVP.
|
||||
The frontend is a **React/Vite SPA** with no PrimeFaces or JSF dependency. Component primitives come from [shadcn/ui](https://ui.shadcn.com/) (Radix UI + Tailwind CSS). This gives full control over styling, accessibility, and mobile responsiveness without JSF's lifecycle overhead.
|
||||
|
||||
| Component | Usage |
|
||||
|---|---|
|
||||
| `p:panel` | Section containers, card wrappers |
|
||||
| `p:dataTable` with `p:column` | Tabular data: distributions, members, batches |
|
||||
| `p:paginator` | Pagination on all tables |
|
||||
| `p:inputText` | Single-line text fields |
|
||||
| `p:inputNumber` | Weight inputs (gram precision) |
|
||||
| `p:selectOneMenu` | Dropdown selects (member, strain, batch) |
|
||||
| `p:calendar` | Date range pickers for reports |
|
||||
| `p:progressBar` | Quota consumption display |
|
||||
| `p:commandButton` | Primary and secondary actions |
|
||||
| `p:confirmDialog` | Dangerous actions (recall, delete) |
|
||||
| `p:messages` / `p:message` | Inline validation errors |
|
||||
| `p:badge` | Status indicators (AVAILABLE, LOW, RECALLED) |
|
||||
| `p:sidebar` | Mobile nav drawer (member portal) |
|
||||
| `p:dialog` | Modal overlays |
|
||||
> **Why not PrimeFaces?** JSF/PrimeFaces is a server-side component model ill-suited to the modern REST API backend we're building. It tightly couples UI lifecycle to the backend, makes mobile responsiveness painful, and creates a hiring bottleneck. React is the right tool here. PrimeFaces is a fine choice for internal enterprise apps — not for a commercial SaaS.
|
||||
|
||||
| Component | Library | Usage |
|
||||
|---|---|---|
|
||||
| `Card` / `Panel` | shadcn/ui | Section containers |
|
||||
| `DataTable` | TanStack Table v8 | Distributions, members, batches — virtualized |
|
||||
| `Pagination` | shadcn/ui Pagination | All tables |
|
||||
| `Input` | shadcn/ui Input | Single-line text fields |
|
||||
| `NumberInput` | react-number-format | Weight inputs (gram precision, min/max) |
|
||||
| `Select` | shadcn/ui Select | Dropdown selects (member, strain, batch) |
|
||||
| `DatePicker` | shadcn/ui Calendar | Date range pickers for reports |
|
||||
| `Progress` | shadcn/ui Progress | Quota consumption bar |
|
||||
| `Button` | shadcn/ui Button | Primary and secondary actions |
|
||||
| `AlertDialog` | shadcn/ui AlertDialog | Dangerous actions (recall) |
|
||||
| `Toast` | sonner | Success/error notifications |
|
||||
| `Badge` | shadcn/ui Badge | Status indicators (AVAILABLE, LOW, RECALLED) |
|
||||
| `Sheet` | shadcn/ui Sheet | Mobile nav drawer |
|
||||
| `Dialog` | shadcn/ui Dialog | Modal overlays |
|
||||
|
||||
### 1.4 Layout Grid
|
||||
|
||||
@@ -118,13 +120,13 @@ All UI components come from **PrimeFaces 13.x** (JSF-based). No external React/A
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| KPI Cards | `p:panel` with custom CSS | Auto-refreshed via `@poll` every 60s |
|
||||
| Recent Distributions table | `p:dataTable` (5 rows, no paginator) | Row click → navigate to distribution detail |
|
||||
| Member column link | `p:commandLink` | Navigate to `/admin/members/{id}` |
|
||||
| `+ New Entry` button | `p:commandButton` style="primary" | Navigate to `/admin/distributions/new` |
|
||||
| Trend indicators | Custom CSS `<span>` | Green ▲ / Red ▼ with delta value |
|
||||
| KPI Cards | shadcn/ui Card | Auto-refreshed via `useQuery` (react-query, 60s stale) |
|
||||
| Recent Distributions table | TanStack Table (5 rows) | Row click → navigate to distribution detail |
|
||||
| Member column link | React Router `<Link>` | Navigate to `/admin/members/{id}` |
|
||||
| `+ New Entry` button | shadcn/ui Button variant="default" | Navigate to `/admin/distributions/new` |
|
||||
| Trend indicators | Tailwind `text-green-600` / `text-red-600` | ▲/▼ with delta value |
|
||||
|
||||
---
|
||||
|
||||
@@ -178,14 +180,14 @@ The quota progress bar updates live as the weight field changes (via `f:ajax eve
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Member search | `p:selectOneMenu` with `p:ajax` filter | Filters on type, shows name + member no. |
|
||||
| Strain/Batch dropdown | `p:selectOneMenu` | Populated after member selection; shows only `AVAILABLE` batches |
|
||||
| Weight input | `p:inputNumber` min=`0.1` max=`25.0` step=`0.1` | Triggers quota recalculation on blur |
|
||||
| Quota bar | `p:progressBar` with dynamic `value` | Color class applied via `styleClass` computed in backing bean |
|
||||
| Submit | `p:commandButton` | Disabled via `disabled="#{bean.quotaExceeded}"` |
|
||||
| Cancel | `p:link` | Returns to distribution log without saving |
|
||||
| Member search | shadcn/ui Combobox | `useQuery` debounced search; shows name + member no. |
|
||||
| Strain/Batch dropdown | shadcn/ui Select | Populated after member selection; filters `AVAILABLE` batches |
|
||||
| Weight input | react-number-format | min=0.1 max=25.0 step=0.1; triggers quota recalculation via `onChange` |
|
||||
| Quota bar | shadcn/ui Progress | Color class via `cn()` utility computed in component state |
|
||||
| Submit | shadcn/ui Button | `disabled={quotaExceeded}` from react state |
|
||||
| Cancel | React Router `<Link>` | Returns to distribution log without saving |
|
||||
|
||||
---
|
||||
|
||||
@@ -229,15 +231,15 @@ The quota progress bar updates live as the weight field changes (via `f:ajax eve
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Strain filter | `p:inputText` with `filterBy` | Filters table client-side on keyup |
|
||||
| Status filter | `p:selectOneMenu` | Filters table rows by status value |
|
||||
| Batch table | `p:dataTable` lazy=`true` | Server-side pagination, 10 rows/page |
|
||||
| Status badge | Custom CSS `<span class="badge badge-{status}">` | Icon + text label (not color alone) |
|
||||
| Recall button | `p:commandButton` styleClass=`p-button-danger` | Opens `p:confirmDialog` before executing |
|
||||
| Confirm dialog | `p:confirmDialog` | "Recall batch B-12 (OG Kush, 850g)? This cannot be undone." |
|
||||
| Add Batch | `p:commandButton` | Opens `p:dialog` with batch entry form |
|
||||
| Strain filter | shadcn/ui Input | Filters TanStack table client-side via `columnFilters` state |
|
||||
| Status filter | shadcn/ui Select | Filters table rows by status value |
|
||||
| Batch table | TanStack Table | Server-side pagination via `manualPagination`, 10 rows/page |
|
||||
| Status badge | shadcn/ui Badge variant mapped | Icon + text label (not color alone) |
|
||||
| Recall button | shadcn/ui Button variant="destructive" | Opens shadcn/ui AlertDialog before executing |
|
||||
| Confirm dialog | shadcn/ui AlertDialog | "Recall batch B-12 (OG Kush, 850g)? This cannot be undone." |
|
||||
| Add Batch | shadcn/ui Button | Opens shadcn/ui Dialog with batch entry form |
|
||||
|
||||
---
|
||||
|
||||
@@ -287,15 +289,15 @@ The quota progress bar updates live as the weight field changes (via `f:ajax eve
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Month selector | `p:selectOneMenu` | Months Jan–Dec |
|
||||
| Year selector | `p:selectOneMenu` | Current year ± 2 |
|
||||
| Generate button | `p:commandButton` | Calls report service; shows spinner; renders PDF thumbnail |
|
||||
| PDF preview | `<iframe>` embedding `/report/preview?month=3&year=2026` | Generated by iText 7 in `cannamanage-report` module |
|
||||
| Download PDF | `p:commandButton` | Streams PDF response from REST endpoint |
|
||||
| Download CSV | `p:commandButton` | Streams CSV response (member-level data) |
|
||||
| Summary table | `p:dataTable` | Computed compliance metrics; zero violations = green row |
|
||||
| Month selector | shadcn/ui Select | Months Jan–Dec |
|
||||
| Year selector | shadcn/ui Select | Current year ± 2 |
|
||||
| Generate button | shadcn/ui Button | Calls report API; shows loading spinner; renders PDF thumbnail |
|
||||
| PDF preview | `<iframe>` embedding `/api/v1/reports/preview?month=3&year=2026` | Generated by iText 7 backend |
|
||||
| Download PDF | shadcn/ui Button | `window.open(reportUrl)` — streams PDF from REST endpoint |
|
||||
| Download CSV | shadcn/ui Button | `window.open(csvUrl)` — streams CSV from REST endpoint |
|
||||
| Summary table | TanStack Table | Compliance metrics; zero violations row has `text-green-600` |
|
||||
|
||||
---
|
||||
|
||||
@@ -354,12 +356,12 @@ Quantities, batch codes, and THC/CBD percentages are **not exposed** in the memb
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Quota circle | Custom CSS radial progress (`conic-gradient`) | Computed from monthly total; color matches threshold rules |
|
||||
| Quota bar | `p:progressBar` | Same color logic as admin distribution form |
|
||||
| History table | `p:dataTable` | Last 10 distributions; sorted newest first; no pagination in MVP |
|
||||
| Strains table | `p:dataTable` | `status` column: text + icon only, no quantities |
|
||||
| Quota bar | shadcn/ui Progress | Same color logic as admin distribution form |
|
||||
| History table | TanStack Table | Last 10 distributions; sorted newest first; no pagination in MVP |
|
||||
| Strains table | TanStack Table | `status` column: text + icon only, no quantities |
|
||||
|
||||
---
|
||||
|
||||
@@ -367,6 +369,64 @@ Quantities, batch codes, and THC/CBD percentages are **not exposed** in the memb
|
||||
|
||||
> *No mockup image — ASCII wireframe only.*
|
||||
|
||||
---
|
||||
|
||||
### Screen 7 — Staff Management (Admin)
|
||||
|
||||
> *Core feature — not deferred to v2.*
|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
|
||||
├────────────┬────────────────────────────────────────────────────────┤
|
||||
│ │ Settings › Staff Members [+ Add Staff] │
|
||||
│ 📊 Dashbrd│ │
|
||||
│ │ ┌──────────────────────────────────────────────────┐ │
|
||||
│ 👥 Members│ │ Name │ Role Template │ Permissions │ Act│ │
|
||||
│ │ ├─────────────────┼───────────────┼─────────────┼────┤ │
|
||||
│ 📋 Distrib│ │ Lisa Schmidt │ Ausgabe │ 3 of 8 │[✎][⛔]│
|
||||
│ │ │ Tom Weber │ Lager │ 4 of 8 │[✎][⛔]│
|
||||
│ 📦 Stock │ │ Sandra Müller │ Vorstand │ 7 of 8 │[✎][⛔]│
|
||||
│ │ └──────────────────────────────────────────────────┘ │
|
||||
│ 📄 Reports│ │
|
||||
│ │ ┌─── Add / Edit Staff ──────────────────────────────┐ │
|
||||
│ ✅ Complian│ │ Name: _______________ Email: _______________ │ │
|
||||
│ │ │ │ │
|
||||
│ 👤 Staff │ │ Role Template: [ Ausgabe ▼ ] (pre-fills below) │ │
|
||||
│ │ │ │ │
|
||||
│ ⚙ Settings│ │ Permissions: │ │
|
||||
│ │ │ ☑ Record Distribution ☑ View Member List │ │
|
||||
│ │ │ ☑ View Member Quota ☐ Add Member │ │
|
||||
│ │ │ ☐ View Stock ☐ Record Stock In │ │
|
||||
│ │ │ ☐ View Compliance Report ☐ Manage Grow Calendar │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ [ Save Staff Member ] [ Cancel ] │ │
|
||||
│ │ └────────────────────────────────────────────────────┘ │
|
||||
└────────────┴────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Design Decisions
|
||||
|
||||
- **Admin sees everything.** The staff management screen is only accessible with `ROLE_CLUB_ADMIN`. Staff accounts cannot modify their own permissions.
|
||||
- **DSGVO principle of least privilege.** Each staff member only sees the data their role requires. A distribution desk worker (`Ausgabe`) does not see cultivation calendar or full stock levels — only what they need to hand out product.
|
||||
- **Pre-created role templates** reduce admin setup time. Templates are editable — they just pre-fill the permission checkboxes.
|
||||
- **Staff ≠ reduced admin.** Staff accounts do not have access to billing, club settings, or staff management. Even a "Vorstand" staff member cannot create other staff accounts.
|
||||
- **Audit trail.** All distributions recorded by staff include `recorded_by = staffUserId` so it's clear who did what.
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Staff table | TanStack Table | Shows name, role template, permission count, actions |
|
||||
| Role template dropdown | shadcn/ui Select | Pre-populates permission checkboxes on selection |
|
||||
| Permission checkboxes | shadcn/ui Checkbox | Individual overrides after template selection |
|
||||
| Save | shadcn/ui Button | POST/PUT `/api/v1/staff` with `{ permissions: [...] }` |
|
||||
| Deactivate | shadcn/ui Button variant="destructive" | Soft-deletes staff account; data retained for audit |
|
||||
|
||||
---
|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
@@ -403,12 +463,12 @@ Quantities, batch codes, and THC/CBD percentages are **not exposed** in the memb
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | PrimeFaces | Behavior |
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Email field | `p:inputText` with `required="true"` | Bean Validation `@Email` |
|
||||
| Password field | `p:password` feedback=`false` | No strength meter on login |
|
||||
| Login button | `p:commandButton` | Submit form; shows `p:messages` on failure |
|
||||
| Error message | `p:messages` | "Invalid email or password." (never specific about which field failed) |
|
||||
| Email field | shadcn/ui Input type="email" | HTML5 validation + react-hook-form `@Email` |
|
||||
| Password field | shadcn/ui Input type="password" | No strength meter on login |
|
||||
| Login button | shadcn/ui Button | Submit via react-hook-form; shows error toast on failure |
|
||||
| Error message | sonner toast | "Invalid email or password." (never specific about which field failed) |
|
||||
|
||||
---
|
||||
|
||||
@@ -419,6 +479,7 @@ graph TD
|
||||
Root["CannaManage Root"]
|
||||
Root --> AdminPortal["Admin Portal /admin/"]
|
||||
Root --> MemberPortal["Member Portal /member/"]
|
||||
Root --> StaffPortal["Staff Portal /staff/"]
|
||||
|
||||
AdminPortal --> AdminDash["Dashboard (default)"]
|
||||
AdminPortal --> Members["Members"]
|
||||
@@ -436,9 +497,14 @@ graph TD
|
||||
Reports --> RecallReport["Batch Recall Report"]
|
||||
AdminPortal --> Compliance["Compliance"]
|
||||
Compliance --> PreventionOfficer["Prevention Officer Info"]
|
||||
AdminPortal --> StaffMgmt["Staff Members"]
|
||||
StaffMgmt --> StaffList["Staff List"]
|
||||
StaffMgmt --> StaffNew["Add/Edit Staff"]
|
||||
AdminPortal --> Settings["Settings"]
|
||||
Settings --> ClubProfile["Club Profile"]
|
||||
|
||||
StaffPortal --> StaffDash["Staff Dashboard\n(permissions-filtered)"]
|
||||
|
||||
MemberPortal --> MemberDash["Dashboard / Quota"]
|
||||
MemberPortal --> DistHistory["Distribution History"]
|
||||
MemberPortal --> StockAvail["Stock Availability"]
|
||||
@@ -460,7 +526,11 @@ graph TD
|
||||
| `/admin/reports/members` | Member data export | `ROLE_ADMIN` |
|
||||
| `/admin/reports/recall` | Recall report | `ROLE_ADMIN` |
|
||||
| `/admin/compliance` | Prevention officer | `ROLE_ADMIN` |
|
||||
| `/admin/staff` | Staff list | `ROLE_ADMIN` |
|
||||
| `/admin/staff/new` | Create staff account | `ROLE_ADMIN` |
|
||||
| `/admin/staff/{id}` | Edit staff permissions | `ROLE_ADMIN` |
|
||||
| `/admin/settings` | Club settings | `ROLE_ADMIN` |
|
||||
| `/staff/dashboard` | Staff home (permissions-filtered) | `ROLE_STAFF` |
|
||||
| `/member/dashboard` | Member quota view | `ROLE_MEMBER` |
|
||||
| `/member/distributions` | Personal history | `ROLE_MEMBER` |
|
||||
| `/member/stock` | Strain availability | `ROLE_MEMBER` |
|
||||
@@ -469,34 +539,34 @@ graph TD
|
||||
|
||||
## 5. Responsive Design Notes
|
||||
|
||||
### MVP (v1) — Desktop-First
|
||||
### MVP (v1) — Tailwind Breakpoints
|
||||
|
||||
Target viewport: **1024px+**. PrimeFaces responsive grid (`p:panelGrid` with responsive columns, `ui-g-12 ui-md-6 ui-lg-4`) handles most layout adaptation down to tablet without custom media queries.
|
||||
The React/Vite SPA uses **Tailwind CSS** breakpoints throughout. The switch from PrimeFaces means we no longer depend on JSF's `ui-g-*` responsive grid — Tailwind's `sm:` / `md:` / `lg:` utilities apply cleanly to every component.
|
||||
|
||||
| Breakpoint | Behavior |
|
||||
|---|---|
|
||||
| `≥ 1280px` | Full layout — sidebar + content side-by-side |
|
||||
| `1024–1279px` | Sidebar collapses to icon-only (60px); tooltips on hover |
|
||||
| `768–1023px` | Sidebar hidden; hamburger menu in top navbar |
|
||||
| `< 768px` | Admin portal degraded (tables scroll horizontally) |
|
||||
| Breakpoint | Tailwind prefix | Admin Portal | Member Portal |
|
||||
|---|---|---|---|
|
||||
| `≥ 1280px` | `xl:` | Full layout — sidebar + content | Two-column: quota left, history right |
|
||||
| `1024–1279px` | `lg:` | Sidebar collapses to icons (60px) | Two-column (narrower) |
|
||||
| `768–1023px` | `md:` | Sidebar hidden; hamburger sheet | Single-column, full-width cards |
|
||||
| `< 768px` | `sm:` / base | Admin: horizontal table scroll | Member: compact quota ring, condensed table |
|
||||
|
||||
### Member Portal — Mobile-First from Day One
|
||||
|
||||
Members will typically check quota status on their phone. The member portal is designed mobile-first regardless of MVP/v2 timeline.
|
||||
Members will typically check quota status on their phone. The member portal uses `flex-col` mobile-first layout with `md:flex-row` for wider viewports — no breakpoint-specific class sprawl.
|
||||
|
||||
| Breakpoint | Behavior |
|
||||
|---|---|
|
||||
| `≥ 1024px` | Two-column layout: quota circle left, history right |
|
||||
| `768–1023px` | Single-column, full-width cards |
|
||||
| `375–767px` | Single-column, compact quota ring, condensed table |
|
||||
| `< 375px` | Minimum supported; no horizontal scroll |
|
||||
### Responsive Conventions (React/Tailwind)
|
||||
|
||||
- No inline styles — use Tailwind utilities exclusively
|
||||
- `cn()` utility (clsx + tailwind-merge) for conditional class composition
|
||||
- Tables on mobile: horizontal scroll wrapper `overflow-x-auto` on `<div>` wrapping `<table>`
|
||||
- All modals and sheets use `shadcn/ui Dialog` / `Sheet` — these are already mobile-friendly (viewport-aware positioning)
|
||||
- Touch targets: all interactive elements `min-h-[44px]` and `min-w-[44px]` per WCAG 2.5.5
|
||||
|
||||
### v2 Roadmap
|
||||
|
||||
- PWA manifest + service worker (offline quota display)
|
||||
- 768px and 375px explicit breakpoints with design tokens
|
||||
- Touch-friendly `p:sidebar` for mobile member nav
|
||||
- Push notifications for low quota warnings
|
||||
- Per-club subdomain routing (`clubname.cannamanage.de`)
|
||||
|
||||
---
|
||||
|
||||
@@ -516,11 +586,11 @@ CannaManage targets **WCAG 2.1 AA** compliance across both portals.
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
- All `p:inputText` / `p:inputNumber` fields have `<label>` with `for` attribute
|
||||
- All `Input` / `NumberInput` fields have `<label>` with `htmlFor` (React) — Radix UI enforces this automatically for shadcn/ui form fields
|
||||
- `aria-label` set on icon-only buttons (e.g., recall action column)
|
||||
- `aria-live="polite"` region on quota bar — announces percentage changes
|
||||
- `aria-describedby` links compliance warning messages to the weight input
|
||||
- PrimeFaces generates `role="grid"` and `aria-rowcount` on all data tables
|
||||
- TanStack Table exposes `role="grid"` and `aria-rowcount` via `getTableProps()`
|
||||
|
||||
### Color Independence
|
||||
|
||||
|
||||
Reference in New Issue
Block a user