wiki: add 06 Wireframes
+620
@@ -0,0 +1,620 @@
|
||||
# CannaManage — Wireframes & UI Mockups
|
||||
|
||||
**Phase 4a | Document 6 of 7**
|
||||
**Date:** 2026-04-06
|
||||
**Stack:** Spring Boot 3.x · React/Vite SPA · PostgreSQL
|
||||
|
||||
---
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Design System Overview](#1-design-system-overview)
|
||||
2. [Admin Portal Screens](#2-admin-portal-screens)
|
||||
3. [Member Portal Screens](#3-member-portal-screens)
|
||||
4. [Navigation & Information Architecture](#4-navigation--information-architecture)
|
||||
5. [Responsive Design Notes](#5-responsive-design-notes)
|
||||
6. [Accessibility](#6-accessibility)
|
||||
|
||||
---
|
||||
|
||||
## 1. Design System Overview
|
||||
|
||||
### 1.1 Color Palette
|
||||
|
||||
| Token | Hex | Usage |
|
||||
|---|---|---|
|
||||
| `--color-primary` | `#2D5016` | Sidebar background, primary buttons, active nav items |
|
||||
| `--color-primary-medium` | `#4A7C28` | Hover states, section headers, badge outlines |
|
||||
| `--color-accent` | `#8BC34A` | Highlights, progress bars filled, success indicators |
|
||||
| `--color-bg` | `#F5F5F5` | Page background, card backgrounds |
|
||||
| `--color-text` | `#1A1A1A` | Body text, table cell content |
|
||||
| `--color-warning` | `#FF6B35` | Quota >80%, low stock, warnings |
|
||||
| `--color-error` | `#D32F2F` | Quota exceeded, recalled batches, destructive actions |
|
||||
| `--color-white` | `#FFFFFF` | Sidebar text, button labels on dark bg, card surfaces |
|
||||
|
||||
### 1.2 Typography
|
||||
|
||||
| Element | Font | Size | Weight |
|
||||
|---|---|---|---|
|
||||
| H1 — Page title | Inter | 24px | 600 |
|
||||
| H2 — Section heading | Inter | 18px | 600 |
|
||||
| H3 — Card title | Inter | 14px | 600 |
|
||||
| Body / table rows | Inter | 14px | 400 |
|
||||
| Caption / label | Inter | 12px | 400 |
|
||||
| Mono (codes, IDs) | JetBrains Mono | 13px | 400 |
|
||||
|
||||
### 1.3 Component Library
|
||||
|
||||
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.
|
||||
|
||||
> **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
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ TOP NAVBAR (56px) club name · avatar · logout │
|
||||
├──────────────┬─────────────────────────────────────┤
|
||||
│ │ │
|
||||
│ SIDEBAR │ MAIN CONTENT │
|
||||
│ (240px) │ (fluid, min 784px) │
|
||||
│ fixed │ │
|
||||
│ │ │
|
||||
└──────────────┴─────────────────────────────────────┘
|
||||
```
|
||||
|
||||
- **Sidebar:** fixed left, `#2D5016` background, white nav labels with `#8BC34A` icons
|
||||
- **Top Navbar:** `#FFFFFF` with bottom border `#E0E0E0`, breadcrumb left, user controls right
|
||||
- **Main Content:** `#F5F5F5` background, 24px padding, max content width 1200px centered
|
||||
|
||||
---
|
||||
|
||||
## 2. Admin Portal Screens
|
||||
|
||||
### Screen 1 — Admin Dashboard
|
||||
|
||||

|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
|
||||
├────────────┬────────────────────────────────────────────────────────┤
|
||||
│ │ Dashboard 🗓 April 2026 │
|
||||
│ 📊 Dashboard◄│ │
|
||||
│ │ ┌──────────────┐ ┌──────────────┐ ┌───────────────┐ │
|
||||
│ 👥 Members│ │ Total Members│ │ Distributions│ │ Stock Available│ │
|
||||
│ │ │ │ │ This Month │ │ │ │
|
||||
│ 📋 Distrib│ │ 142 │ │ 87 │ │ 3,240 g │ │
|
||||
│ │ │ ▲ +3 MoM │ │ ▲ +12 MoM │ │ ▼ -800g MoM │ │
|
||||
│ 📦 Stock │ └──────────────┘ └──────────────┘ └───────────────┘ │
|
||||
│ │ │
|
||||
│ 📄 Reports│ Recent Distributions [+ New Entry] │
|
||||
│ │ ┌─────────────────────────────────────────────────┐ │
|
||||
│ ✅ Complian│ │ Member │ Strain │ Qty │ Date │ ✓ │ │
|
||||
│ │ ├─────────────┼─────────────┼───────┼───────┼────┤ │
|
||||
│ ⚙ Settings│ │ Müller, A. │ OG Kush B12 │ 5.0g │ 06.04 │ ✓ │ │
|
||||
│ │ │ Schmidt, K. │ Amnesia H09 │ 3.5g │ 06.04 │ ✓ │ │
|
||||
│ │ │ Weber, T. │ OG Kush B12 │ 7.0g │ 05.04 │ ✓ │ │
|
||||
│ │ │ … │ │ │ │ │ │
|
||||
└────────────┴──┴─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
### Screen 2 — Distribution Recording Form
|
||||
|
||||

|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
|
||||
├────────────┬────────────────────────────────────────────────────────┤
|
||||
│ │ Distributions › New Distribution │
|
||||
│ 📊 Dashbrd│ │
|
||||
│ │ ┌──────────────────────────────────────────────────┐ │
|
||||
│ 👥 Members│ │ Member * │ │
|
||||
│ │ │ ┌──────────────────────────────────────────┐ │ │
|
||||
│ 📋 Distrib◄│ │ │ 🔍 Search by name or member no. │ │ │
|
||||
│ │ │ └──────────────────────────────────────────┘ │ │
|
||||
│ 📦 Stock │ │ │ │
|
||||
│ │ │ Strain / Batch * │ │
|
||||
│ 📄 Reports│ │ ┌──────────────────────────────────────────┐ │ │
|
||||
│ │ │ │ Select available batch ▼ │ │ │
|
||||
│ ✅ Complian│ │ └──────────────────────────────────────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ ⚙ Settings│ │ Weight (grams) * │ │
|
||||
│ │ │ ┌──────────┐ │ │
|
||||
│ │ │ │ 0.0 g │ ← p:inputNumber min=0.1 max=25 │ │
|
||||
│ │ │ └──────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ Monthly Quota — Müller, Anna │ │
|
||||
│ │ │ ████████████░░░░░░░░ 32.5g / 50g 65% │ │
|
||||
│ │ │ [████████████████░░░] <- p:progressBar │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ [ Record Distribution ] [Cancel] │ │
|
||||
│ │ └──────────────────────────────────────────────────┘ │
|
||||
└────────────┴────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Compliance UX — Real-Time Quota Indicator
|
||||
|
||||
The quota progress bar updates live as the weight field changes (via `f:ajax event="keyup"`):
|
||||
|
||||
| Quota Used After Distribution | Bar Color | Submit Button | Message |
|
||||
|---|---|---|---|
|
||||
| 0–79% | `#8BC34A` (green) | Enabled | — |
|
||||
| 80–99% | `#FF6B35` (orange) | Enabled | "⚠ Approaching monthly limit" |
|
||||
| 100% | `#D32F2F` (red) | **Disabled** | "🚫 Monthly limit reached (50g)" |
|
||||
| Over-21 member, >30g monthly | `#D32F2F` (red) | **Disabled** | "🚫 Under-21 limit reached (30g)" |
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
### Screen 3 — Stock Management
|
||||
|
||||

|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
|
||||
├────────────┬────────────────────────────────────────────────────────┤
|
||||
│ │ Stock Management [+ Add Batch] │
|
||||
│ 📊 Dashbrd│ │
|
||||
│ │ ┌──────────────────────┐ ┌────────────────────┐ │
|
||||
│ 👥 Members│ │ 🔍 Filter by strain │ │ Status: All ▼ │ │
|
||||
│ │ └──────────────────────┘ └────────────────────┘ │
|
||||
│ 📋 Distrib│ │
|
||||
│ │ ┌───────────────────────────────────────────────────┐ │
|
||||
│ 📦 Stock ◄│ │ Strain │Batch│THC% │CBD%│ Qty │Status│Act │ │
|
||||
│ │ ├──────────────┼─────┼─────┼────┼───────┼──────┼────┤ │
|
||||
│ 📄 Reports│ │ OG Kush │B-12 │ 19% │ 1% │ 850g │ ● │[R] │ │
|
||||
│ │ │ Amnesia Haze │H-09 │ 22% │<1% │ 72g │ ⚠ │[R] │ │
|
||||
│ ✅ Complian│ │ Blue Dream │D-05 │ 17% │ 2% │ 0g │ — │[R] │ │
|
||||
│ │ │ Hindu Kush │K-21 │ 8% │15% │ 340g │ ✓ │[R] │ │
|
||||
│ ⚙ Settings│ │ AK-47 #4 │A-03 │ 20% │ 1% │ RECALLED │ ⛔ │[R] │ │
|
||||
│ │ └───────────────────────────────────────────────────┘ │
|
||||
│ │ [◄ 1 2 3 … ►] Showing 1-10/42 │
|
||||
└────────────┴────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Status Badges
|
||||
|
||||
| Badge | Color | Icon | Condition |
|
||||
|---|---|---|---|
|
||||
| `AVAILABLE` | `#4A7C28` bg | ✓ checkmark | `qty > 100g` and not recalled |
|
||||
| `LOW` | `#FF6B35` bg | ⚠ warning | `0 < qty ≤ 100g` |
|
||||
| `EXHAUSTED` | `#9E9E9E` bg | — dash | `qty = 0` |
|
||||
| `RECALLED` | `#D32F2F` bg | ⛔ stop | `recall_date IS NOT NULL` |
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
### Screen 4 — Compliance Report Generation
|
||||
|
||||

|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Grüne Oase Berlin e.V. 👤 Max M. [⏻] │
|
||||
├────────────┬────────────────────────────────────────────────────────┤
|
||||
│ │ Reports › Monthly Compliance Report │
|
||||
│ 📊 Dashbrd│ │
|
||||
│ │ ┌─────────────────────────────────────────────────┐ │
|
||||
│ 👥 Members│ │ Reporting Period │ │
|
||||
│ │ │ Month: [ March ▼ ] Year: [ 2026 ▼ ] │ │
|
||||
│ 📋 Distrib│ │ [ Generate Report ] │ │
|
||||
│ │ └─────────────────────────────────────────────────┘ │
|
||||
│ 📦 Stock │ │
|
||||
│ │ ┌─────────────────────────────────────────────────┐ │
|
||||
│ 📄 Reports◄│ │ PDF PREVIEW │ │
|
||||
│ │ │ ┌─────────────────────────────────────────┐ │ │
|
||||
│ ✅ Complian│ │ │ 🌿 CannaManage — Monthly Report Mar 2026 │ │ │
|
||||
│ │ │ │ Club: Grüne Oase Berlin e.V. │ │ │
|
||||
│ ⚙ Settings│ │ │ ─────────────────────────────────────── │ │ │
|
||||
│ │ │ │ Total Members: 142 │ │ │
|
||||
│ │ │ │ Active Members (distributed): 87 │ │ │
|
||||
│ │ │ │ Total Distributed: 435.5g │ │ │
|
||||
│ │ │ └─────────────────────────────────────────┘ │ │
|
||||
│ │ │ │ │
|
||||
│ │ │ [⬇ Download PDF] [⬇ Download CSV] │ │
|
||||
│ │ └─────────────────────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ │ Summary Table │
|
||||
│ │ ┌────────────────────────────────────────────────┐ │
|
||||
│ │ │ Metric │ Value │ Limit │ │
|
||||
│ │ ├──────────────────────┼───────────┼─────────────┤ │
|
||||
│ │ │ Members >50g/month │ 0 │ Must be 0 │ │
|
||||
│ │ │ Members >30g (U21) │ 0 │ Must be 0 │ │
|
||||
│ │ │ Recalled Batches │ 1 │ — (info) │ │
|
||||
│ │ │ Avg grams / member │ 5.0g │ — │ │
|
||||
│ │ └────────────────────────────────────────────────┘ │
|
||||
└────────────┴────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| 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` |
|
||||
|
||||
---
|
||||
|
||||
## 3. Member Portal Screens
|
||||
|
||||
### Screen 5 — Member Dashboard / Quota View
|
||||
|
||||

|
||||
|
||||
#### ASCII Wireframe
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────┐
|
||||
│ 🌿 CannaManage Anna Müller #M-0042 │
|
||||
│ ────────────────────────────────────────────── │
|
||||
│ │
|
||||
│ Monthly Quota Remaining │
|
||||
│ │
|
||||
│ ╭───────────────╮ │
|
||||
│ │ │ │
|
||||
│ │ 17.5 g │ │
|
||||
│ │ remaining │ │
|
||||
│ │ │ │
|
||||
│ │ of 50g/month │ │
|
||||
│ ╰───────────────╯ │
|
||||
│ ▓▓▓▓▓▓▓▓▓▓▓░░░░░░░ 65% used │
|
||||
│ │
|
||||
│ Distribution History │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Date │ Strain │ Quantity │ │
|
||||
│ ├────────────┼──────────────┼────────────────┤ │
|
||||
│ │ 06.04.2026 │ OG Kush │ 5.0g │ │
|
||||
│ │ 02.04.2026 │ Amnesia Haze │ 12.5g │ │
|
||||
│ │ 28.03.2026 │ OG Kush │ 15.0g │ │
|
||||
│ └────────────┴──────────────┴────────────────┘ │
|
||||
│ │
|
||||
│ Available Strains │
|
||||
│ ┌────────────────────────────────────────────┐ │
|
||||
│ │ Strain │ Availability │ │
|
||||
│ ├──────────────┼─────────────────────────────┤ │
|
||||
│ │ OG Kush │ ● Available │ │
|
||||
│ │ Amnesia Haze │ ⚠ Limited │ │
|
||||
│ │ Hindu Kush │ ● Available │ │
|
||||
│ └────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
└────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Compliance Note — Available Strains Display
|
||||
|
||||
Per CanG §§6–7, members may NOT see specific batch quantities or total stock levels. The **Available Strains** table shows only:
|
||||
- Strain name
|
||||
- Availability status (Available / Limited / Unavailable)
|
||||
|
||||
Quantities, batch codes, and THC/CBD percentages are **not exposed** in the member portal.
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| Quota circle | Custom CSS radial progress (`conic-gradient`) | Computed from monthly total; color matches threshold rules |
|
||||
| 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 |
|
||||
|
||||
---
|
||||
|
||||
### Screen 6 — Member Login
|
||||
|
||||
> *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
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────┐
|
||||
│ │
|
||||
│ 🌿 CannaManage │
|
||||
│ │
|
||||
│ ┌──────────────────────────────────┐ │
|
||||
│ │ E-Mail Address │ │
|
||||
│ │ ┌────────────────────────────┐ │ │
|
||||
│ │ │ you@example.com │ │ │
|
||||
│ │ └────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ Password │ │
|
||||
│ │ ┌────────────────────────────┐ │ │
|
||||
│ │ │ •••••••••••• │ │ │
|
||||
│ │ └────────────────────────────┘ │ │
|
||||
│ │ │ │
|
||||
│ │ [ ████ Log In ████████████ ] │ │
|
||||
│ └──────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Problems logging in? │
|
||||
│ Contact your club administrator. │
|
||||
│ │
|
||||
└──────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
#### Design Decisions
|
||||
|
||||
- **No self-registration link** — member accounts are created exclusively by admins via the admin portal. The login page has no "Create account" or "Sign up" flow.
|
||||
- **No forgot-password link** — password resets are initiated by the club admin only. The login page directs users to contact their admin, avoiding email-based reset flows that would require verified email infrastructure in MVP.
|
||||
- **No social login** — DSGVO compliance and club accountability require traceable credential management.
|
||||
- **Form submission:** POST to `/login` (Spring Security form login), redirect to `/member/dashboard` on success.
|
||||
|
||||
#### Components & Behavior
|
||||
|
||||
| Component | Library | Behavior |
|
||||
|---|---|---|
|
||||
| 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) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Navigation & Information Architecture
|
||||
|
||||
```mermaid
|
||||
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"]
|
||||
Members --> MemberList["Member List"]
|
||||
Members --> MemberDetail["Member Detail"]
|
||||
AdminPortal --> Distributions["Distributions"]
|
||||
Distributions --> DistLog["Distribution Log"]
|
||||
Distributions --> NewDist["New Distribution"]
|
||||
AdminPortal --> Stock["Stock"]
|
||||
Stock --> Strains["Strains"]
|
||||
Stock --> Batches["Batches"]
|
||||
AdminPortal --> Reports["Reports"]
|
||||
Reports --> MonthlyReport["Monthly Compliance"]
|
||||
Reports --> MemberExport["Member Export"]
|
||||
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"]
|
||||
```
|
||||
|
||||
### URL Structure
|
||||
|
||||
| Path | Description | Role |
|
||||
|---|---|---|
|
||||
| `/login` | Login page | Public |
|
||||
| `/admin/dashboard` | Admin home | `ROLE_ADMIN` |
|
||||
| `/admin/members` | Member list | `ROLE_ADMIN` |
|
||||
| `/admin/members/{id}` | Member detail | `ROLE_ADMIN` |
|
||||
| `/admin/distributions` | Distribution log | `ROLE_ADMIN` |
|
||||
| `/admin/distributions/new` | New distribution form | `ROLE_ADMIN` |
|
||||
| `/admin/stock/strains` | Strain catalog | `ROLE_ADMIN` |
|
||||
| `/admin/stock/batches` | Batch management | `ROLE_ADMIN` |
|
||||
| `/admin/reports/monthly` | Compliance reports | `ROLE_ADMIN` |
|
||||
| `/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` |
|
||||
|
||||
---
|
||||
|
||||
## 5. Responsive Design Notes
|
||||
|
||||
### MVP (v1) — Tailwind Breakpoints
|
||||
|
||||
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 | 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 uses `flex-col` mobile-first layout with `md:flex-row` for wider viewports — no breakpoint-specific class sprawl.
|
||||
|
||||
### 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)
|
||||
- Push notifications for low quota warnings
|
||||
- Per-club subdomain routing (`clubname.cannamanage.de`)
|
||||
|
||||
---
|
||||
|
||||
## 6. Accessibility
|
||||
|
||||
CannaManage targets **WCAG 2.1 AA** compliance across both portals.
|
||||
|
||||
### Keyboard Navigation
|
||||
|
||||
| Element | Keyboard Behavior |
|
||||
|---|---|
|
||||
| Navigation sidebar | Tab navigates items; Enter activates |
|
||||
| Data tables | Tab to table; arrow keys for row navigation |
|
||||
| Dropdown menus | Enter/Space to open; arrow keys to navigate; Escape to close |
|
||||
| Modal dialogs | Focus trapped inside; Escape to close; first focusable element receives focus on open |
|
||||
| Confirmation dialogs | Tab between Confirm and Cancel; Enter on focused button |
|
||||
|
||||
### Screen Reader Support
|
||||
|
||||
- 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
|
||||
- TanStack Table exposes `role="grid"` and `aria-rowcount` via `getTableProps()`
|
||||
|
||||
### Color Independence
|
||||
|
||||
Status badges must never rely on color alone:
|
||||
|
||||
| Status | Color | Icon | Text label |
|
||||
|---|---|---|---|
|
||||
| AVAILABLE | Green | ✓ | "Available" |
|
||||
| LOW | Orange | ⚠ | "Low Stock" |
|
||||
| RECALLED | Red | ⛔ | "Recalled" |
|
||||
| EXHAUSTED | Gray | — | "Exhausted" |
|
||||
|
||||
Quota progress bar additionally shows numeric percentage text alongside color change.
|
||||
|
||||
### Contrast Ratios
|
||||
|
||||
| Foreground | Background | Ratio | AA pass |
|
||||
|---|---|---|---|
|
||||
| `#FFFFFF` | `#2D5016` | 9.1:1 | ✅ |
|
||||
| `#1A1A1A` | `#F5F5F5` | 16.0:1 | ✅ |
|
||||
| `#1A1A1A` | `#FFFFFF` | 19.0:1 | ✅ |
|
||||
| `#FFFFFF` | `#D32F2F` | 5.1:1 | ✅ |
|
||||
| `#FFFFFF` | `#FF6B35` | 3.1:1 | ⚠ verify at large text only |
|
||||
|
||||
---
|
||||
|
||||
*Next: [07-CODING-STANDARDS.md](07-CODING-STANDARDS.md)*
|
||||
Reference in New Issue
Block a user