feat(dashboard, i18n): enhance result batch queue management and translations
Added new translations for "resultBatchQueueScope", "batchPendingDraws", and "batchCurrentDrawPending" in English, Nepali, and Chinese language files. Updated the dashboard console to manage the result batch queue, including a new component for displaying pending review totals and related draw counts. This improves the user interface for monitoring batch statuses and enhances multi-language support.
This commit is contained in:
@@ -42,7 +42,7 @@ import {
|
||||
FinanceStructureChart,
|
||||
HotUsageBars,
|
||||
PayoutPanelSnapshot,
|
||||
ResultBatchProgress,
|
||||
ResultBatchQueueSummary,
|
||||
DashboardPanelCard,
|
||||
SettlementStatusChart,
|
||||
} from "@/modules/dashboard/dashboard-visuals";
|
||||
@@ -53,7 +53,10 @@ import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||
import { formatAdminMinorUnits, getAdminCurrencyDecimalPlaces } from "@/lib/money";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDashboardDrawPanel } from "@/types/api/admin-dashboard";
|
||||
import type {
|
||||
AdminDashboardDrawPanel,
|
||||
AdminDashboardResultBatchQueue,
|
||||
} from "@/types/api/admin-dashboard";
|
||||
import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance";
|
||||
import type { AdminRiskPoolRow } from "@/types/api/admin-risk";
|
||||
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
|
||||
@@ -84,6 +87,17 @@ function drawScopedHref(
|
||||
return drawId != null ? `/admin/draws/${drawId}${suffix}` : fallback;
|
||||
}
|
||||
|
||||
function pendingReviewHref(
|
||||
drawId: number | null,
|
||||
queue: AdminDashboardResultBatchQueue | null,
|
||||
): string {
|
||||
if (queue != null && queue.pending_review_total > 0 && queue.first_pending_draw_id != null) {
|
||||
return `/admin/draws/${queue.first_pending_draw_id}/review`;
|
||||
}
|
||||
|
||||
return drawScopedHref(drawId, "/review");
|
||||
}
|
||||
|
||||
function poolPlayCategory(normalizedNumber: string): HotPlayTab | "other" {
|
||||
const raw = normalizedNumber.trim();
|
||||
const digits = raw.replace(/\D/g, "");
|
||||
@@ -134,7 +148,9 @@ export function DashboardConsole(): ReactElement {
|
||||
const [drawPanel, setDrawPanel] = useState<AdminDashboardDrawPanel | null>(null);
|
||||
const [finance, setFinance] = useState<AdminDrawFinanceSummaryData | null>(null);
|
||||
const [capabilities, setCapabilities] = useState<{ draw_finance_risk: boolean; wallet_transfer_view: boolean } | null>(null);
|
||||
const [pendingReview, setPendingReview] = useState<number | null>(null);
|
||||
const [resultBatchQueue, setResultBatchQueue] = useState<AdminDashboardResultBatchQueue | null>(
|
||||
null,
|
||||
);
|
||||
const [riskLocked, setRiskLocked] = useState(0);
|
||||
const [riskCap, setRiskCap] = useState(0);
|
||||
const [hotPoolSample, setHotPoolSample] = useState<AdminRiskPoolRow[]>([]);
|
||||
@@ -173,7 +189,7 @@ export function DashboardConsole(): ReactElement {
|
||||
setFinance(null);
|
||||
setCapabilities(null);
|
||||
setDrawPanel(null);
|
||||
setPendingReview(null);
|
||||
setResultBatchQueue(null);
|
||||
setDrawId(null);
|
||||
setRiskLocked(0);
|
||||
setRiskCap(0);
|
||||
@@ -194,7 +210,7 @@ export function DashboardConsole(): ReactElement {
|
||||
}
|
||||
if (d.draw != null) {
|
||||
setDrawPanel(d.draw);
|
||||
setPendingReview(d.draw.result_batch_counts.pending_review);
|
||||
setResultBatchQueue(d.result_batch_queue);
|
||||
}
|
||||
if (d.risk != null) {
|
||||
setRiskLocked(d.risk.locked_amount);
|
||||
@@ -225,6 +241,9 @@ export function DashboardConsole(): ReactElement {
|
||||
|
||||
const hotRows = useMemo(() => topPoolsForTab(hotPoolSample, hotTab), [hotPoolSample, hotTab]);
|
||||
|
||||
const pendingReviewTotal = resultBatchQueue?.pending_review_total ?? 0;
|
||||
const currentDrawPending = drawPanel?.result_batch_counts.pending_review ?? 0;
|
||||
|
||||
const analytics = useDashboardAnalytics({ enabled: canFinance, playOptions });
|
||||
const showAnalytics = canFinance;
|
||||
|
||||
@@ -288,21 +307,27 @@ export function DashboardConsole(): ReactElement {
|
||||
/>
|
||||
<div className="grid min-w-0 grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4">
|
||||
<DashboardPanelCard
|
||||
href={drawScopedHref(drawId, "/review")}
|
||||
href={pendingReviewHref(drawId, resultBatchQueue)}
|
||||
title={t("pendingReviewResults")}
|
||||
value={pendingReview ?? "—"}
|
||||
subtitle={t("resultBatches")}
|
||||
value={resultBatchQueue != null ? pendingReviewTotal : "—"}
|
||||
subtitle={t("resultBatchQueueScope")}
|
||||
actionLabel={
|
||||
(pendingReview ?? 0) > 0
|
||||
pendingReviewTotal > 0
|
||||
? t("actions.reviewNow", { ns: "common" })
|
||||
: t("drawDetails")
|
||||
}
|
||||
icon={<ClipboardList className="size-5" aria-hidden />}
|
||||
accent={(pendingReview ?? 0) > 0 ? "destructive" : "muted"}
|
||||
highlight={(pendingReview ?? 0) > 0}
|
||||
accent={pendingReviewTotal > 0 ? "destructive" : "muted"}
|
||||
highlight={pendingReviewTotal > 0}
|
||||
loading={loading}
|
||||
>
|
||||
{drawPanel ? <ResultBatchProgress draw={drawPanel} compact /> : null}
|
||||
{resultBatchQueue != null ? (
|
||||
<ResultBatchQueueSummary
|
||||
queue={resultBatchQueue}
|
||||
currentDrawPending={currentDrawPending}
|
||||
compact
|
||||
/>
|
||||
) : null}
|
||||
</DashboardPanelCard>
|
||||
|
||||
<DashboardPanelCard
|
||||
|
||||
@@ -44,6 +44,7 @@ import type { AdminDrawFinanceSummaryData } from "@/types/api/admin-draw-finance
|
||||
import type { AdminRiskPoolRow } from "@/types/api/admin-risk";
|
||||
import type {
|
||||
AdminDashboardDrawPanel,
|
||||
AdminDashboardResultBatchQueue,
|
||||
AdminDashboardSoldOutBuckets,
|
||||
} from "@/types/api/admin-dashboard";
|
||||
|
||||
@@ -973,6 +974,52 @@ export function ResultBatchProgress({
|
||||
);
|
||||
}
|
||||
|
||||
export function ResultBatchQueueSummary({
|
||||
queue,
|
||||
currentDrawPending,
|
||||
compact = false,
|
||||
}: {
|
||||
queue: AdminDashboardResultBatchQueue;
|
||||
currentDrawPending: number;
|
||||
compact?: boolean;
|
||||
}): ReactElement {
|
||||
const { t } = useTranslation("dashboard");
|
||||
const { pending_review_total, pending_draw_count } = queue;
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-3 gap-2 text-center">
|
||||
<div className="rounded-lg bg-amber-500/8 px-2 py-2 ring-1 ring-amber-500/15">
|
||||
<p
|
||||
className={cn(
|
||||
"font-bold tabular-nums text-amber-700 dark:text-amber-400",
|
||||
compact ? "text-lg" : "text-2xl",
|
||||
)}
|
||||
>
|
||||
{pending_review_total}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-muted-foreground">{t("batchPending")}</p>
|
||||
</div>
|
||||
<div className="rounded-lg bg-sky-500/8 px-2 py-2 ring-1 ring-sky-500/15">
|
||||
<p
|
||||
className={cn(
|
||||
"font-bold tabular-nums text-sky-800 dark:text-sky-300",
|
||||
compact ? "text-lg" : "text-2xl",
|
||||
)}
|
||||
>
|
||||
{pending_draw_count}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-muted-foreground">{t("batchPendingDraws")}</p>
|
||||
</div>
|
||||
<div className="rounded-lg bg-muted/50 px-2 py-2 ring-1 ring-border/60">
|
||||
<p className={cn("font-bold tabular-nums text-foreground", compact ? "text-lg" : "text-2xl")}>
|
||||
{currentDrawPending}
|
||||
</p>
|
||||
<p className="mt-0.5 text-[10px] text-muted-foreground">{t("batchCurrentDrawPending")}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function SettlementStatusChart({
|
||||
finance,
|
||||
}: {
|
||||
|
||||
Reference in New Issue
Block a user