diff --git a/app/(app)/feedback/page.tsx b/app/(app)/feedback/page.tsx index d497bdfc..231dc5e2 100644 --- a/app/(app)/feedback/page.tsx +++ b/app/(app)/feedback/page.tsx @@ -9,23 +9,31 @@ * Authed visitors don't see the contact-email field — the BFF reads * their account email from the session cookie. Anonymous visitors get * the field and must fill it in so we have a reply path. + * + * Categories are managed in the admin panel and loaded from the BFF. If + * that request fails the form falls back to the original four, so it can + * always be submitted. */ -import { useState } from "react"; +import { useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { ArrowLeft, MessageSquare, Send } from "lucide-react"; import { toast } from "sonner"; -import { feedback as feedbackAPI, SocialError } from "@/lib/social"; +import { + feedback as feedbackAPI, + SocialError, + type FeedbackCategory, +} from "@/lib/social"; import { useAuth } from "@/components/auth/auth-provider"; -type Category = "bug" | "feature" | "general" | "other"; - -const CATEGORIES: { value: Category; label: string; description: string }[] = [ - { value: "general", label: "General feedback", description: "Thoughts, impressions, suggestions — anything else." }, - { value: "bug", label: "Bug report", description: "Something is broken or behaving unexpectedly." }, - { value: "feature", label: "Feature request", description: "An idea for something new on the site." }, - { value: "other", label: "Other", description: "Doesn't fit the above? Use this." }, +// Used only if the categories can't be loaded — the original four, which +// the BFF seeds and keeps accepting unless an admin archives them. +const FALLBACK_CATEGORIES: FeedbackCategory[] = [ + { key: "general", label: "General feedback", description: "Thoughts, impressions, suggestions — anything else." }, + { key: "bug", label: "Bug report", description: "Something is broken or behaving unexpectedly." }, + { key: "feature", label: "Feature request", description: "An idea for something new on the site." }, + { key: "other", label: "Other", description: "Doesn't fit the above? Use this." }, ]; export default function FeedbackPage() { @@ -33,7 +41,28 @@ export default function FeedbackPage() { const { user, loading } = useAuth(); const authed = !loading && !!user; - const [category, setCategory] = useState("general"); + const [categories, setCategories] = + useState(FALLBACK_CATEGORIES); + // Empty until chosen: the first category on the form. + const [picked, setPicked] = useState(""); + const category = categories.some((c) => c.key === picked) + ? picked + : (categories[0]?.key ?? ""); + + useEffect(() => { + let cancelled = false; + feedbackAPI + .categories() + .then((list) => { + if (!cancelled && list?.length) setCategories(list); + }) + .catch(() => { + /* keep the fallback list */ + }); + return () => { + cancelled = true; + }; + }, []); const [message, setMessage] = useState(""); const [contactEmail, setContactEmail] = useState(""); const [submitting, setSubmitting] = useState(false); @@ -72,7 +101,7 @@ export default function FeedbackPage() { } } - const selected = CATEGORIES.find((c) => c.value === category); + const selected = categories.find((c) => c.key === category); return (
@@ -111,12 +140,12 @@ export default function FeedbackPage() {

diff --git a/app/(app)/settings/feedbacks/page.tsx b/app/(app)/settings/feedbacks/page.tsx index ecdd4a58..d5b829a4 100644 --- a/app/(app)/settings/feedbacks/page.tsx +++ b/app/(app)/settings/feedbacks/page.tsx @@ -152,7 +152,9 @@ export default function FeedbacksPage() { function FeedbackCard({ feedback: f }: { feedback: Feedback }) { const status = statusOf(f); const meta = STATUS_META[status]; - const category = CATEGORY_LABEL[f.category] ?? f.category; + // The server's label tracks renames in the admin; the map covers an older + // BFF that doesn't send one yet. + const category = f.category_label || CATEGORY_LABEL[f.category] || f.category; return (

  • diff --git a/lib/social.ts b/lib/social.ts index 70393b0b..52e43413 100644 --- a/lib/social.ts +++ b/lib/social.ts @@ -212,6 +212,8 @@ export type Feedback = { id: string; user_id?: string; category: string; + /** The category's current display name (renames apply to past feedback). */ + category_label?: string; message: string; contact_email?: string; created_at: string; @@ -560,13 +562,25 @@ export const collections = { // ─── Feedback ──────────────────────────────────────────────────────────── +/** One option on the /feedback form, managed from the admin panel. */ +export type FeedbackCategory = { + key: string; + label: string; + description: string; +}; + export const feedback = { + /** The form's categories, in order. Public. */ + categories() { + return call("/feedback/categories"); + }, /** Submit a /feedback page entry. When the viewer is signed in the * BFF captures their user_id automatically via the auth cookie — * the form doesn't ask for it. `contact_email` is required only * for anonymous submissions; the BFF re-enforces that on its end. */ submit(input: { - category: "bug" | "feature" | "general" | "other"; + /** A FeedbackCategory key. */ + category: string; message: string; contact_email?: string; }) {