"use client"; import { useCallback, useEffect, useState } from "react"; import { apiFetch, ApiError } from "@/lib/api"; import { useToast } from "@/components/toast"; import { DataTable, type Column } from "@/components/DataTable"; import { Button, Card, ConfirmDialog, PageHeader } from "@/components/ui"; import { TrashIcon } from "@/components/icons"; import { toFa } from "@/lib/utils"; interface Feedback { id: number; stars?: number | null; content?: string | null; created_at?: string; user?: { name?: string | null; identifier?: string | null; mobile?: string | null; } | null; } interface Summary { total: number; rated: number; with_comment: number; average_stars: number; } interface AdminResponse { summary: Summary; feedback: { data: Feedback[] }; } function Stars({ value }: { value?: number | null }) { if (!value) return —; return ( {"★".repeat(value)} {"★".repeat(5 - value)} ); } function userName(u: Feedback["user"]): string { return u?.name || u?.identifier || u?.mobile || "—"; } function faDate(value?: string): string { if (!value) return "—"; try { return toFa(new Date(value).toLocaleDateString("fa-IR")); } catch { return "—"; } } export default function AppFeedbackPage() { const toast = useToast(); const [rows, setRows] = useState([]); const [summary, setSummary] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [deleting, setDeleting] = useState(null); const [removing, setRemoving] = useState(false); // Fetch without touching state synchronously (safe to call from an effect). const fetchFeedback = useCallback(() => { return apiFetch("/admin/app-feedback") .then((res) => { setRows(res.feedback?.data ?? []); setSummary(res.summary ?? null); setError(null); }) .catch((e: unknown) => setError(e instanceof ApiError ? e.message : "خطا در دریافت اطلاعات"), ) .finally(() => setLoading(false)); }, []); // Reload triggered by user actions (retry / after delete): show the spinner. const reload = useCallback(() => { setLoading(true); setError(null); fetchFeedback(); }, [fetchFeedback]); useEffect(() => { fetchFeedback(); }, [fetchFeedback]); async function confirmDelete() { if (!deleting) return; setRemoving(true); try { await apiFetch(`/admin/app-feedback/${deleting.id}`, { method: "DELETE" }); toast.success("بازخورد حذف شد."); setDeleting(null); reload(); } catch (err) { toast.error(err instanceof ApiError ? err.message : "خطا در حذف"); } finally { setRemoving(false); } } const columns: Column[] = [ { key: "id", header: "شناسه", render: (r) => toFa(r.id), className: "w-20" }, { key: "user", header: "کاربر", render: (r) => userName(r.user) }, { key: "stars", header: "امتیاز", className: "w-28", render: (r) => , }, { key: "content", header: "ایده / نظر", render: (r) => r.content || —, }, { key: "created_at", header: "تاریخ", className: "w-32", render: (r) => faDate(r.created_at), }, ]; return ( {summary && ( میانگین امتیاز {toFa(summary.average_stars)} ★ کل بازخوردها {toFa(summary.total)} دارای امتیاز {toFa(summary.rated)} دارای نظر {toFa(summary.with_comment)} )} ( setDeleting(row)} aria-label="حذف" className="text-danger" > )} /> setDeleting(null)} /> ); }
میانگین امتیاز
{toFa(summary.average_stars)} ★
کل بازخوردها
{toFa(summary.total)}
دارای امتیاز
{toFa(summary.rated)}
دارای نظر
{toFa(summary.with_comment)}