"use client"; /** * /settings/feedbacks — the signed-in user's own feedback history. * * Lists every submission they've made (newest first) with its current * status — Submitted → Acknowledged → Resolved — and, when the team has * written back, the response inline. Read-only: submitting new feedback * still happens on /feedback. * * Anonymous visitors are prompted to sign in and bounced home if they * dismiss, mirroring /settings/profile — the underlying endpoint is * auth-only. */ import { useCallback, useEffect, useState } from "react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { CheckCircle2, Clock, Eye, MessageSquarePlus } from "lucide-react"; import { feedback as feedbackAPI, SocialError, type Feedback } from "@/lib/social"; import { useAuth } from "@/components/auth/auth-provider"; import { useAuthGate } from "@/components/auth/auth-gate"; import { SettingsShell } from "@/components/settings/settings-ui"; import { cn } from "@/lib/utils"; const CATEGORY_LABEL: Record = { bug: "Bug report", feature: "Feature request", general: "General feedback", other: "Other", }; type Status = "resolved" | "acknowledged" | "open"; function statusOf(f: Feedback): Status { if (f.resolved_at) return "resolved"; if (f.acknowledged_at) return "acknowledged"; return "open"; } const STATUS_META: Record< Status, { label: string; icon: React.ReactNode; className: string } > = { resolved: { label: "Resolved", icon: , className: "bg-emerald-500/10 text-emerald-700 dark:text-emerald-300", }, acknowledged: { label: "Acknowledged", icon: , className: "bg-amber-500/10 text-amber-700 dark:text-amber-300", }, open: { label: "Submitted", icon: , className: "bg-muted text-muted-foreground", }, }; function formatDate(iso: string): string { try { return new Date(iso).toLocaleDateString(undefined, { year: "numeric", month: "short", day: "numeric", }); } catch { return ""; } } export default function FeedbacksPage() { const { user, loading } = useAuth(); const { requireAuth } = useAuthGate(); const router = useRouter(); const [items, setItems] = useState(null); const [error, setError] = useState(null); // Same auth gate as /settings/profile: prompt anonymous visitors and // bounce them home if they dismiss. useEffect(() => { if (loading || user) return; let cancelled = false; (async () => { const ok = await requireAuth(); if (!ok && !cancelled) router.replace("/"); })(); return () => { cancelled = true; }; }, [loading, user, requireAuth, router]); const load = useCallback(async () => { setError(null); try { setItems(await feedbackAPI.mine()); } catch (err) { setError( err instanceof SocialError ? err.message : "Could not load your feedback.", ); } }, []); useEffect(() => { if (loading || !user) return; void load(); }, [loading, user, load]); return ( {loading || !user ? (

{loading ? "Loading…" : "Sign in to see your feedback."}

) : error ? (

{error}

) : items === null ? (

Loading…

) : items.length === 0 ? ( ) : (
    {items.map((f) => ( ))}
)}
); } function FeedbackCard({ feedback: f }: { feedback: Feedback }) { const status = statusOf(f); const meta = STATUS_META[status]; // 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 (
  • {meta.icon} {meta.label} {category} {formatDate(f.created_at)}

    {f.message}

    {f.reply_message ? (

    Response from the team {f.replied_at ? ( {" · "} {formatDate(f.replied_at)} ) : null}

    {f.reply_message}

    ) : null}
  • ); } function EmptyState() { return (

    You haven’t sent any feedback yet.

    Send feedback
    ); }