managarten/docs/plans/feedback-hub.md
Till JS 8804a20a7f feat(community): public anon hub — module + inline + admin + onboarding
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>
2026-04-27 00:02:25 +02:00

9.2 KiB

status owner created
draft till 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:

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'

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.

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.