"use client"; import { useCallback, useEffect, useMemo, useState } from "react"; import { MessageSquare, RefreshCw } from "lucide-react"; import type { UserFeedbackEntry, UserFeedbackPayload } from "@/types/ops"; import { feedbackStatusLabel, feedbackStatusTone, } from "@/components/dashboard/scan-terminal/feedback-status"; import { getSupabaseBrowserClient, hasSupabasePublicEnv, } from "@/lib/supabase/client"; function compactDate(value?: string) { if (!value) return "--"; return value.slice(0, 16).replace("T", " "); } function categoryLabel(value?: string, isEn = false) { const key = String(value || "").toLowerCase(); if (key === "bug") return "Bug"; if (key === "data") return isEn ? "Data" : "数据"; if (key === "idea") return isEn ? "Suggestion" : "建议"; if (key === "payment") return isEn ? "Payment" : "支付"; if (key === "account") return isEn ? "Account" : "账号"; return isEn ? "Other" : "其他"; } function formatRewardPoints(points?: number, isEn = false) { const raw = Number(points || 0); const value = Number.isFinite(raw) ? Math.max(0, raw) : 0; return isEn ? `+${value.toLocaleString()} points` : `+${value.toLocaleString()} 积分`; } function rewardStatusText(status?: string, isEn = false) { const key = String(status || "").toLowerCase(); if (key === "pending") return isEn ? "Reward pending" : "奖励待处理"; if (key === "skipped") return isEn ? "No points awarded" : "未发放积分"; return isEn ? "Feedback reward" : "反馈奖励"; } function renderFeedbackReward(entry: UserFeedbackEntry, isEn: boolean) { const rawPoints = Number(entry.reward_points || 0); const points = Number.isFinite(rawPoints) ? Math.max(0, rawPoints) : 0; const rewardStatus = String(entry.reward_status || "").toLowerCase(); const reason = String(entry.reward_reason || "").trim(); if (points <= 0 && !rewardStatus && !reason) return null; const granted = points > 0 || rewardStatus === "granted"; return (
{rewardStatusText(rewardStatus, isEn)} {points > 0 ? ( {formatRewardPoints(points, isEn)} ) : null}
{reason ? (
{isEn ? "Reason" : "奖励原因"}: {reason}
) : null} {entry.rewarded_at ? (
{compactDate(entry.rewarded_at)}
) : null}
); } export function AccountFeedbackPanel({ isEn, title, description, refreshLabel, }: { isEn: boolean; title: string; description: string; refreshLabel: string; }) { const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [available, setAvailable] = useState(true); const load = useCallback(async (signal?: AbortSignal) => { if (typeof fetch !== "function") return; setLoading(true); setError(""); try { if (!hasSupabasePublicEnv()) { setAvailable(false); setEntries([]); return; } const { data: { session }, } = await getSupabaseBrowserClient().auth.getSession(); if (signal?.aborted) return; const accessToken = String(session?.access_token || "").trim(); if (!accessToken) { setAvailable(false); setEntries([]); return; } const res = await fetch("/api/feedback?limit=10", { cache: "no-store", headers: { Accept: "application/json", Authorization: `Bearer ${accessToken}`, }, signal, }); if (res.status === 401 || res.status === 403) { setAvailable(false); setEntries([]); return; } if (!res.ok) throw new Error(`HTTP ${res.status}`); const payload = (await res.json()) as UserFeedbackPayload; setEntries(Array.isArray(payload.feedback) ? payload.feedback : []); setAvailable(true); } catch (err) { if (signal?.aborted) return; setError(String(err).slice(0, 140)); } finally { if (!signal?.aborted) setLoading(false); } }, []); useEffect(() => { const controller = new AbortController(); void load(controller.signal); return () => controller.abort(); }, [load]); const emptyText = useMemo(() => { if (loading) return isEn ? "Loading feedback..." : "正在加载反馈..."; return isEn ? "No submitted feedback yet." : "暂无已提交反馈。"; }, [isEn, loading]); if (!available) return null; return (

{title}

{description}

{error && (
{isEn ? "Failed to load feedback: " : "反馈加载失败:"}{error}
)} {entries.length === 0 ? (
{emptyText}
) : (
{entries.map((entry) => (
{feedbackStatusLabel(entry.status, isEn)} {categoryLabel(entry.category, isEn)} {compactDate(entry.created_at)}

{entry.message || (isEn ? "Feedback" : "反馈")}

{renderFeedbackReward(entry, isEn)}
{compactDate(entry.updated_at)}
{isEn ? "Updated" : "最近更新"}
))}
)}
); }