Feedback System
Overview
MysticX includes a two-channel feedback system that lets users share their experience, report bugs, and suggest improvements. Feedback is collected through a floating widget on all main pages and a dedicated full-page form, then managed by admins in the admin panel.
Feedback Channels
Floating Widget
A lightweight feedback widget that appears on all (main) layout pages after a 5-second delay.
Flow:
- User clicks the floating "Feedback" button (bottom-right corner)
- A compact panel slides up with the question "How's your experience?"
- User picks a mood emoji (Terrible → Bad → Okay → Good → Amazing)
- A textarea expands for optional text input (max 500 characters)
- User clicks "Send Feedback" to submit
Behavior:
- Hidden on
/feedbackand/auth/*paths to avoid redundancy - After successful submission, the button hides for 7 days (stored in
localStorageunderfeedback_widget_submitted_at) - On mobile: icon-only trigger button; on desktop: icon + localized "Feedback" label
- Click outside or Escape key closes the panel
- Non-logged-in users submit with a placeholder email; logged-in users auto-fill their session email
- Captures
window.location.pathnamefor pageUrl tracking
Files:
components/FeedbackWidget/FeedbackWidget.tsx— Main widget componentcomponents/FeedbackWidget/MoodPicker.tsx— 5-mood emoji picker (supportssize="sm"for widget,size="lg"for page)components/FeedbackWidget/FeedbackWidget.css— Animationsstores/feedbackWidgetStore.ts— Zustand store (isOpen,open,close)
Feedback Page
A full-page form at /feedback with richer input options.
Features:
- Email field (pre-filled from session)
- Mood picker (optional, large variant) — reuses
MoodPickerwithsize="lg" - Content textarea (required, max 2,000 characters)
- Screenshot upload (up to 5 images, JPEG/PNG/WebP, max 5MB each)
- FAQ section with common questions
- Hero section with animated title and gold shimmer effect
Files:
app/[locale]/(main)/feedback/page.tsx— Server component with i18n metadataapp/[locale]/(main)/feedback/_components/FeedbackPageContent.tsx— Hero + layoutapp/[locale]/(main)/feedback/_components/FeedbackForm.tsx— Form componentapp/[locale]/(main)/feedback/_components/FeedbackFAQ.tsx— FAQ accordion
Data Model
enum FeedbackStatus {
PENDING
REVIEWED
RESOLVED
}
enum FeedbackMood {
AMAZING
GOOD
OKAY
BAD
TERRIBLE
}
model UserFeedback {
id String @id @default(uuid())
userId String?
user User? @relation(...)
email String
content String? @db.Text
screenshotUrls Json @default("[]") // string[], max 5
status FeedbackStatus @default(PENDING)
adminNote String? @db.Text
mood FeedbackMood?
source String @default("page") // "widget" | "page"
pageUrl String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}Key fields:
mood— Nullable. Set by widget submissions (required); optional on page submissions.source—"widget"or"page". Identifies the submission channel.pageUrl— The page the user was on when submitting. Captured automatically.content— Optional for widget submissions (mood-only is valid); required for page submissions.
API Endpoints
POST /api/v1/feedback
Submits a feedback entry. Auth is optional (guests can submit).
Request body:
{
"email": "user@example.com",
"content": "Great app!",
"screenshotUrls": [],
"mood": "AMAZING",
"source": "widget",
"pageUrl": "/en/love-tarot"
}Validation rules:
| Field | Widget | Page |
|---|---|---|
email | Required (valid format) | Required (valid format) |
mood | Required | Optional |
content | Optional | Required |
screenshotUrls | Not supported | Optional (max 5) |
pageUrl | Auto-captured | Auto-captured |
POST /api/v1/feedback/upload
Uploads a screenshot to Cloudflare R2 for use in the page form.
- Accepts:
FormDatawith afilefield - Validates: JPEG/PNG/WebP, max 5MB, verified with
sharp - Returns:
{ data: { url } }with the public R2 URL
Admin Panel
Located at /admin/app/feedback.
Feedback List
- Filterable by status: All, Pending, Reviewed, Resolved
- Searchable by email or content
- Paginated (20 per page)
- Columns: Email, Mood (emoji), Content, Source (badge), Screenshots, Status, Submitted, Actions
Feedback Detail Modal
- Full content view with metadata (email, user, timestamp)
- Source badge and mood display
- Page URL where feedback was submitted
- Screenshot gallery with external link buttons
- Status dropdown (Pending → Reviewed → Resolved)
- Admin note textarea
Admin Actions
updateFeedback(id, { status, adminNote })— Update status and notesdeleteFeedback(id)— Delete feedback and clean up R2 screenshots
i18n
All user-facing text is fully translated in 12 locales using inline useTrans/getTrans dictionaries: en, zh_CN, ja, ko, pt, es, fr, de, ar, zh_TW, id, nl.
The admin panel is English-only per project conventions.
Footer Link
A "Feedback" link is included in the SiteFooter under the "Explore" section, pointing to /feedback.