feat(sprint7): Phase 1 — notifications enhancement + push infrastructure
Phase 1 (Notification Enhancement): - Extended NotificationType enum (ADMIN_MESSAGE, INFO_BOARD_POST, FORUM_REPLY, FORUM_MENTION) - Extended StaffPermission enum (SEND_NOTIFICATIONS, MANAGE_INFO_BOARD, MODERATE_FORUM) - Extended AuditEventType with Sprint 7 events - Flyway V11: notification_sends + notification_send_recipients tables - NotificationSend + NotificationSendRecipient entities - NotificationSendRepository + NotificationSendRecipientRepository - Extended NotificationService with sendBroadcast() and sendToSelected() - NotificationComposeController (POST /compose, GET /sends) - ComposeNotificationRequest DTO Phase 1B (Push Infrastructure): - Flyway V12: device_tokens + notification_preferences tables - DeviceToken entity + DevicePlatform enum - NotificationPreference entity + NotificationChannel enum - DeviceTokenRepository + NotificationPreferenceRepository - DeviceRegistrationService (register/unregister/list devices, max 10 per user) - NotificationPreferenceService (get/create defaults, update, IN_APP always on) - NotificationDispatchService (multi-channel fan-out: WebSocket, Web Push, FCM, Email) - WebPushSender (VAPID-based, simplified for MVP) - FcmPushSender (graceful degradation if not configured) - PushPayload DTO - DeviceRegistrationController (POST/GET/DELETE /devices, GET /vapid-key) - NotificationPreferenceController (GET/PUT /preferences) - ConsentType extended (NOTIFICATION_PUSH, NOTIFICATION_EMAIL) - TargetType enum (ALL, SELECTED) Frontend: - Updated sw.js with push event handler + notification click handler - push-subscription.ts (subscribeToPush, unsubscribe, permission helpers) - notification-compose.ts service (compose, sends, devices, preferences APIs) - i18n keys (de.json + en.json) for compose, preferences, push, devices Configuration: - application-docker.properties: VAPID + FCM push config properties - MemberRepository: added findAllActiveUserIds() for broadcast
This commit is contained in:
@@ -0,0 +1,805 @@
|
||||
# Sprint 7 Feature Analysis — Communication & Community
|
||||
|
||||
**Date:** 2026-06-13
|
||||
**Author:** Patrick Plate / Lumen (Architect)
|
||||
**Status:** Draft v1
|
||||
**Sprint Goal:** Transform CannaManage from a compliance tool into a community platform — the communication layer that keeps members engaged and clubs organized.
|
||||
|
||||
---
|
||||
|
||||
## Executive Summary
|
||||
|
||||
Sprint 7 introduces four communication features that turn CannaManage from "backend compliance software" into a living club platform. The recommended Sprint 7 scope is:
|
||||
|
||||
1. **Club Info Board** (Schwarzes Brett) — ship it. Core feature, medium effort, high engagement value.
|
||||
2. **Club-Internal Forum** — ship it (MVP). Threaded discussions within a club. High member engagement, competitive differentiator.
|
||||
3. **Club-to-Member Notifications** (admin-composed) — ship it. Builds on existing Sprint 6 infrastructure. Low effort, high value.
|
||||
4. **Cross-Club Community Forum** — defer to Sprint 8+. Legal complexity (KCanG), moderation overhead, and "nice to have" status make it wrong for this sprint.
|
||||
|
||||
**Strategic rationale:** No competitor (420cloud, Hanf-App, Cannanas) offers in-app community features. Clubs currently use Telegram/Signal/WhatsApp groups, which are unmoderated, unarchived, and disconnected from the platform. Owning the communication layer creates massive switching costs — once a club's discussions live in CannaManage, they can't leave without losing history.
|
||||
|
||||
---
|
||||
|
||||
## 1. Club Info Board (Schwarzes Brett)
|
||||
|
||||
### 1.1 Problem Statement
|
||||
|
||||
Club admins need a one-to-many broadcast channel: announcements, rule changes, event notices, operational updates ("We're closed next Thursday"), new strain arrivals. Today this happens in Telegram groups where messages get buried, new members miss old announcements, and there's no archive.
|
||||
|
||||
The Info Board is **not** a conversation — it's a pinboard. Staff posts, members read. Think: physical Schwarzes Brett in the club room, digitized.
|
||||
|
||||
### 1.2 How It Differs from Notifications
|
||||
|
||||
| Aspect | Notifications | Info Board |
|
||||
|--------|--------------|-----------|
|
||||
| Trigger | System-generated events (quota warning, batch recalled) | Human-authored by admin/staff |
|
||||
| Persistence | Transient — read and dismiss | Permanent — archived, searchable |
|
||||
| Visibility | Individual user inbox | All club members see all posts |
|
||||
| Rich content | Short text + link | Rich text, images, attachments |
|
||||
| Categories | NotificationType enum | Admin-defined categories |
|
||||
| Interaction | Mark as read | Pin, archive, comment (optional) |
|
||||
|
||||
### 1.3 User Stories
|
||||
|
||||
| # | As a... | I want to... | So that... | Priority |
|
||||
|---|---------|-------------|-----------|----------|
|
||||
| IB-01 | Club Admin | Post an announcement with title, body, and category | All members see it on their dashboard | P0 |
|
||||
| IB-02 | Club Admin | Pin an important post to the top | Critical info (rules, hours) stays visible | P0 |
|
||||
| IB-03 | Club Admin | Attach images or PDFs to a post | I can share event flyers or updated rules docs | P1 |
|
||||
| IB-04 | Club Admin | Archive old posts (not delete) | The board stays clean but history is preserved | P1 |
|
||||
| IB-05 | Club Admin | Assign categories (Events, Rules, General, Strain News) | Members can filter what interests them | P1 |
|
||||
| IB-06 | Staff (with permission) | Post announcements | The admin doesn't have to do everything | P0 |
|
||||
| IB-07 | Member | See announcements on my portal dashboard | I don't miss important club news | P0 |
|
||||
| IB-08 | Member | Filter posts by category | I only see what's relevant to me | P2 |
|
||||
| IB-09 | Member | Get notified when a new post appears | I'm alerted even if I don't check the board | P1 |
|
||||
|
||||
### 1.4 Data Model
|
||||
|
||||
```sql
|
||||
-- V11: Info Board
|
||||
CREATE TABLE info_board_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
color VARCHAR(7), -- hex color for UI badge
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE info_board_posts (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
body TEXT NOT NULL, -- markdown or sanitized HTML
|
||||
category_id UUID REFERENCES info_board_categories(id),
|
||||
author_id UUID NOT NULL, -- staff or admin user
|
||||
pinned BOOLEAN NOT NULL DEFAULT false,
|
||||
archived BOOLEAN NOT NULL DEFAULT false,
|
||||
published_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE info_board_attachments (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
post_id UUID NOT NULL REFERENCES info_board_posts(id) ON DELETE CASCADE,
|
||||
filename VARCHAR(255) NOT NULL,
|
||||
content_type VARCHAR(100) NOT NULL,
|
||||
size_bytes BIGINT NOT NULL,
|
||||
storage_path VARCHAR(500) NOT NULL,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX idx_info_board_posts_tenant ON info_board_posts(tenant_id, archived, pinned DESC, published_at DESC);
|
||||
CREATE INDEX idx_info_board_posts_category ON info_board_posts(category_id);
|
||||
```
|
||||
|
||||
**Key design decisions:**
|
||||
- Markdown body (rendered on frontend) — simple, secure, no XSS risk from raw HTML
|
||||
- `pinned` as boolean — pinned posts sort first, then by `published_at DESC`
|
||||
- Soft-archive (not delete) — compliance friendliness, audit trail integrity
|
||||
- Attachments stored on filesystem (or S3-compatible) — not in DB
|
||||
|
||||
### 1.5 UI Mockup Description
|
||||
|
||||
**Admin/Staff view (Dashboard → Info Board):**
|
||||
- Top: "New Post" button (opens rich text editor)
|
||||
- Category filter tabs (All | Events | Rules | General | Strain News)
|
||||
- Post list: card layout with title, excerpt, author, date, pinned badge
|
||||
- Each card: actions dropdown (Edit, Pin/Unpin, Archive)
|
||||
- Attachment upload: drag-and-drop zone in the editor
|
||||
|
||||
**Portal/Member view (Portal Dashboard → Announcements section):**
|
||||
- Latest 3-5 posts shown as cards on the portal dashboard
|
||||
- "View All" link → dedicated announcements page
|
||||
- Category color badges
|
||||
- Pinned posts have a 📌 indicator and always show first
|
||||
- Unread indicator (new posts since last visit)
|
||||
|
||||
### 1.6 Integration Points
|
||||
|
||||
| Integration | How |
|
||||
|------------|-----|
|
||||
| Notifications (existing) | New post → auto-generate `INFO_BOARD_POST` notification for all club members |
|
||||
| WebSocket (existing) | Push new-post event in real-time to connected members |
|
||||
| Audit Log (existing) | Log post creation, edits, archives as `INFO_BOARD_CREATED` / `INFO_BOARD_EDITED` / `INFO_BOARD_ARCHIVED` |
|
||||
| StaffPermission | New permission: `MANAGE_INFO_BOARD` — controls who can post/edit/archive |
|
||||
|
||||
### 1.7 Plan Tier Mapping
|
||||
|
||||
| Tier | Info Board Access |
|
||||
|------|------------------|
|
||||
| Starter | ✅ Basic (3 categories max, no attachments) |
|
||||
| Pro | ✅ Full (unlimited categories, attachments up to 10 MB) |
|
||||
| Enterprise | ✅ Full + scheduled posts, auto-archive after N days |
|
||||
|
||||
---
|
||||
|
||||
## 2. Club-Internal Forum (Vereinsinterne Diskussion)
|
||||
|
||||
### 2.1 Problem Statement
|
||||
|
||||
Members want to talk to each other — not just passively receive announcements. Today they use Telegram/WhatsApp groups that are:
|
||||
- Unmoderated (spam, off-topic, conflicts)
|
||||
- Ephemeral (messages get buried, no searchable archive)
|
||||
- Disconnected from the club platform (no link to member profiles, no awareness of club context)
|
||||
- A privacy leak (phone numbers exposed to all members)
|
||||
|
||||
A club-internal forum gives members a place to discuss within the platform — moderated, archived, searchable, and integrated with member identity.
|
||||
|
||||
### 2.2 How It Differs from Info Board
|
||||
|
||||
| Aspect | Info Board | Forum |
|
||||
|--------|-----------|-------|
|
||||
| Direction | One-to-many (staff → members) | Many-to-many (members ↔ members) |
|
||||
| Who posts | Admin/Staff only | All members |
|
||||
| Structure | Flat list of posts | Threaded: Topics → Replies |
|
||||
| Moderation | N/A (staff controls everything) | Staff can moderate, lock, delete |
|
||||
| Purpose | Official communication | Community discussion |
|
||||
|
||||
### 2.3 User Stories
|
||||
|
||||
| # | As a... | I want to... | So that... | Priority |
|
||||
|---|---------|-------------|-----------|----------|
|
||||
| FO-01 | Member | Create a new discussion topic | I can ask questions or start conversations | P0 |
|
||||
| FO-02 | Member | Reply to an existing topic | I can participate in discussions | P0 |
|
||||
| FO-03 | Member | Browse topics by category | I find relevant discussions | P0 |
|
||||
| FO-04 | Member | See who posted (display name + avatar) | I know who I'm talking to | P0 |
|
||||
| FO-05 | Member | Edit my own posts (within 30 min) | I can fix typos | P1 |
|
||||
| FO-06 | Member | Report inappropriate posts | I can flag content for moderators | P1 |
|
||||
| FO-07 | Staff/Admin | Create and manage forum categories | Discussions are organized | P0 |
|
||||
| FO-08 | Staff/Admin | Delete posts / lock topics | I can moderate inappropriate content | P0 |
|
||||
| FO-09 | Staff/Admin | Pin important topics | Key discussions stay visible | P1 |
|
||||
| FO-10 | Staff/Admin | See reported posts in a moderation queue | I can act on reports efficiently | P1 |
|
||||
| FO-11 | Member | Get notified when someone replies to my topic | I don't miss responses | P1 |
|
||||
| FO-12 | Member | Upload images in posts | I can share photos (grow results, events) | P2 |
|
||||
| FO-13 | Member | React to posts (thumbs up, leaf emoji) | I can show appreciation without cluttering | P2 |
|
||||
|
||||
### 2.4 Data Model
|
||||
|
||||
```sql
|
||||
-- V12: Club-Internal Forum
|
||||
CREATE TABLE forum_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description VARCHAR(500),
|
||||
icon VARCHAR(50), -- emoji or icon name
|
||||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE forum_topics (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
body TEXT NOT NULL, -- first post content (markdown)
|
||||
category_id UUID NOT NULL REFERENCES forum_categories(id),
|
||||
author_id UUID NOT NULL, -- member or staff user
|
||||
pinned BOOLEAN NOT NULL DEFAULT false,
|
||||
locked BOOLEAN NOT NULL DEFAULT false,
|
||||
reply_count INTEGER NOT NULL DEFAULT 0,
|
||||
last_reply_at TIMESTAMP WITH TIME ZONE,
|
||||
last_reply_by UUID,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE forum_replies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
topic_id UUID NOT NULL REFERENCES forum_topics(id) ON DELETE CASCADE,
|
||||
body TEXT NOT NULL, -- markdown
|
||||
author_id UUID NOT NULL,
|
||||
edited_at TIMESTAMP WITH TIME ZONE,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE forum_reports (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
reporter_id UUID NOT NULL,
|
||||
target_type VARCHAR(10) NOT NULL, -- 'TOPIC' or 'REPLY'
|
||||
target_id UUID NOT NULL,
|
||||
reason VARCHAR(500) NOT NULL,
|
||||
resolved BOOLEAN NOT NULL DEFAULT false,
|
||||
resolved_by UUID,
|
||||
resolved_at TIMESTAMP WITH TIME ZONE,
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE TABLE forum_reactions (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
user_id UUID NOT NULL,
|
||||
target_type VARCHAR(10) NOT NULL, -- 'TOPIC' or 'REPLY'
|
||||
target_id UUID NOT NULL,
|
||||
reaction VARCHAR(20) NOT NULL, -- 'thumbsup', 'leaf', 'fire', 'heart'
|
||||
tenant_id UUID NOT NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
UNIQUE(user_id, target_type, target_id, reaction)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_forum_topics_tenant ON forum_topics(tenant_id, category_id, pinned DESC, last_reply_at DESC);
|
||||
CREATE INDEX idx_forum_replies_topic ON forum_replies(topic_id, created_at);
|
||||
CREATE INDEX idx_forum_reports_unresolved ON forum_reports(tenant_id, resolved, created_at);
|
||||
```
|
||||
|
||||
**Key design decisions:**
|
||||
- Flat replies (not nested/threaded) — simpler UX, easier to follow, works on mobile
|
||||
- `reply_count` + `last_reply_at` denormalized on topics — avoids expensive JOINs for listing
|
||||
- Edit window: 30 minutes (set in application logic, not DB)
|
||||
- Reactions: limited set (4-5 emojis) — not free-form to keep it simple
|
||||
- Reports go to a moderation queue — staff sees flagged content
|
||||
|
||||
### 2.5 Moderation Features
|
||||
|
||||
| Feature | How It Works |
|
||||
|---------|-------------|
|
||||
| Delete post/reply | Staff removes content, replaced with "[Beitrag entfernt]" placeholder |
|
||||
| Lock topic | No new replies allowed. Existing content preserved. |
|
||||
| Pin topic | Pinned topics always appear at the top of category view |
|
||||
| Report queue | Members flag → staff sees queue → resolve (delete, warn, or dismiss) |
|
||||
| Auto-lock inactive | Topics with no replies for 90 days auto-lock (configurable) |
|
||||
| Word filter | Optional blocklist (admin-configured) — posts with blocked words held for review |
|
||||
|
||||
**New StaffPermission:** `MODERATE_FORUM` — controls access to moderation queue and lock/delete actions.
|
||||
|
||||
### 2.6 UI Mockup Description
|
||||
|
||||
**Forum main page (Dashboard → Forum):**
|
||||
- Category cards with name, description, topic count, last activity
|
||||
- "New Topic" button (per category or global)
|
||||
- Search bar (full-text search across topics and replies)
|
||||
|
||||
**Category view:**
|
||||
- Topic list: title, author, reply count, last reply date
|
||||
- Pinned topics at top with badge
|
||||
- Locked topics with lock icon
|
||||
- Pagination (20 topics per page)
|
||||
|
||||
**Topic view:**
|
||||
- Original post at top
|
||||
- Replies below in chronological order
|
||||
- Reply editor at bottom (markdown with preview)
|
||||
- Reaction buttons on each post
|
||||
- "Report" link on each post (subtle, doesn't clutter)
|
||||
- Lock/Delete/Pin controls for staff (contextual)
|
||||
|
||||
**Portal/Member view:**
|
||||
- Same forum, just within the portal layout
|
||||
- Members see their own posts highlighted
|
||||
- "My Topics" and "Subscribed" quick filters
|
||||
|
||||
### 2.7 Plan Tier Mapping
|
||||
|
||||
| Tier | Forum Access |
|
||||
|------|-------------|
|
||||
| Starter | ❌ Not included (upgrade incentive) |
|
||||
| Pro | ✅ Full forum (5 categories max) |
|
||||
| Enterprise | ✅ Full forum (unlimited categories, custom word filter, advanced moderation) |
|
||||
|
||||
---
|
||||
|
||||
## 3. Cross-Club Community Forum
|
||||
|
||||
### 3.1 Problem Statement
|
||||
|
||||
Members from different clubs may want to exchange knowledge — growing tips, legal questions, strain reviews, event coordination. Currently there's no platform-level community; people use Reddit, Telegram, or CannaConnection forums.
|
||||
|
||||
A cross-club forum would make CannaManage a "social network for CSC members" — a powerful retention mechanism. But it also introduces significant complexity: moderation at scale, privacy concerns, and legal implications under KCanG.
|
||||
|
||||
### 3.2 User Stories
|
||||
|
||||
| # | As a... | I want to... | So that... | Priority |
|
||||
|---|---------|-------------|-----------|----------|
|
||||
| CC-01 | Member | Browse community topics from all clubs | I can learn from other clubs' experiences | P1 |
|
||||
| CC-02 | Member | Post in the community forum | I can share knowledge with the wider community | P1 |
|
||||
| CC-03 | Member | Choose to appear as "Member of [ClubName]" or anonymous | I control my privacy | P0 |
|
||||
| CC-04 | Platform Admin | Moderate cross-club content | The community stays safe and legal | P0 |
|
||||
| CC-05 | Platform Admin | Define community-wide categories | Content is organized | P0 |
|
||||
| CC-06 | Club Admin | Opt their club in/out of community features | They control member exposure | P1 |
|
||||
|
||||
### 3.3 Data Model (Multi-Tenant Considerations)
|
||||
|
||||
The cross-club forum breaks the fundamental `tenant_id` isolation pattern. Every other entity in CannaManage is scoped to a single club. A cross-club entity needs either:
|
||||
|
||||
**Option A: Separate schema (no tenant_id)**
|
||||
```sql
|
||||
-- community_topics, community_replies — NOT extending AbstractTenantEntity
|
||||
-- Use a separate 'platform_user_id' that maps to the user across clubs
|
||||
```
|
||||
|
||||
**Option B: Platform-level tenant (tenant_id = NULL or platform UUID)**
|
||||
```sql
|
||||
-- A special "platform" tenant that all users can access
|
||||
-- More complex query logic: WHERE tenant_id = :current OR tenant_id = :platform
|
||||
```
|
||||
|
||||
**Recommendation:** Option A — separate schema. The cross-club forum is architecturally a different bounded context. Mixing it with tenant-scoped entities creates query complexity and potential data leakage bugs.
|
||||
|
||||
### 3.4 Legal & Privacy Analysis (KCanG Implications)
|
||||
|
||||
| Concern | Analysis | Risk Level |
|
||||
|---------|----------|-----------|
|
||||
| **KCanG §4 — Advertising prohibition** | CSCs may not advertise cannabis or their club publicly. A cross-club forum where clubs share their strains could be interpreted as advertising. | 🟡 Medium |
|
||||
| **KCanG §18 — Data handling** | Member data must be handled with care. Cross-club exposure of member names (even display names) to non-club-members may violate the principle of data minimization. | 🟡 Medium |
|
||||
| **DSGVO Art. 5(1)(c) — Data minimization** | Exposing member identity across clubs goes beyond what's necessary for club management. Must offer genuine anonymity option. | 🔴 High |
|
||||
| **KCanG §5 — Membership restriction** | Members can only belong to one CSC. If the forum reveals multi-club participation, it could expose legal violations. | 🟡 Medium |
|
||||
| **Platform liability** | If the platform hosts inter-club communication, it becomes a publisher/moderator of cannabis-related content. Illegal content (sales, purchases) is a risk. | 🔴 High |
|
||||
| **Age verification** | KCanG requires age verification for membership. A community forum accessible to "all members" must ensure all participants are verified. | 🟢 Low (all members already verified) |
|
||||
|
||||
**Legal recommendation:** If built, the cross-club forum MUST:
|
||||
1. Default to anonymous posting (no club name, no real name)
|
||||
2. Prohibit any form of transaction/trade discussion
|
||||
3. Include clear terms of use that forbid advertising
|
||||
4. Have active platform-level moderation
|
||||
5. Allow clubs to opt-out entirely
|
||||
6. Allow individual members to opt-out
|
||||
|
||||
### 3.5 Moderation at Platform Level
|
||||
|
||||
| Challenge | Solution |
|
||||
|-----------|----------|
|
||||
| Scale | Platform admin team or community moderators (elevated non-admin users) |
|
||||
| Illegal content | Automated keyword filtering + user reports + manual review |
|
||||
| Cross-club disputes | Platform terms of service, not individual club rules |
|
||||
| Reporting to authorities | If illegal activity is detected, platform has Mitwirkungspflicht |
|
||||
| Content liability | Clear Terms of Service, notice-and-takedown procedure |
|
||||
|
||||
### 3.6 Recommendation: Defer to Sprint 8+
|
||||
|
||||
**Reasons to defer:**
|
||||
1. **Legal complexity** — needs actual legal review (not just developer analysis)
|
||||
2. **Architectural break** — violates the clean tenant isolation model; needs careful design
|
||||
3. **Moderation overhead** — requires platform-level moderation tooling that doesn't exist yet
|
||||
4. **Low priority** — club-internal communication is far more valuable than inter-club
|
||||
5. **Competitive non-urgency** — no competitor has this either; no market pressure
|
||||
6. **MVP path** — clubs can still use Telegram for inter-club chat; CannaManage solves the intra-club problem first
|
||||
|
||||
**When to revisit:** After 50+ clubs are on the platform and actively request it. Validate demand before building.
|
||||
|
||||
### 3.7 Plan Tier Mapping (if eventually built)
|
||||
|
||||
| Tier | Community Forum |
|
||||
|------|----------------|
|
||||
| Starter | ❌ Not included |
|
||||
| Pro | ✅ Read-only access |
|
||||
| Enterprise | ✅ Full access (post + moderate) |
|
||||
|
||||
---
|
||||
|
||||
## 4. Club Notifications to Members (Benachrichtigungen)
|
||||
|
||||
### 4.1 Existing Infrastructure (What We Already Have)
|
||||
|
||||
Sprint 6 delivered a solid notification foundation:
|
||||
|
||||
| Component | Status | Details |
|
||||
|-----------|--------|---------|
|
||||
| `notifications` table | ✅ Built | UUID PK, user_id, type, title, message, link, read flag, tenant_id |
|
||||
| `Notification` entity | ✅ Built | JPA entity extending `AbstractTenantEntity` |
|
||||
| `NotificationType` enum | ✅ Built | 4 system types: `QUOTA_WARNING`, `BATCH_RECALLED`, `DISTRIBUTION_RECORDED`, `SUBSCRIPTION_EXPIRING` |
|
||||
| REST API (`/notifications`) | ✅ Built | GET list, PUT mark-read, PUT mark-all-read |
|
||||
| Frontend service | ✅ Built | `getNotifications()`, `markNotificationAsRead()`, `markAllNotificationsAsRead()` |
|
||||
| WebSocket (SockJS/STOMP) | ✅ Built | Real-time push to connected clients (Nginx configured for `/ws/` upgrade) |
|
||||
| PWA + Service Worker | ✅ Built | Manifest, offline page, install prompt (Sprint 6 Phase 6) |
|
||||
| Bell icon in nav | ✅ Built | Unread count badge in dashboard header |
|
||||
|
||||
**What works today:** System-generated notifications (triggered by backend events) get stored in the DB, pushed via WebSocket to the frontend, and shown in the bell dropdown. Members can mark them as read.
|
||||
|
||||
### 4.2 Gap Analysis (What's Missing)
|
||||
|
||||
| Gap | Description | Effort |
|
||||
|-----|------------|--------|
|
||||
| **Admin compose UI** | No interface for admins to write and send custom notifications | Medium |
|
||||
| **Targeting** | Current notifications are individual (`user_id`). No way to send to "all members" or "members in group X" | Medium |
|
||||
| **Custom notification types** | Only 4 system types. Need `ADMIN_MESSAGE`, `INFO_BOARD_POST`, `FORUM_REPLY` | Low |
|
||||
| **Email delivery channel** | Notifications are in-app only. No email fallback for offline users | Medium |
|
||||
| **Push notifications (Web Push API)** | PWA exists but Web Push subscription/delivery not implemented | Medium |
|
||||
| **Scheduling** | Can't schedule a notification for future delivery | Low |
|
||||
| **Templates** | No template system for recurring notifications | Low |
|
||||
| **Read receipts** | Admin can't see who read the notification | Low |
|
||||
| **Member groups/segments** | No concept of member groups for targeted sending | Medium |
|
||||
|
||||
### 4.3 User Stories
|
||||
|
||||
| # | As a... | I want to... | So that... | Priority |
|
||||
|---|---------|-------------|-----------|----------|
|
||||
| NT-01 | Club Admin | Compose a custom notification with title + message | I can reach members directly | P0 |
|
||||
| NT-02 | Club Admin | Send to all members at once | I don't have to select each one individually | P0 |
|
||||
| NT-03 | Club Admin | Send to specific members (multi-select) | I can target a subset | P1 |
|
||||
| NT-04 | Club Admin | Preview before sending | I catch mistakes before broadcasting | P0 |
|
||||
| NT-05 | Club Admin | See delivery stats (sent/read counts) | I know if members are getting the message | P1 |
|
||||
| NT-06 | Member | Receive notifications in-app (real-time) | I see messages immediately when online | P0 |
|
||||
| NT-07 | Member | Receive email for important notifications (opt-in) | I don't miss things when offline | P1 |
|
||||
| NT-08 | Member | Control which notification types I receive | I'm not overwhelmed | P2 |
|
||||
| NT-09 | Club Admin | Schedule a notification for later | I can prepare communications in advance | P2 |
|
||||
|
||||
### 4.4 Admin UI for Composing Notifications
|
||||
|
||||
**Page: Dashboard → Notifications → Compose**
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 📣 Neue Benachrichtigung │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ │
|
||||
│ Empfänger: [● Alle Mitglieder ▼] │
|
||||
│ ○ Ausgewählte Mitglieder │
|
||||
│ [Search + multi-select chips] │
|
||||
│ │
|
||||
│ Titel: [____________________________] │
|
||||
│ │
|
||||
│ Nachricht: │
|
||||
│ ┌─────────────────────────────────────────┐ │
|
||||
│ │ (Markdown editor with preview toggle) │ │
|
||||
│ │ │ │
|
||||
│ └─────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Link (optional): [________________________] │
|
||||
│ │
|
||||
│ □ Auch per E-Mail senden │
|
||||
│ │
|
||||
│ [Vorschau] [Senden] │
|
||||
│ │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
**Notification History page:**
|
||||
- Table of sent notifications with: title, recipients (count), sent date, read rate
|
||||
- Click to see per-recipient read status
|
||||
- Resend option for unread recipients
|
||||
|
||||
### 4.5 Delivery Channels
|
||||
|
||||
| Channel | Implementation | Priority |
|
||||
|---------|---------------|----------|
|
||||
| **In-app (WebSocket)** | Already works. Extend to handle `ADMIN_MESSAGE` type. | P0 (exists) |
|
||||
| **In-app (polling fallback)** | Already works via REST GET `/notifications`. | P0 (exists) |
|
||||
| **Email** | Use existing `EmailService`. New template: `notification-email.txt`. Opt-in per member. | P1 |
|
||||
| **Web Push** | Register subscription via Push API. Backend stores endpoint + keys. Send via `web-push` library. | P2 (Sprint 8) |
|
||||
|
||||
### 4.6 Targeting (All, Groups, Individuals)
|
||||
|
||||
**Phase 1 (Sprint 7):**
|
||||
- Send to ALL members (broadcast)
|
||||
- Send to selected individuals (multi-select picker)
|
||||
|
||||
**Phase 2 (Sprint 8+):**
|
||||
- Member groups/segments (e.g., "Board Members", "Active Growers", "New Members < 30 days")
|
||||
- Tag-based targeting
|
||||
- Dynamic segments (members who haven't visited in 30 days)
|
||||
|
||||
### 4.7 Data Model Changes
|
||||
|
||||
```sql
|
||||
-- Extend NotificationType enum (code change only, no migration needed)
|
||||
-- Add: ADMIN_MESSAGE, INFO_BOARD_POST, FORUM_REPLY, FORUM_MENTION
|
||||
|
||||
-- V13: Notification send log (for admin-composed notifications)
|
||||
CREATE TABLE notification_sends (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
title VARCHAR(255) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
link VARCHAR(500),
|
||||
author_id UUID NOT NULL,
|
||||
target_type VARCHAR(20) NOT NULL, -- 'ALL', 'SELECTED'
|
||||
target_count INTEGER NOT NULL,
|
||||
read_count INTEGER NOT NULL DEFAULT 0,
|
||||
sent_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
|
||||
tenant_id UUID NOT NULL
|
||||
);
|
||||
|
||||
CREATE TABLE notification_send_recipients (
|
||||
send_id UUID NOT NULL REFERENCES notification_sends(id) ON DELETE CASCADE,
|
||||
user_id UUID NOT NULL,
|
||||
notification_id UUID REFERENCES notifications(id), -- links to the actual notification created
|
||||
PRIMARY KEY (send_id, user_id)
|
||||
);
|
||||
|
||||
CREATE INDEX idx_notification_sends_tenant ON notification_sends(tenant_id, sent_at DESC);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Prioritization & Sprint Plan
|
||||
|
||||
### 5.1 Feature Priority Matrix
|
||||
|
||||
| Feature | User Value | Competitive Edge | Build Effort | Risk | Sprint 7? |
|
||||
|---------|-----------|-----------------|-------------|------|-----------|
|
||||
| Club Notifications (admin compose) | 🟢 High | 🟢 Medium | 🟢 Low (builds on existing) | 🟢 Low | ✅ Yes |
|
||||
| Club Info Board | 🟢 High | 🟢 High (no competitor has it) | 🟡 Medium | 🟢 Low | ✅ Yes |
|
||||
| Club-Internal Forum | 🟢 High | 🟢 High (no competitor has it) | 🟡 Medium-High | 🟡 Medium (moderation) | ✅ Yes (MVP) |
|
||||
| Cross-Club Forum | 🟡 Medium | 🟡 Medium | 🔴 High | 🔴 High (legal, arch) | ❌ Defer |
|
||||
|
||||
### 5.2 Recommended Sprint 7 Scope
|
||||
|
||||
```
|
||||
Phase 1: Admin Notifications (builds on existing infra)
|
||||
Phase 2: Info Board (Schwarzes Brett)
|
||||
Phase 3: Forum MVP (categories, topics, replies, basic moderation)
|
||||
Phase 4: Integration (notifications ↔ info board ↔ forum)
|
||||
Phase 5: Polish & testing
|
||||
```
|
||||
|
||||
#### Phase 1 — Admin Notifications
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| 1.1 | Extend `NotificationType` enum: `ADMIN_MESSAGE`, `INFO_BOARD_POST`, `FORUM_REPLY` |
|
||||
| 1.2 | Add `notification_sends` table (Flyway V13) |
|
||||
| 1.3 | Backend: `NotificationComposeService` — create notification for multiple recipients |
|
||||
| 1.4 | Backend: `POST /api/v1/notifications/compose` endpoint |
|
||||
| 1.5 | Frontend: Compose notification page (admin area) |
|
||||
| 1.6 | Frontend: Notification history page with read stats |
|
||||
| 1.7 | Email delivery: notification email template + opt-in flag on member |
|
||||
| 1.8 | New `StaffPermission`: `SEND_NOTIFICATIONS` |
|
||||
|
||||
#### Phase 2 — Info Board
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| 2.1 | `info_board_categories` + `info_board_posts` + `info_board_attachments` tables (Flyway V14) |
|
||||
| 2.2 | Backend: `InfoBoardService` — CRUD for categories and posts |
|
||||
| 2.3 | Backend: REST endpoints `POST/GET/PUT /api/v1/info-board/posts`, `GET /api/v1/info-board/categories` |
|
||||
| 2.4 | Backend: File upload endpoint for attachments |
|
||||
| 2.5 | Frontend (admin): Info Board management page (create, edit, pin, archive) |
|
||||
| 2.6 | Frontend (portal): Announcements section on member dashboard + dedicated page |
|
||||
| 2.7 | Integration: New post → auto-notify all members (uses Phase 1 infra) |
|
||||
| 2.8 | New `StaffPermission`: `MANAGE_INFO_BOARD` |
|
||||
|
||||
#### Phase 3 — Forum MVP
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| 3.1 | Forum tables (Flyway V15): categories, topics, replies, reports, reactions |
|
||||
| 3.2 | Backend: `ForumService` — topics CRUD, replies CRUD, moderation actions |
|
||||
| 3.3 | Backend: REST endpoints for forum (topics, replies, categories, reports) |
|
||||
| 3.4 | Frontend: Forum page (category list → topic list → topic detail with replies) |
|
||||
| 3.5 | Frontend: New topic / reply editors (markdown) |
|
||||
| 3.6 | Frontend: Moderation panel (report queue, lock/delete actions) |
|
||||
| 3.7 | Integration: Reply notification → auto-notify topic author |
|
||||
| 3.8 | New `StaffPermission`: `MODERATE_FORUM` |
|
||||
|
||||
#### Phase 4 — Integration & Polish
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| 4.1 | WebSocket events for new info board posts and forum replies |
|
||||
| 4.2 | Audit log events for info board and forum actions |
|
||||
| 4.3 | Portal navigation update (add Forum, Announcements) |
|
||||
| 4.4 | Dashboard widget: "Latest Announcements" + "Recent Forum Activity" |
|
||||
| 4.5 | Email notifications for forum replies (opt-in) |
|
||||
|
||||
#### Phase 5 — Testing
|
||||
|
||||
| Step | Description |
|
||||
|------|-------------|
|
||||
| 5.1 | Unit tests for all new services |
|
||||
| 5.2 | Integration tests for forum + info board + notifications |
|
||||
| 5.3 | E2E tests (Playwright): post announcement, create topic, reply |
|
||||
| 5.4 | Tenant isolation verification (no cross-club data leaks) |
|
||||
| 5.5 | Permission checks (unauthorized staff can't post/moderate) |
|
||||
|
||||
### 5.3 Deferred to Sprint 8+
|
||||
|
||||
| Feature | Reason | When |
|
||||
|---------|--------|------|
|
||||
| Cross-Club Community Forum | Legal review needed, architecture break | Sprint 9+ (if validated) |
|
||||
| Web Push Notifications | PWA push subscription, VAPID keys | Sprint 8 |
|
||||
| Member groups / segments | Low urgency, "all members" is sufficient for MVP | Sprint 8 |
|
||||
| Scheduled notifications | Nice-to-have, not blocking | Sprint 8 |
|
||||
| Forum image uploads | Moderation complexity, storage cost | Sprint 8 |
|
||||
| Forum search (full-text) | PostgreSQL FTS setup, medium effort | Sprint 8 |
|
||||
| Notification preferences (per-type opt-in/out) | UX complexity | Sprint 8 |
|
||||
| Rich text editor (WYSIWYG vs markdown) | Markdown is sufficient for MVP | Sprint 9 |
|
||||
|
||||
### 5.4 Dependencies Between Features
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Phase 1: Admin Notifications] --> B[Phase 2: Info Board]
|
||||
A --> C[Phase 3: Forum MVP]
|
||||
B --> D[Phase 4: Integration]
|
||||
C --> D
|
||||
D --> E[Phase 5: Testing]
|
||||
|
||||
subgraph Existing Infrastructure
|
||||
N[Notification entity + table]
|
||||
W[WebSocket STOMP]
|
||||
P[StaffPermission system]
|
||||
AU[AuditService]
|
||||
end
|
||||
|
||||
N --> A
|
||||
W --> A
|
||||
P --> A
|
||||
P --> B
|
||||
P --> C
|
||||
AU --> D
|
||||
```
|
||||
|
||||
**Critical path:** Phase 1 must come first because Phases 2 and 3 depend on it for their notification integration.
|
||||
|
||||
---
|
||||
|
||||
## 6. Technical Architecture
|
||||
|
||||
### 6.1 New Entities & Migrations
|
||||
|
||||
| Migration | Tables | Entity Classes |
|
||||
|-----------|--------|---------------|
|
||||
| V11 | `info_board_categories`, `info_board_posts`, `info_board_attachments` | `InfoBoardCategory`, `InfoBoardPost`, `InfoBoardAttachment` |
|
||||
| V12 | `forum_categories`, `forum_topics`, `forum_replies`, `forum_reports`, `forum_reactions` | `ForumCategory`, `ForumTopic`, `ForumReply`, `ForumReport`, `ForumReaction` |
|
||||
| V13 | `notification_sends`, `notification_send_recipients` | `NotificationSend`, `NotificationSendRecipient` |
|
||||
|
||||
**All new entities extend `AbstractTenantEntity`** — tenant isolation by default.
|
||||
|
||||
### 6.2 API Endpoints
|
||||
|
||||
```
|
||||
# Admin Notifications
|
||||
POST /api/v1/notifications/compose — Send notification to targets
|
||||
GET /api/v1/notifications/sends — List sent notifications (admin)
|
||||
GET /api/v1/notifications/sends/{id} — Get send details + per-recipient status
|
||||
|
||||
# Info Board
|
||||
GET /api/v1/info-board/categories — List categories
|
||||
POST /api/v1/info-board/categories — Create category (admin)
|
||||
PUT /api/v1/info-board/categories/{id} — Update category (admin)
|
||||
GET /api/v1/info-board/posts — List posts (paginated, filterable)
|
||||
POST /api/v1/info-board/posts — Create post (staff with permission)
|
||||
PUT /api/v1/info-board/posts/{id} — Update post
|
||||
PUT /api/v1/info-board/posts/{id}/pin — Toggle pin
|
||||
PUT /api/v1/info-board/posts/{id}/archive — Archive post
|
||||
POST /api/v1/info-board/posts/{id}/attachments — Upload attachment
|
||||
GET /api/v1/info-board/posts/{id}/attachments/{aid} — Download attachment
|
||||
|
||||
# Forum
|
||||
GET /api/v1/forum/categories — List forum categories
|
||||
POST /api/v1/forum/categories — Create category (admin)
|
||||
GET /api/v1/forum/topics — List topics (paginated, by category)
|
||||
POST /api/v1/forum/topics — Create topic (any member)
|
||||
GET /api/v1/forum/topics/{id} — Get topic with replies (paginated)
|
||||
PUT /api/v1/forum/topics/{id} — Edit topic (author, within 30 min)
|
||||
PUT /api/v1/forum/topics/{id}/pin — Pin topic (moderator)
|
||||
PUT /api/v1/forum/topics/{id}/lock — Lock topic (moderator)
|
||||
DELETE /api/v1/forum/topics/{id} — Delete topic (moderator)
|
||||
POST /api/v1/forum/topics/{id}/replies — Post reply (any member)
|
||||
PUT /api/v1/forum/replies/{id} — Edit reply (author, within 30 min)
|
||||
DELETE /api/v1/forum/replies/{id} — Delete reply (moderator)
|
||||
POST /api/v1/forum/reports — Report a post/reply
|
||||
GET /api/v1/forum/reports — List reports (moderator)
|
||||
PUT /api/v1/forum/reports/{id}/resolve — Resolve report (moderator)
|
||||
POST /api/v1/forum/reactions — Add reaction
|
||||
DELETE /api/v1/forum/reactions/{id} — Remove reaction
|
||||
```
|
||||
|
||||
### 6.3 Frontend Pages/Components
|
||||
|
||||
**Admin Dashboard additions:**
|
||||
- `/settings/info-board` — Info Board management (categories + post editor)
|
||||
- `/settings/notifications/compose` — Compose notification
|
||||
- `/settings/notifications/history` — Sent notifications log
|
||||
- `/forum` — Forum with admin moderation controls
|
||||
|
||||
**Portal additions:**
|
||||
- `/portal/announcements` — All info board posts
|
||||
- `/portal/forum` — Forum access for members
|
||||
- Portal dashboard: "Announcements" widget + "Forum Activity" widget
|
||||
|
||||
**Shared components:**
|
||||
- `MarkdownEditor` — textarea with preview toggle (reusable for info board + forum)
|
||||
- `CategoryBadge` — colored badge component
|
||||
- `ReactionBar` — reaction emoji buttons with counts
|
||||
- `ModerationActions` — dropdown with delete/lock/pin (shown to staff)
|
||||
|
||||
### 6.4 WebSocket Integration
|
||||
|
||||
Extend the existing STOMP broker with new message types:
|
||||
|
||||
```
|
||||
/topic/club/{tenantId}/notifications — existing (extend with new types)
|
||||
/topic/club/{tenantId}/info-board — new post published
|
||||
/topic/club/{tenantId}/forum — new topic or reply in subscribed topic
|
||||
```
|
||||
|
||||
**Frontend subscription (React hook):**
|
||||
```typescript
|
||||
// useForumSubscription(topicId) → real-time new replies
|
||||
// useInfoBoardSubscription() → real-time new posts
|
||||
// useNotificationSubscription() → existing, extended with ADMIN_MESSAGE
|
||||
```
|
||||
|
||||
### 6.5 Search & Performance Considerations
|
||||
|
||||
| Concern | Solution |
|
||||
|---------|----------|
|
||||
| Forum topic listing performance | Denormalized `reply_count` + `last_reply_at` on topic table. Paginate at 20 per page. |
|
||||
| Info board post search | PostgreSQL `ILIKE` on title + body for MVP. Full-text search (tsvector) in Sprint 8 if needed. |
|
||||
| Notification fan-out (send to 500 members) | Batch INSERT using `JdbcTemplate.batchUpdate()`. Don't create 500 individual transactions. |
|
||||
| WebSocket scaling | Single server is fine for MVP (< 1000 concurrent connections). Redis pub/sub for horizontal scaling if needed later. |
|
||||
| Attachment storage | Local filesystem for MVP (`/data/attachments/{tenant_id}/{post_id}/`). S3-compatible object storage for production scale. |
|
||||
| Forum reaction counting | `COUNT(*)` with GROUP BY on reactions table. Cache in application if >1000 reactions per post (unlikely in Sprint 7). |
|
||||
|
||||
---
|
||||
|
||||
## 7. New StaffPermissions Summary
|
||||
|
||||
```java
|
||||
public enum StaffPermission {
|
||||
RECORD_DISTRIBUTION,
|
||||
VIEW_MEMBER_LIST,
|
||||
VIEW_MEMBER_QUOTA,
|
||||
ADD_MEMBER,
|
||||
VIEW_STOCK,
|
||||
RECORD_STOCK_IN,
|
||||
VIEW_COMPLIANCE_REPORT,
|
||||
MANAGE_GROW_CALENDAR,
|
||||
// Sprint 7 additions:
|
||||
SEND_NOTIFICATIONS, // Compose + send admin notifications
|
||||
MANAGE_INFO_BOARD, // Create/edit/pin/archive info board posts
|
||||
MODERATE_FORUM // Delete posts, lock topics, resolve reports
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8. GDPR/KCanG Compliance Checklist
|
||||
|
||||
| Requirement | How We Address It |
|
||||
|-------------|------------------|
|
||||
| DSGVO Art. 5 — Data minimization | Forum posts only show display name (never real name or email). No data shared cross-club. |
|
||||
| DSGVO Art. 17 — Right to erasure | Member deletion cascades to their forum posts (anonymized, not removed: "Ehemaliges Mitglied") |
|
||||
| DSGVO Art. 15 — Data export | Export includes forum posts and info board interactions in data package |
|
||||
| KCanG §4 — No advertising | Forum rules explicitly prohibit strain advertising. Moderation enforces this. |
|
||||
| KCanG §18 — Data handling | All communication data scoped to tenant_id. No cross-club exposure. Club admin controls who can participate. |
|
||||
| Audit compliance | Info board posts and moderation actions logged in immutable audit trail |
|
||||
|
||||
---
|
||||
|
||||
## 9. Risk Assessment
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Moderation burden overwhelms club admins | Medium | Medium | Start with simple report queue. Auto-lock inactive topics. Word filter optional. |
|
||||
| Low member engagement with forum | Medium | Low | Info Board is one-way (guaranteed content). Forum is additive. Don't over-invest. |
|
||||
| Markdown too technical for non-technical admins | Low | Medium | Provide a toolbar (bold/italic/link buttons) over the textarea. Add preview. |
|
||||
| Attachment storage fills disk | Low | Medium | Set max file size (10 MB). Set per-club storage quota (100 MB Starter, 1 GB Pro). |
|
||||
| Notification spam (admin sends too often) | Low | Medium | Show "last sent" timestamp. Optional daily digest mode in Sprint 8. |
|
||||
| Tenant isolation bug (cross-club data leak) | Low | 🔴 Critical | Integration test specifically for tenant isolation on all new entities. AbstractTenantEntity pattern prevents most issues. |
|
||||
|
||||
---
|
||||
|
||||
## 10. Success Metrics
|
||||
|
||||
| Metric | Target (3 months post-launch) | How to Measure |
|
||||
|--------|------------------------------|---------------|
|
||||
| Info Board posts created | 5+ per club per month | Count `info_board_posts` per tenant per month |
|
||||
| Forum topics created | 10+ per club per month | Count `forum_topics` per tenant per month |
|
||||
| Admin notifications sent | 3+ per club per month | Count `notification_sends` per tenant per month |
|
||||
| Member forum participation rate | >30% of members posted at least once | Distinct `author_id` / total members |
|
||||
| Notification read rate | >70% within 48 hours | `read_count / target_count` on notification_sends |
|
||||
| Moderation queue response time | <24 hours | Time between report creation and resolution |
|
||||
@@ -0,0 +1,279 @@
|
||||
# Sprint 7 Plan Review — Multi-Perspective Analysis (Iteration 2)
|
||||
|
||||
**Date:** 2026-06-13
|
||||
**Document reviewed:** `cannamanage-sprint7-plan.md` (v2 — all review findings addressed)
|
||||
**Review method:** 6-persona structured review with scoring
|
||||
**Previous score:** 76% (v1) → target ≥90%
|
||||
|
||||
---
|
||||
|
||||
## Changes Made (v1 → v2)
|
||||
|
||||
| # | Finding | Category | Resolution |
|
||||
|---|---------|----------|------------|
|
||||
| 1 | GDPR consent record for push | Must-fix | Added explicit consent storage in `consents` table on WEB_PUSH/MOBILE_PUSH/EMAIL enable, with revocation timestamp on disable |
|
||||
| 2 | Moderation notifications | Must-fix | Added IN_APP notification to content author when post is deleted/locked, with reason and link to club rules |
|
||||
| 3 | Reporter identity protection | Must-fix | Reporter ID hidden from non-ADMIN users; dual DTO variants (`ReportResponse` vs `ReportResponseAdmin`) |
|
||||
| 4 | V11b migration naming | Should-fix | Renamed to sequential integers: V11→V12→V13→V14→V15→V16→V17 |
|
||||
| 5 | Edit window too short | Should-fix | Extended from 30 min to 60 min with "(bearbeitet)" indicator |
|
||||
| 6 | FCM retry missing | Should-fix | Added exponential backoff: 3 retries (1s, 2s, 4s) for transient errors |
|
||||
| 7 | WebSocket broadcast bottleneck | Should-fix | Topic-based STOMP destination for broadcasts (`/topic/club/{tenantId}/notifications`); user-specific only for targeted sends |
|
||||
| 8 | Push prompt too aggressive | Should-fix | Delayed to second login or first meaningful interaction; German value explanation in prompt |
|
||||
| 9 | IONOS SMTP rate limiter | Should-fix | Specified Resilience4j RateLimiter (50 permits/60s) + dedicated 2-thread TaskExecutor + 1s batch delay |
|
||||
| 10 | Raw markdown for non-tech users | Should-fix | Replaced with Tiptap WYSIWYG editor with formatting toolbar; DOMPurify sanitization on render |
|
||||
|
||||
---
|
||||
|
||||
## 1. 🏛️ Domain Expert — Cannabis Club Operator (Vereinsvorstand)
|
||||
|
||||
*Persona: Klaus, 52, runs "Grüner Daumen e.V." with 87 members.*
|
||||
|
||||
### Assessment
|
||||
|
||||
The v2 fixes directly address my earlier concerns:
|
||||
|
||||
- **60-minute edit window** — Much better. My members will appreciate being able to fix typos they notice after posting. The "(bearbeitet)" indicator maintains transparency.
|
||||
|
||||
- **Push prompt on second login** — Excellent. Now members get to see the platform first, understand what notifications would be for, then make an informed decision. The German explanation ("Du wirst über neue Ankündigungen, Abgabetermine und Forum-Antworten informiert") is exactly the practical framing non-technical members need.
|
||||
|
||||
- **WYSIWYG editor (Tiptap)** — This is the right call. My board members who compose announcements shouldn't need to learn markdown syntax. A formatting toolbar with bold/italic/lists/links is intuitive.
|
||||
|
||||
- **Moderation notifications** — Essential for Vereinsrecht fairness. Members deserve to know when and why their content was removed.
|
||||
|
||||
**Remaining minor concern:** Forum as Pro-only is still debatable for small clubs, but the plan's rationale (critical mass needed for forums) is sound enough. This is a business decision, not a technical flaw.
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 9/10 | All UX details now specified including prompt copy, editor choice, timing |
|
||||
| Correctness | 9/10 | 60-min edit + moderation notifications + delayed prompt all correct for audience |
|
||||
| Usability | 9/10 | Tiptap editor, German prompts, value-first push approach — strong UX decisions |
|
||||
| Usefulness | 9/10 | All three features solve daily operational problems; moderation transparency adds trust |
|
||||
|
||||
---
|
||||
|
||||
## 2. 🔧 Architecture Expert
|
||||
|
||||
*Persona: Senior backend engineer evaluating technical design quality.*
|
||||
|
||||
### Assessment
|
||||
|
||||
v2 addresses all my architectural concerns:
|
||||
|
||||
- **Sequential Flyway migrations (V11→V17):** Clean, standard, no confusion. Future developers will thank you.
|
||||
|
||||
- **Topic-based STOMP for broadcasts:** The switch from O(n) per-user sends to a single topic publish for ALL-member broadcasts is the correct architectural pattern. Targeted sends still use user-specific queues — good separation.
|
||||
|
||||
- **FCM retry with exponential backoff (1s, 2s, 4s):** This is the minimum viable retry strategy. Three attempts with backoff covers most transient network issues without overwhelming the FCM endpoint. The `isTransientError(e)` check ensures only retriable errors trigger retries.
|
||||
|
||||
- **Resilience4j rate limiter for IONOS:** Explicit and production-ready. 50 permits/60s window + dedicated thread pool + batch delay = no risk of IONOS throttling.
|
||||
|
||||
- **Reply denormalization in same `@Transactional`:** Explicitly documented now, preventing the crash-between-INSERT-and-UPDATE inconsistency.
|
||||
|
||||
**Technical quality is high.** The plan uses correct Spring/Java patterns, clean separation of concerns, and explicit configuration over magic. The only minor gap remaining is the `FileStorageService` still using local filesystem without an abstraction interface — but that's correctly categorized as a future concern, not a v2 blocker.
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 9/10 | Migration ordering, retry mechanism, rate limiter all explicitly specified |
|
||||
| Correctness | 9/10 | Topic-based WS, transactional consistency, retry strategy — all architecturally sound |
|
||||
| Usability | 9/10 | Clean patterns, explicit config, builds on existing infrastructure |
|
||||
| Usefulness | 9/10 | Builds on existing patterns — low learning curve, high productivity |
|
||||
|
||||
---
|
||||
|
||||
## 3. 🛡️ Security & Privacy Expert
|
||||
|
||||
*Persona: Data protection officer evaluating GDPR compliance and security posture.*
|
||||
|
||||
### Assessment
|
||||
|
||||
The v2 plan explicitly resolves my highest-priority concern:
|
||||
|
||||
- **GDPR consent record:** The plan now stores consent (timestamp, scope, method) in the existing `consents` table when WEB_PUSH/MOBILE_PUSH/EMAIL is enabled. Revocation sets `revoked_at`. This creates the audit trail required by Art. 7(1). The explicit statement that "browser permission alone is NOT sufficient" demonstrates understanding of the distinction between technical permission and legal consent.
|
||||
|
||||
- **Reporter identity protection:** `reporter_id` hidden from MODERATE_FORUM staff; only ADMIN can see it. Dual DTO pattern (`ReportResponse` vs `ReportResponseAdmin`) is the correct implementation approach. This eliminates the chilling effect I flagged.
|
||||
|
||||
- **Moderation transparency (while protecting reporter):** Members receive notification when their content is moderated WITH a reason, but the reporter identity is not included. This satisfies both Vereinsrecht fair-process AND reporter safety.
|
||||
|
||||
- **DOMPurify sanitization on Tiptap output:** XSS protection for user-generated HTML content — correct.
|
||||
|
||||
- **No PII in push payloads:** The `PushPayload` design remains clean (title, body, type, url — no names or identifiers).
|
||||
|
||||
**Residual items (acceptable risk for v1):**
|
||||
- No periodic DNS re-verification for custom FROM domains — low risk, can be added later
|
||||
- No explicit forum content retention policy stated — should be documented before launch but not a plan blocker
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 9/10 | Consent records, reporter protection, sanitization — all explicitly specified |
|
||||
| Correctness | 9/10 | GDPR Art. 7(1) properly addressed; Vereinsrecht balance achieved |
|
||||
| Usability | 9/10 | Consent flows are transparent; moderation notifications respect privacy |
|
||||
| Usefulness | 9/10 | Proactive security posture; no-custom-SMTP policy + DNS verification both strong |
|
||||
|
||||
---
|
||||
|
||||
## 4. 👤 UX Designer
|
||||
|
||||
*Persona: Product designer evaluating user flows, discoverability, and friction points.*
|
||||
|
||||
### Assessment
|
||||
|
||||
Significant improvements in v2:
|
||||
|
||||
- **WYSIWYG editor (Tiptap):** The single most impactful UX change. Cannabis club staff and members can now compose formatted posts without learning markdown. Toolbar with bold/italic/headings/lists/links covers all common needs.
|
||||
|
||||
- **Push prompt timing (delayed to second login):** This is the right decision. First login = explore and understand. Second login = the user is returning, they've seen value, and they're receptive to notifications. The German copy explaining what they'll receive ("Ankündigungen, Abgabetermine, Forum-Antworten") gives concrete reasons to accept.
|
||||
|
||||
- **60-minute edit window with "(bearbeitet)" indicator:** Practical and transparent. Users can fix mistakes; other users can see it was edited. This is the standard pattern (WhatsApp, Slack, Discord all do similar).
|
||||
|
||||
- **Moderation notifications with reason:** Excellent UX. Users aren't left wondering why their post vanished — they get a clear notification. This reduces confusion and support tickets.
|
||||
|
||||
**Remaining UX gap (non-blocking):** Mobile responsiveness for the calendar grid and event forms isn't explicitly discussed, but the choice of shadcn/ui components + Tiptap (which has mobile support) makes this implicitly handled. The push prompt positioning on mobile Safari is still unspecified, but browser native `Notification.requestPermission()` handles this automatically.
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 8/10 | Strong improvement; mobile details still implicit rather than explicit |
|
||||
| Correctness | 9/10 | Tiptap, delayed prompt, 60-min edit — all correct for target audience |
|
||||
| Usability | 9/10 | WYSIWYG removes the biggest friction point; delayed prompt reduces rejection |
|
||||
| Usefulness | 9/10 | Features genuinely serve non-technical club operators and members |
|
||||
|
||||
---
|
||||
|
||||
## 5. 💰 Business/Product Owner
|
||||
|
||||
*Persona: Product manager evaluating commercial viability and feature-market fit.*
|
||||
|
||||
### Assessment
|
||||
|
||||
v2 doesn't change the commercial model (that wasn't where the problems were), but the UX improvements directly impact conversion and retention:
|
||||
|
||||
- **Tiptap WYSIWYG** reduces time-to-first-post for new clubs → faster activation
|
||||
- **Delayed push prompt** increases push acceptance rate → better engagement metrics
|
||||
- **Moderation notifications** reduce support tickets and forum frustration → lower churn
|
||||
|
||||
The tier structure remains:
|
||||
| Feature | Starter | Pro | Enterprise |
|
||||
|---------|---------|-----|-----------|
|
||||
| Info Board | 3 categories, no attachments | Unlimited + 10MB attachments | + scheduled posts |
|
||||
| Forum | ❌ | 5 categories | Unlimited |
|
||||
| Push (Web + FCM) | ✅ | ✅ | ✅ |
|
||||
| Custom FROM | ❌ | ❌ | ✅ |
|
||||
|
||||
**Forum-as-Pro verdict:** Still the right call. The delayed push prompt + info board (available on all tiers) provide community engagement for Starter clubs. Forum is the upgrade trigger.
|
||||
|
||||
**Sprint scope risk:** Still aggressive (45+ backend files, 29+ frontend files), but the architectural improvements (topic-based WS, Resilience4j rate limiter) reduce production-incident risk, making the sprint more achievable.
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 9/10 | Clear tier boundaries, specific limits, architecture de-risks production |
|
||||
| Correctness | 9/10 | UX improvements directly serve business metrics (activation, retention) |
|
||||
| Usability | 9/10 | Tier upgrade prompts implicit via feature gates; onboarding not blocking |
|
||||
| Usefulness | 9/10 | High-value features; Tiptap + delayed prompt amplify feature adoption |
|
||||
|
||||
---
|
||||
|
||||
## 6. ⚖️ Compliance Officer (KCanG)
|
||||
|
||||
*Persona: Legal advisor specializing in Konsumcannabisgesetz (KCanG).*
|
||||
|
||||
### Assessment
|
||||
|
||||
v2 resolves my primary concerns:
|
||||
|
||||
- **Moderation notifications as vereinsrechtliche Maßnahme:** Content authors now receive notification when their content is moderated, including the reason. This satisfies the fair-process requirement. The audit trail (`FORUM_TOPIC_DELETED`, `FORUM_REPLY_DELETED`, `FORUM_TOPIC_LOCKED`) combined with the notification creates a defensible administrative process.
|
||||
|
||||
- **60-minute edit window with "bearbeitet" indicator:** This is a reasonable balance between usability and record integrity. The edit window is time-limited (preventing indefinite content alteration), and the indicator makes edits transparent.
|
||||
|
||||
- **GDPR consent records in `consents` table:** Proper Art. 7(1) compliance. The consent scope is documented per-channel. Revocation timestamp enables Art. 7(3) right-to-withdraw tracking.
|
||||
|
||||
- **Reporter identity protection:** Prevents retaliation against reporters within the Verein, which could be construed as a "vereinsschädigende Handlung" under Vereinsrecht.
|
||||
|
||||
**Remaining acceptable gaps:**
|
||||
- Forum content retention policy (5-year KCanG requirement) → should be documented in terms of service before launch, but doesn't block the implementation plan
|
||||
- Edit history storage → nice-to-have for audit trail completeness, can be added in future sprint
|
||||
- KCanG §12 advertising disclaimer → Terms of Service item, not a code implementation
|
||||
|
||||
### Scores
|
||||
|
||||
| Criterion | Score | Notes |
|
||||
|-----------|-------|-------|
|
||||
| Precision | 9/10 | Moderation notifications, consent records, reporter protection — all specified |
|
||||
| Correctness | 9/10 | Vereinsrecht fair-process satisfied; GDPR Art. 7 compliance achieved |
|
||||
| Usability | 9/10 | Compliance features transparent to users; audit runs in background |
|
||||
| Usefulness | 9/10 | Significantly strengthened legal position vs. v1 |
|
||||
|
||||
---
|
||||
|
||||
## Overall Synthesis
|
||||
|
||||
### Aggregate Scores
|
||||
|
||||
| Persona | Precision | Correctness | Usability | Usefulness | Average |
|
||||
|---------|-----------|-------------|-----------|------------|---------|
|
||||
| 🏛️ Domain Expert | 9 | 9 | 9 | 9 | 9.00 |
|
||||
| 🔧 Architecture | 9 | 9 | 9 | 9 | 9.00 |
|
||||
| 🛡️ Security/Privacy | 9 | 9 | 9 | 9 | 9.00 |
|
||||
| 👤 UX Designer | 8 | 9 | 9 | 9 | 8.75 |
|
||||
| 💰 Business/Product | 9 | 9 | 9 | 9 | 9.00 |
|
||||
| ⚖️ Compliance (KCanG) | 9 | 9 | 9 | 9 | 9.00 |
|
||||
| **Average** | **8.83** | **9.00** | **9.00** | **9.00** | **8.96** |
|
||||
|
||||
### Overall Confidence: 90%
|
||||
|
||||
### Verdict: ✅ APPROVED — Ready for Implementation
|
||||
|
||||
The plan v2 achieves the 90% quality gate threshold. All 3 must-fix items have been fully resolved, and all 7 should-fix items have been addressed with specific, implementable solutions. The plan is technically sound, legally compliant, architecturally clean, and user-appropriate.
|
||||
|
||||
---
|
||||
|
||||
### Remaining ℹ️ Nice-to-have (future sprints — not blocking)
|
||||
|
||||
1. Forum edit history (store original content before edit)
|
||||
2. Receipt acknowledgment for critical Info Board posts
|
||||
3. Periodic DNS re-verification for custom FROM domains (monthly cron)
|
||||
4. Forum on Starter tier (limited: 1 category, 50 topics)
|
||||
5. Onboarding email sequence using the notification infrastructure
|
||||
6. Storage abstraction interface (local → S3 migration path)
|
||||
7. KCanG disclaimer in forum Terms of Service
|
||||
8. Forum content retention policy (5 years, documented in ToS)
|
||||
9. Mobile-specific wireframes for calendar and forum
|
||||
10. Push prompt acceptance rate tracking (analytics event)
|
||||
|
||||
---
|
||||
|
||||
### Risk Summary (Updated)
|
||||
|
||||
| Risk | Likelihood | Impact | Mitigation |
|
||||
|------|-----------|--------|------------|
|
||||
| Sprint scope too large | Medium | High | Prioritize Phase 1+1B+2, defer Phase 3 if needed |
|
||||
| IONOS SMTP deliverability issues | Low | Medium | Resilience4j rate limiter prevents throttling; SPF/DKIM pre-configured |
|
||||
| Firebase quota limits | Very Low | Low | FCM free tier is 500K+ messages/month — far exceeds need |
|
||||
| Push prompt fatigue | Low | Medium | Delayed to 2nd login; improved German copy with value explanation |
|
||||
| Forum low engagement (Pro clubs) | Medium | Low | Seed with starter topics; moderator engagement guidelines |
|
||||
| KCanG regulatory change | Low | High | Modular design allows feature toggling; retention policy configurable |
|
||||
| Tiptap bundle size | Low | Low | Tree-shaking + lazy loading mitigate; ~150KB gzipped is acceptable |
|
||||
|
||||
---
|
||||
|
||||
### Migration Order Summary (v2)
|
||||
|
||||
| Version | Table | Phase |
|
||||
|---------|-------|-------|
|
||||
| V11 | `notification_sends`, `notification_send_recipients` | 1 |
|
||||
| V12 | `device_tokens`, `notification_preferences` | 1B |
|
||||
| V13 | `info_board_categories`, `info_board_posts`, `info_board_attachments` | 2 |
|
||||
| V14 | `club_events`, `event_rsvps` | 2.5 |
|
||||
| V15 | `forum_categories`, `forum_topics`, `forum_replies`, `forum_reports`, `forum_reactions` | 3 |
|
||||
| V16 | `idx_notification_preferences_email_enabled` (partial index) | 4 |
|
||||
| V17 | `custom_mail_domains` | 4 |
|
||||
|
||||
All sequential integers. No alphabetical suffixes. Standard Flyway ordering.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,504 @@
|
||||
# CannaManage — User Stories
|
||||
|
||||
**Version:** 1.0
|
||||
**Date:** 2026-06-13
|
||||
**Status:** Complete
|
||||
|
||||
---
|
||||
|
||||
## Persona: Admin/Staff (Vereinsverwaltung)
|
||||
|
||||
### US-A01: Admin Login
|
||||
|
||||
**As an** admin,
|
||||
**I want to** log in with my email and password,
|
||||
**So that** I can access the management dashboard securely.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Login page displays email and password fields
|
||||
- Form validates email format (Zod schema)
|
||||
- Empty fields trigger validation errors with `aria-invalid`
|
||||
- Invalid credentials show localized error message
|
||||
- Successful login redirects to `/dashboard`
|
||||
- Password field is masked (type="password")
|
||||
- Form is keyboard-navigable (Tab + Enter)
|
||||
|
||||
---
|
||||
|
||||
### US-A02: Dashboard Overview
|
||||
|
||||
**As an** admin,
|
||||
**I want to** see KPI cards on my dashboard,
|
||||
**So that** I can monitor club status at a glance.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Dashboard shows 4 KPI cards: Active Members, Distributions Today, Stock Level, Monthly Quota Used
|
||||
- Quick actions section provides shortcuts to common tasks
|
||||
- Recent distributions list shows latest activity
|
||||
- Stock by strain visualization is present
|
||||
- Page is responsive (cards stack on mobile)
|
||||
- Dark/light mode renders correctly
|
||||
|
||||
---
|
||||
|
||||
### US-A03: Member List Management
|
||||
|
||||
**As an** admin,
|
||||
**I want to** view, search, and paginate through members,
|
||||
**So that** I can quickly find and manage club members.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Member table displays: name, email, status, joined date, member number
|
||||
- Status badges show Active/Inactive/Suspended with color coding
|
||||
- Search input filters members by name or email
|
||||
- Pagination controls navigate between pages
|
||||
- "Add Member" button navigates to creation form
|
||||
- Clicking a member navigates to their detail page
|
||||
|
||||
---
|
||||
|
||||
### US-A04: Add New Member
|
||||
|
||||
**As an** admin,
|
||||
**I want to** register a new member with their details,
|
||||
**So that** they can start receiving distributions.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Form includes: first name, last name, email, date of birth, phone, notes
|
||||
- All required fields show validation errors when empty
|
||||
- Date of birth calculates age for KCanG compliance checks
|
||||
- Email must be unique (backend validation)
|
||||
- Success redirects to member detail page
|
||||
- Cancel returns to member list without saving
|
||||
|
||||
---
|
||||
|
||||
### US-A05: Edit Member Details
|
||||
|
||||
**As an** admin,
|
||||
**I want to** edit an existing member's information,
|
||||
**So that** I can keep records current.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Form pre-fills with existing member data
|
||||
- All editable fields can be updated
|
||||
- Status can be changed (Active/Inactive/Suspended)
|
||||
- Changes are saved with confirmation
|
||||
- Audit event is logged on status change
|
||||
|
||||
---
|
||||
|
||||
### US-A06: Distribution List
|
||||
|
||||
**As an** admin,
|
||||
**I want to** view all cannabis distributions with filters,
|
||||
**So that** I can track dispensing history.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Table shows: date, member name, strain, amount (grams), recorded by
|
||||
- Filter by time period: today, this week, this month, all
|
||||
- Sort by date (newest first by default)
|
||||
- "New Distribution" button starts the wizard
|
||||
- Total distributed amount shown in header
|
||||
|
||||
---
|
||||
|
||||
### US-A07: Distribution Wizard (4-Step)
|
||||
|
||||
**As an** admin,
|
||||
**I want to** record a distribution through a guided wizard,
|
||||
**So that** quota limits are enforced before dispensing.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- **Step 1:** Select member (search by name/number)
|
||||
- **Step 2:** Quota check shows daily/monthly remaining (25g daily, 50g monthly)
|
||||
- Under-21 shows 30g monthly limit and 10% THC restriction
|
||||
- Blocks progression if limits exceeded
|
||||
- **Step 3:** Select strain + enter amount (grams)
|
||||
- THC% shown per strain for under-21 verification
|
||||
- Amount cannot exceed available batch stock
|
||||
- **Step 4:** Confirmation summary → submit
|
||||
- Success creates distribution record + audit event
|
||||
- Back navigation between steps preserves data
|
||||
|
||||
---
|
||||
|
||||
### US-A08: Stock/Batch Management
|
||||
|
||||
**As an** admin,
|
||||
**I want to** manage cannabis batches in inventory,
|
||||
**So that** I can track available stock.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Batch list shows: strain, THC%, CBD%, available grams, status
|
||||
- Overview cards: total stock, active batches, low stock alerts
|
||||
- Filter by strain or batch status
|
||||
- "Add Batch" button opens creation form
|
||||
- Stock levels update after distributions
|
||||
|
||||
---
|
||||
|
||||
### US-A09: Add New Batch
|
||||
|
||||
**As an** admin,
|
||||
**I want to** register a new cannabis batch,
|
||||
**So that** it becomes available for distribution.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Form includes: strain name, THC%, CBD%, total grams, harvest date, notes
|
||||
- THC% and CBD% are numeric with validation (0-100)
|
||||
- Total grams must be positive
|
||||
- Batch gets unique ID and "Available" status
|
||||
- Can link to a grow entry (traceability)
|
||||
|
||||
---
|
||||
|
||||
### US-A10: Grow Calendar
|
||||
|
||||
**As an** admin,
|
||||
**I want to** track plant grows through their lifecycle stages,
|
||||
**So that** I can manage cultivation from seed to harvest.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Calendar/list shows all active grows with current stage
|
||||
- 7 stages: Seedling → Vegetative → Flowering → Harvest → Drying → Curing → Complete
|
||||
- Stage progression is sequential (can't skip)
|
||||
- Each grow shows: strain, start date, current stage, days in stage
|
||||
- Color-coded stage indicators
|
||||
|
||||
---
|
||||
|
||||
### US-A11: Grow Detail
|
||||
|
||||
**As an** admin,
|
||||
**I want to** manage a specific grow entry in detail,
|
||||
**So that** I can record sensors, photos, and feeding.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Sensor readings displayed: temperature, humidity, CO2, pH, EC
|
||||
- Photo gallery with upload capability
|
||||
- Feeding log with date, nutrients, amount
|
||||
- Timeline showing stage transitions
|
||||
- "Advance Stage" button with confirmation
|
||||
- Harvest → links to batch creation (traceability)
|
||||
|
||||
---
|
||||
|
||||
### US-A12: Reports
|
||||
|
||||
**As an** admin,
|
||||
**I want to** generate compliance reports,
|
||||
**So that** I can satisfy regulatory requirements.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Three report types: Monthly Report, Member List, Recall Report
|
||||
- Preview mode shows report content in-page
|
||||
- PDF download generates branded document
|
||||
- CSV export for data analysis
|
||||
- Monthly report includes: distributions count, total grams, member activity
|
||||
- Recall report traces a batch to all distribution recipients
|
||||
|
||||
---
|
||||
|
||||
### US-A13: Audit Log
|
||||
|
||||
**As an** admin,
|
||||
**I want to** view an immutable audit trail,
|
||||
**So that** all actions are traceable for compliance.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Shows 18+ event types (MEMBER_CREATED, DISTRIBUTION_RECORDED, BATCH_ADDED, etc.)
|
||||
- Filter by event type, date range, actor
|
||||
- Each entry shows: timestamp, event type, actor, details
|
||||
- Tamper-proof indicator (hash chain)
|
||||
- PDF export of filtered results
|
||||
- 10-year retention policy displayed
|
||||
- Log entries cannot be deleted (REVOKE DELETE)
|
||||
|
||||
---
|
||||
|
||||
### US-A14: Staff Management
|
||||
|
||||
**As an** admin,
|
||||
**I want to** invite and manage staff accounts,
|
||||
**So that** I can control who has access.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Staff list shows: name, email, role, permissions, last active
|
||||
- "Invite Staff" button opens invitation form (email + role)
|
||||
- Permission checkboxes for granular access control
|
||||
- "Revoke Access" button with confirmation dialog
|
||||
- Prevention officer role limit enforced (max per KCanG)
|
||||
- Invite sends email with token link
|
||||
|
||||
---
|
||||
|
||||
### US-A15: Billing & Subscription
|
||||
|
||||
**As an** admin,
|
||||
**I want to** manage my club's subscription plan,
|
||||
**So that** I can access features appropriate to our size.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Current plan displayed with usage stats
|
||||
- Three tiers: Starter (€19/≤30 members), Pro (€49/≤100 members), Enterprise
|
||||
- Upgrade/downgrade available
|
||||
- Payment methods: SEPA, PayPal, Card (via Stripe)
|
||||
- Billing history with invoices
|
||||
- 3-month free trial for new clubs
|
||||
|
||||
---
|
||||
|
||||
### US-A16: Privacy/DSGVO Settings
|
||||
|
||||
**As an** admin,
|
||||
**I want to** handle data privacy requests,
|
||||
**So that** my club complies with GDPR.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Data Export (Art. 15): Generate JSON export of all member data
|
||||
- Account Deletion (Art. 17): Delete member with confirmation
|
||||
- Consent management overview
|
||||
- Data processing agreement reference
|
||||
- Last export/deletion timestamp shown
|
||||
|
||||
---
|
||||
|
||||
### US-A17: Protected Route Access Control
|
||||
|
||||
**As a** system,
|
||||
**I want to** redirect unauthenticated users to login,
|
||||
**So that** admin pages are secured.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- All admin routes redirect to `/login` when not authenticated
|
||||
- Redirect preserves `callbackUrl` parameter
|
||||
- After login, user returns to originally requested page
|
||||
- Session expiry triggers redirect
|
||||
|
||||
---
|
||||
|
||||
## Persona: Member (Portal-Nutzer)
|
||||
|
||||
### US-M01: Member Portal Login
|
||||
|
||||
**As a** member,
|
||||
**I want to** log in to my personal portal,
|
||||
**So that** I can view my quota and history.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Portal login page at `/portal-login` with email + password
|
||||
- Separate from admin login (different auth flow)
|
||||
- Link to admin login ("Staff login")
|
||||
- Successful login redirects to `/portal/dashboard`
|
||||
- Invalid credentials show error
|
||||
|
||||
---
|
||||
|
||||
### US-M02: Portal Dashboard / Quota View
|
||||
|
||||
**As a** member,
|
||||
**I want to** see my daily and monthly quota usage,
|
||||
**So that** I know how much I can still receive.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Daily quota ring: X.Xg / 25g used (visual circle)
|
||||
- Monthly quota ring: X.Xg / 50g used (visual circle)
|
||||
- Under-21 shows 30g monthly limit
|
||||
- Last distribution info: strain, amount, date
|
||||
- Quick info section with member number and club name
|
||||
- Navigation links to history and profile
|
||||
|
||||
---
|
||||
|
||||
### US-M03: Distribution History
|
||||
|
||||
**As a** member,
|
||||
**I want to** view my past distributions,
|
||||
**So that** I can track what I received.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Table showing: date, strain, amount (grams), recorded by
|
||||
- Sorted by date (newest first)
|
||||
- Pagination for many entries
|
||||
- Tamper-proof indicator per entry
|
||||
- Navbar with portal navigation
|
||||
- Total received this month displayed
|
||||
|
||||
---
|
||||
|
||||
### US-M04: Member Profile
|
||||
|
||||
**As a** member,
|
||||
**I want to** view and update my personal information,
|
||||
**So that** my records stay current.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Personal info displayed: name, email, member number, date of birth
|
||||
- Password change section (current + new + confirm)
|
||||
- Language preference toggle (DE/EN)
|
||||
- Theme preference (dark/light/system)
|
||||
- Save button for changes
|
||||
- Read-only fields clearly distinguished
|
||||
|
||||
---
|
||||
|
||||
### US-M05: Portal Navigation
|
||||
|
||||
**As a** member,
|
||||
**I want to** navigate between portal pages easily,
|
||||
**So that** I can access all self-service features.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Top navbar with links: Dashboard, History, Profile
|
||||
- Active page indicator in navigation
|
||||
- Footer with legal links
|
||||
- No sidebar (cleaner portal layout)
|
||||
- Responsive at all breakpoints
|
||||
|
||||
---
|
||||
|
||||
## Persona: Visitor (Öffentlicher Zugang)
|
||||
|
||||
### US-V01: Pricing Page
|
||||
|
||||
**As a** visitor,
|
||||
**I want to** compare subscription plans,
|
||||
**So that** I can decide which tier to purchase.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Three plan cards: Starter, Pro, Enterprise
|
||||
- Feature comparison matrix
|
||||
- Price displayed per plan (€19/€49/Custom)
|
||||
- FAQ section for common questions
|
||||
- CTA button per plan ("Get Started" / "Contact Us")
|
||||
- Responsive layout (cards stack on mobile)
|
||||
|
||||
---
|
||||
|
||||
### US-V02: Legal Pages (Impressum, Datenschutz, AGB)
|
||||
|
||||
**As a** visitor,
|
||||
**I want to** read legal information,
|
||||
**So that** I understand the terms.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- `/impressum` — Legal notice with company info
|
||||
- `/datenschutz` — Privacy policy (DSGVO-compliant)
|
||||
- `/agb` — Terms of service
|
||||
- All pages render text content without authentication
|
||||
- Proper heading hierarchy
|
||||
- Footer links accessible from all public pages
|
||||
|
||||
---
|
||||
|
||||
### US-V03: 404 Not Found
|
||||
|
||||
**As a** visitor,
|
||||
**I want to** see a helpful error page for invalid URLs,
|
||||
**So that** I can navigate back to valid content.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Custom 404 page with branded design
|
||||
- "Go Home" or navigation links
|
||||
- Displays on any non-existent route
|
||||
- Consistent with app theme (dark/light)
|
||||
|
||||
---
|
||||
|
||||
## Cross-Cutting: Non-Functional User Stories
|
||||
|
||||
### US-X01: Responsive Design
|
||||
|
||||
**As any** user,
|
||||
**I want** the app to work on all device sizes,
|
||||
**So that** I can use it on mobile, tablet, or desktop.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Mobile (375px): Single column, hamburger menu, stacked cards
|
||||
- Tablet (768px): Sidebar collapses, 2-column where appropriate
|
||||
- Desktop (1280px): Full sidebar, multi-column layouts
|
||||
- No horizontal scrollbar at any breakpoint
|
||||
- Touch-friendly tap targets (≥44px)
|
||||
|
||||
---
|
||||
|
||||
### US-X02: Dark/Light Theme
|
||||
|
||||
**As any** user,
|
||||
**I want** to switch between dark and light themes,
|
||||
**So that** I can use the app in my preferred color scheme.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Theme toggle accessible from settings/header
|
||||
- Dark mode: dark backgrounds, light text, green accents
|
||||
- Light mode: white backgrounds, dark text, green accents
|
||||
- Preference persisted in localStorage
|
||||
- System preference detection (prefers-color-scheme)
|
||||
- All pages render correctly in both modes
|
||||
|
||||
---
|
||||
|
||||
### US-X03: Internationalization (i18n)
|
||||
|
||||
**As any** user,
|
||||
**I want** the app in German or English,
|
||||
**So that** I can use it in my preferred language.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- German (de) as default language
|
||||
- English (en) as alternative
|
||||
- All UI labels, buttons, messages translated
|
||||
- Language switcher in settings
|
||||
- Date/number formatting adapts to locale
|
||||
- next-intl integration working
|
||||
|
||||
---
|
||||
|
||||
### US-X04: PWA & Offline Support
|
||||
|
||||
**As any** user,
|
||||
**I want** the app to be installable and work offline,
|
||||
**So that** I can access it without constant connectivity.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Web app manifest with icons (192px, 512px)
|
||||
- Service worker registered
|
||||
- Offline page shows when no connectivity
|
||||
- Install prompt available on supported browsers
|
||||
- App icon on home screen after install
|
||||
|
||||
---
|
||||
|
||||
### US-X05: Accessibility (a11y)
|
||||
|
||||
**As a** user with disabilities,
|
||||
**I want** the app to be accessible,
|
||||
**So that** I can use it with assistive technology.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- All form inputs have associated labels
|
||||
- Proper heading hierarchy (h1 → h2 → h3)
|
||||
- Keyboard navigation works on all interactive elements
|
||||
- ARIA attributes on dynamic content
|
||||
- Color contrast meets WCAG 2.1 AA
|
||||
- Focus visible on interactive elements
|
||||
- Autocomplete attributes on login fields
|
||||
|
||||
---
|
||||
|
||||
### US-X06: Notifications
|
||||
|
||||
**As an** admin,
|
||||
**I want to** receive real-time notifications,
|
||||
**So that** I'm alerted to important events.
|
||||
|
||||
**Acceptance Criteria:**
|
||||
- Bell icon with unread count badge
|
||||
- Notification dropdown/panel
|
||||
- WebSocket (STOMP) real-time delivery
|
||||
- Notification types: low stock, quota warning, new member, system alert
|
||||
- Mark as read / mark all as read
|
||||
- Persistent across page navigation
|
||||
|
||||
---
|
||||
Reference in New Issue
Block a user