feat(admin, i18n): enhance reports, draws, config, and player workflows
This commit is contained in:
@@ -9,6 +9,7 @@ import { toast } from "sonner";
|
||||
|
||||
import {
|
||||
getAdminDraw,
|
||||
getAdminDrawFinanceSummary,
|
||||
postAdminCancelDraw,
|
||||
postAdminManualCloseDraw,
|
||||
postAdminReopenDraw,
|
||||
@@ -22,11 +23,13 @@ import { AdminLoadingState } from "@/components/admin/admin-loading-state";
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { useConfirmAction } from "@/hooks/use-confirm-action";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
|
||||
import type { AdminDrawShowData } from "@/types/api/admin-draws";
|
||||
import { canManageDrawResults } from "@/lib/draw-access";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { formatAdminMinorUnits } from "@/lib/money";
|
||||
|
||||
import { drawStatusLabel, hallPreviewDiffersFromDbStatus } from "./draw-display";
|
||||
import { DrawStatusBadge } from "./draw-status-badge";
|
||||
@@ -80,6 +83,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [acting, setActing] = useState<string | null>(null);
|
||||
const [financeSummary, setFinanceSummary] = useState<AdminDrawFinanceSummaryData | null>(null);
|
||||
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
|
||||
|
||||
const load = useCallback(async () => {
|
||||
@@ -91,9 +95,20 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
setLoading(true);
|
||||
setError(null);
|
||||
try {
|
||||
setData(await getAdminDraw(idNum));
|
||||
const draw = await getAdminDraw(idNum);
|
||||
setData(draw);
|
||||
if (draw.capabilities?.can_view_draw_finance !== false) {
|
||||
try {
|
||||
setFinanceSummary(await getAdminDrawFinanceSummary(idNum));
|
||||
} catch {
|
||||
setFinanceSummary(null);
|
||||
}
|
||||
} else {
|
||||
setFinanceSummary(null);
|
||||
}
|
||||
} catch (e) {
|
||||
setData(null);
|
||||
setFinanceSummary(null);
|
||||
setError(e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
@@ -225,6 +240,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
const batch = data.result_batch_counts;
|
||||
const pendingReview = batch.pending_review ?? 0;
|
||||
const totalBatches = batch.total ?? batch.published;
|
||||
const financeCurrency = financeSummary?.currency_code ?? "NPR";
|
||||
const hasResultActivity =
|
||||
(canManageDraw && (totalBatches > 0 || pendingReview > 0)) || batch.published > 0;
|
||||
const showActions =
|
||||
@@ -236,6 +252,14 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<div className="mb-3">
|
||||
<Link
|
||||
href="/admin/draws"
|
||||
className="text-sm font-medium text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
{t("backToList")}
|
||||
</Link>
|
||||
</div>
|
||||
<CardTitle className="font-mono text-xl tracking-tight">{data.draw_no}</CardTitle>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
{t("detailSubtitle", {
|
||||
@@ -263,6 +287,39 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
</CardHeader>
|
||||
|
||||
<CardContent className="space-y-6 border-t pt-6">
|
||||
<section className="space-y-3">
|
||||
<h3 className="text-sm font-medium">{t("overviewTitle")}</h3>
|
||||
<div className="grid gap-3 sm:grid-cols-3">
|
||||
<div className="rounded-lg border bg-muted/20 px-3 py-2.5">
|
||||
<p className="text-xs font-medium text-muted-foreground">{t("overviewBetTotal")}</p>
|
||||
<p className="mt-1 font-mono text-sm tabular-nums">
|
||||
{formatAdminMinorUnits(
|
||||
financeSummary?.total_bet_minor ?? data.total_bet_minor ?? 0,
|
||||
financeCurrency,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted/20 px-3 py-2.5">
|
||||
<p className="text-xs font-medium text-muted-foreground">{t("overviewPayoutTotal")}</p>
|
||||
<p className="mt-1 font-mono text-sm tabular-nums">
|
||||
{formatAdminMinorUnits(
|
||||
financeSummary?.total_payout_minor ?? data.total_payout_minor ?? 0,
|
||||
financeCurrency,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
<div className="rounded-lg border bg-muted/20 px-3 py-2.5">
|
||||
<p className="text-xs font-medium text-muted-foreground">{t("overviewProfitLoss")}</p>
|
||||
<p className="mt-1 font-mono text-sm tabular-nums">
|
||||
{formatAdminMinorUnits(
|
||||
financeSummary?.approx_house_gross_minor ?? data.profit_loss_minor ?? 0,
|
||||
financeCurrency,
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section className="space-y-3">
|
||||
<h3 className="text-sm font-medium">{t("scheduleTitle")}</h3>
|
||||
<ScheduleTimeline steps={scheduleSteps} />
|
||||
@@ -307,6 +364,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("noResultBatchesYet")}
|
||||
<span className="ml-1">{t("reviewQueueHint")}</span>
|
||||
{canManageDraw ? (
|
||||
<>
|
||||
{" "}
|
||||
|
||||
Reference in New Issue
Block a user