mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 21:41:09 +02:00
Macht @mana/feedback omnipräsent + öffentlich. Phase 2 vom
Public-Community-Hub-Plan (docs/plans/feedback-hub-public.md).
Inline-Touchpoints:
- FeedbackHook: Lightbulb-Button, opens FeedbackQuickModal vorausgefüllt
mit module-context. Auto-injected in jeder ModuleShell-Header
(window-actions row), opt-out via hideFeedback prop.
- GlobalFeedbackPill: Floating "Idee?"-Pill bottom-right, self-hides
auf /onboarding, /feedback, /community, und für Gäste. Auto-detected
module-context aus URL bzw. ?app=-Param.
- FeedbackQuickModal: 3-Klick-Submit mit Category-Dropdown, Public-
Toggle, "Sichtbar als {Pseudonym}"-Confirm-State.
Community-Modul (eigenes Modul, in Workbench drop-bar):
- module.config.ts (server-only, keine Sync-Tabellen)
- queries.ts: useCommunityFeed + useCommunityItem mit auth-aware Switch
zwischen public + auth-enriched Endpoints
- ListView/DetailView/RoadmapView mit ItemCard-Component
- App-Registry-Eintrag (Megaphone-Icon, #F59E0B)
Public-Mirror-Routes (kein AuthGate):
- /community — Feed mit SSR-Pre-Render via Public-Endpoint
- /community/[id] — Single item + replies, SSR
- /community/roadmap — Kanban Submitted/Planned/InProgress/Completed
- /community/admin — Founder-only Triage (Status, AdminResponse,
visibility-Toggle); Client-side role-gate
redirect → /community.
SEO: <svelte:head> mit title/description, <noscript>-Fallback,
Cache-Headers stale-while-revalidate.
API:
- web's lib/api/feedback.ts pointed an die echte mana-analytics-URL
(3064 dev) statt mana-auth. Neuer publicFeedbackService für
unauthenticated SSR.
- getManaAnalyticsUrl() in lib/api/config.ts.
Onboarding-Wish public-by-default:
- Disclosure-Text: "Erscheint in Community-Page als Tier-Pseudonym".
- Toggle "Öffentlich teilen" / "Nur für Admins" mit Default on.
- Submitted-Confirm zeigt das generierte Display-Name.
Plan-Doc-Updates:
- feedback-hub.md Phase 2 abgespeckt → Verweis auf feedback-hub-public.md
- feedback-hub-public.md komplett: Architektur-Optionen A-E, Phase 2.x,
Phase 3 Roadmap (16 Future-Features), Risiken.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
225 lines
9.2 KiB
Markdown
225 lines
9.2 KiB
Markdown
---
|
|
status: draft
|
|
owner: till
|
|
created: 2026-04-26
|
|
---
|
|
|
|
# `@mana/feedback` als zentraler Feedback-Hub
|
|
|
|
> Alle nutzergenerierten Rückmeldungen — Bug-Reports, Feature-Wünsche, Lob,
|
|
> Onboarding-Wünsche, NPS, Churn-Gründe — landen in einem System: dem
|
|
> `user_feedback`-Table im `mana-analytics`-Service, getypt über das
|
|
> `@mana/feedback`-Package. Ein Schema, ein API-Surface, ein Admin-Hub.
|
|
|
|
---
|
|
|
|
## Ist-Zustand (2026-04-26)
|
|
|
|
- **Package** `packages/feedback/` exportiert Typen, einen Service-Factory
|
|
(`createFeedbackService`), und UI-Komponenten (`FeedbackPage`,
|
|
`FeedbackForm`, `FeedbackList`, `VoteButton`, `StatusBadge`).
|
|
- **Server** `services/mana-analytics` (Port 3064): Postgres-Schema
|
|
`feedback`, Tabellen `user_feedback` + `feedback_votes`, REST-Endpoints
|
|
unter `/api/v1/feedback/*`. Auto-Title via `mana-llm` beim Submit.
|
|
- **Web-App** Singleton in `apps/mana/apps/web/src/lib/api/feedback.ts`,
|
|
Modul-View in `apps/mana/apps/web/src/lib/modules/feedback/ListView.svelte`,
|
|
Route `/feedback`. Voting + Public-Liste sind voll verdrahtet.
|
|
- **Drift**, die fixen müssen, bevor wir mehr draufpacken:
|
|
| Bereich | Package | DB |
|
|
|----------|--------------------------------------------|--------------------------------------|
|
|
| Status | `submitted/under_review/.../completed/declined` | `new/reviewed/.../done/rejected` |
|
|
| Category | `bug/feature/improvement/question/other` | + `praise` (zusätzlich) |
|
|
| Default | `submitted` (impliziert) | `new` |
|
|
Konsequenz: Client typisiert Status als `'submitted'`, kriegt aber
|
|
`'new'` zurück → `FEEDBACK_STATUS_CONFIG[status]` ist `undefined` →
|
|
StatusBadge rendert leise nichts. Niemand fällt's auf weil keine
|
|
Admin-UI Status setzt und alle Records auf Default sitzen.
|
|
- **Fundamentale Annahmen, die wir nicht ändern:**
|
|
- Server-only Persistence (kein Dexie / Local-First, kein mana-sync).
|
|
Submit ist ein einziger POST, fail-soft.
|
|
- Feedback ist **nicht** im Mana-Crypto-Pfad — Klartext im DB.
|
|
OK für Bug-Reports & Wünsche; sensible Daten gehören eh nicht hier rein.
|
|
|
|
---
|
|
|
|
## Phase 0 — Drift fixen *(Refactor, ein Commit)*
|
|
|
|
Ziel: Package + DB konsistent, Defaults sauber, keine Funktions-Erweiterung.
|
|
|
|
### 0a. Status-Enum: Package gewinnt
|
|
|
|
PostgreSQL kann seit 10 `ALTER TYPE ... RENAME VALUE`, das ist non-destructive
|
|
und behält die Sortierung der Enum-Werte. Wir benennen die DB-Werte um, sodass
|
|
sie zum Package passen:
|
|
|
|
```sql
|
|
ALTER TYPE feedback.feedback_status RENAME VALUE 'new' TO 'submitted';
|
|
ALTER TYPE feedback.feedback_status RENAME VALUE 'reviewed' TO 'under_review';
|
|
ALTER TYPE feedback.feedback_status RENAME VALUE 'done' TO 'completed';
|
|
ALTER TYPE feedback.feedback_status RENAME VALUE 'rejected' TO 'declined';
|
|
ALTER TABLE feedback.user_feedback ALTER COLUMN status SET DEFAULT 'submitted';
|
|
```
|
|
|
|
Drizzle-Schema (`services/mana-analytics/src/db/schema/feedback.ts`) parallel
|
|
auf die neuen Werte ziehen, sodass `db:push` nicht versucht, neu anzulegen.
|
|
|
|
### 0b. Category 'praise' ins Package aufnehmen
|
|
|
|
Package hat `bug/feature/improvement/question/other`. DB hat `praise`
|
|
zusätzlich. Wir nehmen `'praise'` ins Package mit Label "Lob" und
|
|
behalten DB unverändert.
|
|
|
|
### 0c. Single Source of Truth
|
|
|
|
Mana-analytics importiert die Enum-Werte ab jetzt aus `@mana/feedback`
|
|
statt eigenes `pgEnum`-Array zu pflegen. Verhindert künftige Drift
|
|
strukturell. (Falls drizzle-kit das nicht direkt kann, dann mindestens
|
|
ein Test in `services/mana-analytics` der die Listen vergleicht.)
|
|
|
|
### Migrations-Workflow
|
|
|
|
mana-analytics benutzt aktuell `drizzle-kit push` (kein
|
|
Migrations-Verzeichnis). Für `ALTER TYPE RENAME VALUE` ist push nicht
|
|
zuverlässig — das ist ein hand-authored SQL-Step.
|
|
|
|
→ Neue Datei `services/mana-analytics/drizzle/0001_align-feedback-enums.sql`
|
|
einführen, in Setup-README dokumentieren ("apply manually before db:push").
|
|
Pattern wie `apps/api/drizzle/{schema}/*.sql`.
|
|
|
|
---
|
|
|
|
## Phase 1 — Onboarding-Wish *(Feature, ein Commit)*
|
|
|
|
Ziel: Letzter Onboarding-Schritt ist Freitext-Frage "Was wünschst du dir
|
|
von Mana?", deren Antwort als `@mana/feedback`-Record landet.
|
|
|
|
### 1a. Neue Category `'onboarding-wish'`
|
|
|
|
```sql
|
|
ALTER TYPE feedback.feedback_category ADD VALUE IF NOT EXISTS 'onboarding-wish';
|
|
```
|
|
|
|
Im Package:
|
|
- `FeedbackCategory` um `'onboarding-wish'` erweitern
|
|
- `FEEDBACK_CATEGORY_LABELS['onboarding-wish'] = 'Was ich mir wünsche'`
|
|
|
|
### 1b. Onboarding-Flow-Store erweitern
|
|
|
|
`apps/mana/apps/web/src/lib/stores/onboarding-flow.svelte.ts`:
|
|
- `pendingWish: string | null`
|
|
- `setPendingWish(value)` / `reset()` mit dabei
|
|
|
|
### 1c. Layout: 3 → 4 Step-Dots
|
|
|
|
`apps/mana/apps/web/src/routes/(app)/onboarding/+layout.svelte`:
|
|
- `currentStep`-Mapping: `/onboarding/wish` → 3
|
|
- Dots-Array `[0,1,2,3]`
|
|
- aria-valuemax = 4
|
|
|
|
### 1d. Neuer Screen `/onboarding/wish/+page.svelte`
|
|
|
|
- **Aktivierungstext:**
|
|
> # Eine letzte Sache
|
|
> Was wünschst du dir von Mana? Wofür willst du's nutzen, was erhoffst du dir?
|
|
>
|
|
> Schreib einfach, wie's dir kommt — wir lesen jede Antwort und sie
|
|
> hilft uns, Mana für dich besser zu machen.
|
|
- **Textarea**: `maxlength=2000`, autofocus, `auto-grow`
|
|
- **Buttons**: Zurück (→ `/onboarding/templates`) + Fertig
|
|
- **Submit-Logik** (Fertig):
|
|
1. Wenn Textarea nicht leer → `feedbackService.createFeedback({
|
|
category: 'onboarding-wish', isPublic: false, feedbackText: trimmed })`
|
|
**fail-soft** (`try/catch`, nur `console.warn`, kein UI-Block)
|
|
2. `onboardingStatus.markComplete()`
|
|
3. `onboardingFlow.reset()`
|
|
4. `goto('/')`
|
|
- **Wenn Textarea leer + Fertig**: gleicher Flow ohne Submit.
|
|
- **isPublic = false** by default, weil Wünsche persönliche Statements sind,
|
|
kein Public-Voting-Material. (Lässt sich später per Admin-Action publishen.)
|
|
|
|
### 1e. Templates-Screen umbiegen
|
|
|
|
`apps/mana/apps/web/src/routes/(app)/onboarding/templates/+page.svelte`:
|
|
- Fertig-Button heißt jetzt "Weiter" und routet `goto('/onboarding/wish')`
|
|
- `markComplete` + `reset` wandern raus aus templates → in den wish-Screen
|
|
- Templates-Save (createScene) bleibt wie er ist
|
|
|
|
### Akzeptanzkriterien
|
|
|
|
- 4 Step-Dots im Footer; bei `/onboarding/wish` ist Dot 4 aktiv
|
|
- Globaler Skip-Button (unten links) funktioniert auf allen 4 Screens
|
|
(markComplete + `/`)
|
|
- Submit von "Was wünschst du dir" landet als Row in `user_feedback`
|
|
mit `category='onboarding-wish'`, `is_public=false`
|
|
- Wenn `mana-analytics` nicht erreichbar ist, blockiert das Onboarding
|
|
nicht — User kommt trotzdem auf `/`
|
|
- Bestehende Public-Feedback-Liste auf `/feedback` zeigt
|
|
`onboarding-wish`-Records **nicht** (weil `is_public=false`)
|
|
|
|
---
|
|
|
|
## Phase 2 — Public Community-Hub *(großer Sprint, eigener Plan)*
|
|
|
|
Phase 2 wurde komplett neu geschnitten: nicht mehr nur Admin-Triage und
|
|
Buttons, sondern eine **vollständige Public-Community-Surface** mit
|
|
Pseudonym-System, Anonymisierung, omnipresenten Inline-Hooks und einem
|
|
eigenen `community`-Modul.
|
|
|
|
→ Detailplan mit Architektur-Optionen für jede Sub-Entscheidung:
|
|
**[`docs/plans/feedback-hub-public.md`](feedback-hub-public.md)**.
|
|
|
|
Kurzform der Architektur-Empfehlungen (Detail siehe Sub-Plan):
|
|
- **Anonymisierung**: Pseudonym-Hash + Tier-Display-Name ("Wachsame Eule #4528")
|
|
- **Voting**: Auth-required, aber Reactions statt simpler Votes (👍 ❤️ 🚀 🤔)
|
|
- **Inline-Hook**: Auto-Inject in `ModuleShell`-Header (opt-out per Modul) + Floating-Pille als Backup
|
|
- **Public-Surface**: Eigenes `community`-Modul + Mirror-Route außerhalb (app)/
|
|
- **Onboarding-Wish ab jetzt PUBLIC** mit Disclosure-Step
|
|
|
|
Alter Phase-2-Inhalt (Admin-Hub etc.) ist in den Sub-Plan migriert.
|
|
|
|
---
|
|
|
|
## Phase 3 — Future Categories *(Backlog, Schema-Slot offenhalten)*
|
|
|
|
Damit das Schema nicht noch mal bricht, halten wir Platz für:
|
|
|
|
- `'nps'` — Score 0-10 + optional Kommentar; nach 30 Tagen aktiver Nutzung
|
|
einmalig getriggert. Brauche dafür eine optionale `score INT`-Spalte
|
|
auf `user_feedback`.
|
|
- `'churn-feedback'` — wenn jemand den Account löscht: warum?
|
|
(Pflicht-Modal vor Final-Delete.)
|
|
- `'support-request'` — 1:1-Hilfe statt öffentlicher Bug.
|
|
- `'praise'` — schon in Phase 0 mitgenommen.
|
|
|
|
→ NICHT jetzt bauen, nur als Roadmap-Marker.
|
|
|
|
---
|
|
|
|
## Phase 4 — Local-First *(deferred)*
|
|
|
|
Aktuell ist `@mana/feedback` Server-Direct-POST. Local-First lohnt sich erst,
|
|
wenn:
|
|
- Leute Feedback offline schreiben sollen (Mobile-Use-Case)
|
|
- `feedbackText` verschlüsselt im Sync laufen soll (privacy-relevant?)
|
|
|
|
Bis dahin: Status quo. Dexie-Tabelle `feedbackEntries` + `crypto/registry.ts`
|
|
+ mana-sync field-level LWW wäre der Migrations-Plan.
|
|
|
|
---
|
|
|
|
## Bekannte Drift, die wir hier NICHT angehen
|
|
|
|
- **`apps/mana/apps/web/src/lib/api/feedback.ts`** schickt an
|
|
`getManaAuthUrl()/api/v1/feedback`. Aber `mana-analytics` (3064) ist die
|
|
echte Heimat. Funktioniert nur, wenn mana-auth proxiet. Eigener Fix-PR.
|
|
- **Feedback fehlt in `packages/shared-branding/src/mana-apps.ts`** —
|
|
ist nur in `apps/web/src/lib/app-registry/apps.ts`. Konsistent oder
|
|
bewusst? Nicht in dieser Plan-Iteration.
|
|
|
|
---
|
|
|
|
## Reihenfolge & Commits
|
|
|
|
1. **Commit 1 (Phase 0)**: `refactor(feedback): align package + DB enums, add 'praise' category`
|
|
2. **Commit 2 (Phase 1)**: `feat(onboarding): add wish step, route to feedback service`
|
|
3. **Phase 2/3/4**: separate Sprints, separate Plan-Updates.
|