"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 (
{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" : "最近更新"}
))}
)}
);
}