Projects BibleWeb Navigation UX Feedback system
Done

Feedback system

Area: Navigation UX

1. Context

During beta and early access, direct user feedback is critical for prioritisation. This feature provides a lightweight, always-accessible feedback drawer that lets users report bugs, request features, or ask questions without leaving the app. It requires no account and adds zero friction.

2. Functional

  • A right-side drawer (width: 380px) containing a free-text textarea and a submit button.
  • The message field is required; the submit button is disabled when the textarea is empty or a submission is in-flight.
  • On success: a checkmark confirmation screen is shown, and the drawer auto-closes after 2 seconds.
  • On failure: an inline error message is shown; the drawer stays open so the user can retry.
  • The drawer form resets (message cleared, states reset) whenever the drawer is opened.
  • Feedback metadata collected: the free-text message, the current url (window.location.href), and the userAgent (truncated to 200 chars server-side).

API: POST /api/feedback

  • Accepts JSON body: { message, category, url, userAgent }.
  • Valid categories: bug, feature, question, other. Category is required and validated server-side (note: the drawer currently sends without a category — this is a mismatch to resolve).
  • Message max length: 5000 characters.
  • Rate limited: 10 requests per minute per IP. Returns 429 with Retry-After header when exceeded.
  • Persists each entry as a JSON line appended to feedback.log in the app root (fire-and-forget, non-fatal if the write fails).
  • Also emits a structured console.log('[feedback]', ...) for server-side visibility in logs.
  • Returns { ok: true } with X-RateLimit-Remaining header on success.

3. UX & Design

  • Drawer slides in from the right; triggered from somewhere in the app layout (e.g., a button in the nav or floating action).
  • Header: "Feedback" title + close button (X icon).
  • Textarea: 6 rows, resizable vertically, placeholder text from i18n.
  • Submit button: full-width, disabled state at 45% opacity, spinner SVG during submission.
  • Success state: centered checkmark icon (gold color --color-jesus-words) + success text.
  • Error state: red inline message text.
  • All colors driven by CSS custom properties for theme compatibility.

4. Technical

Component: apps/web/src/lib/components/layout/FeedbackDrawer.svelte

  • Props: open: boolean, onclose: () => void
  • Uses the shared Drawer component with variant="right".
  • i18n keys: feedback.title, feedback.message_placeholder, feedback.submit, feedback.success, feedback.error.

API Route: apps/web/src/routes/api/feedback/+server.ts

  • Uses $lib/server/rate-limit.js — in-memory rate limiter.
  • Log path: join(process.cwd(), 'feedback.log') — flat newline-delimited JSON file.
  • The category field is validated against ['bug', 'feature', 'question', 'other'] but the drawer currently does not send one. This will cause a 400 error until the drawer is updated to include a category selector or the server-side requirement is relaxed.

5. Status

Implemented, but there is a known mismatch: the drawer POSTs without a category field, while the API requires it. Either add a category selector to the drawer or make the field optional server-side before this feature is fully functional end-to-end.