"use client"; import { Dices, Rocket, Trash2 } from "lucide-react"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAsyncEffect } from "@/hooks/use-async-effect"; import { useTranslationRef } from "@/hooks/use-translation-ref"; import { toast } from "sonner"; import { deleteAdminPendingResultBatch, getAdminDrawResultBatches, postAdminCreateManualResultBatch, } from "@/api/admin-draws"; import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state"; import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { useConfirmAction } from "@/hooks/use-confirm-action"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; import { useAdminProfile } from "@/stores/admin-session"; import { LotteryApiBizError } from "@/types/api/errors"; import type { AdminDrawBatchesData } from "@/types/api/admin-draws"; import { drawStatusLabel } from "./draw-display"; import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd"; import { DrawStatusBadge } from "./draw-status-badge"; const RESULT_SLOTS = [ { prize_type: "first", prize_index: 0, label: "resultSlots.first" }, { prize_type: "second", prize_index: 0, label: "resultSlots.second" }, { prize_type: "third", prize_index: 0, label: "resultSlots.third" }, ...Array.from({ length: 10 }, (_, i) => ({ prize_type: "starter", prize_index: i, label: `resultSlots.starter`, labelIndex: i + 1, })), ...Array.from({ length: 10 }, (_, i) => ({ prize_type: "consolation", prize_index: i, label: `resultSlots.consolation`, labelIndex: i + 1, })), ] as const; function randomDrawNumber4d(): string { return String(Math.floor(Math.random() * 10_000)).padStart(4, "0"); } export function DrawReviewConsole({ drawId }: { drawId: string }) { const { t } = useTranslation(["draws", "common"]); const tRef = useTranslationRef(["draws", "common"]); const profile = useAdminProfile(); const canManageDraw = adminHasAnyPermission(profile?.permissions, [ PRD_DRAW_RESULT_MANAGE, ]); const idNum = Number(drawId); const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [savingManual, setSavingManual] = useState(false); const [discardingBatchId, setDiscardingBatchId] = useState(null); const { request: requestConfirm, ConfirmDialog } = useConfirmAction(); const [manualNumbers, setManualNumbers] = useState( () => RESULT_SLOTS.map(() => ""), ); const load = useCallback(async () => { if (!Number.isFinite(idNum)) { setError(tRef.current("invalidDrawId")); setLoading(false); return; } setLoading(true); setError(null); try { setData(await getAdminDrawResultBatches(idNum)); } catch (e) { setData(null); setError(e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" })); } finally { setLoading(false); } }, [idNum]); useAsyncEffect(() => { void load(); }, [idNum]); const pending = useMemo(() => data?.batches.filter((b) => b.status === "pending_review") ?? [], [ data, ]); function fillRandomManualNumbers(): void { setManualNumbers(RESULT_SLOTS.map(() => randomDrawNumber4d())); } async function discardPendingBatch(batchId: number): Promise { if (!Number.isFinite(idNum)) return; setDiscardingBatchId(batchId); try { await deleteAdminPendingResultBatch(idNum, batchId); toast.success(t("discardPendingBatchSuccess")); await load(); } catch (e) { toast.error( e instanceof LotteryApiBizError ? e.message : t("discardPendingBatchFailed"), ); } finally { setDiscardingBatchId(null); } } async function saveManualDraft(): Promise { if (!Number.isFinite(idNum)) return; const invalid = manualNumbers.some((n) => !/^[0-9]{4}$/.test(n)); if (invalid) { toast.error(t("enter23Numbers")); return; } setSavingManual(true); try { const res = await postAdminCreateManualResultBatch(idNum, { items: RESULT_SLOTS.map((slot, i) => ({ prize_type: slot.prize_type, prize_index: slot.prize_index, number_4d: manualNumbers[i], })), }); toast.success(t("draftSaved", { version: res.batch.result_version })); setManualNumbers(RESULT_SLOTS.map(() => "")); await load(); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("saveFailed")); } finally { setSavingManual(false); } } if (loading && !data) { return ; } if (error) { return

{error}

; } if (!data) { return ; } return (
{t("manualResultEntry")}

{t("currentStatusLabel")} ยท {t("currentStatusDraftHint")}

{RESULT_SLOTS.map((slot, i) => ( ))}
{t("pendingBatches")} {pending.length === 0 ? ( ) : ( {t("batchId")} {t("version", { version: "" }).replace(" v", "").trim()} {t("numberCount")} {t("table.actions", { ns: "common" })} {pending.map((b) => ( {b.id} v{b.result_version} {b.items.length} {canManageDraw ? ( requestConfirm({ title: t("confirm.discardPendingBatchTitle"), description: t("confirm.discardPendingBatchDescription"), confirmVariant: "destructive", onConfirm: () => discardPendingBatch(b.id), }), }, ]} /> ) : ( {t("noPublishPermission")} )} ))}
)}
); }