feat(agents, i18n): enhance agent management and settlement features with new translations and UI updates
Added new translations for agent management and settlement features in English, Nepali, and Chinese, improving multi-language support. Updated the agents console to reflect changes in funding modes and player details, enhancing user experience. Refactored the admin permission gate to include new logic for handling bound line agents, ensuring better permission management. Additionally, streamlined the UI for agent-related pages and improved navigation to the settlement center, consolidating related functionalities for better accessibility.
This commit is contained in:
@@ -17,12 +17,13 @@ import {
|
||||
import { postAdminRunDrawSettlement } from "@/api/admin-settlement";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||
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 { AdminDrawShowData } from "@/types/api/admin-draws";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { canManageDrawResults } from "@/lib/draw-access";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
@@ -68,7 +69,7 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
const tRef = useTranslationRef(["draws", "common"]);
|
||||
const idNum = Number(drawId);
|
||||
const profile = useAdminProfile();
|
||||
const canManageDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_RESULT_MANAGE]);
|
||||
const canManageDraw = canManageDrawResults(profile?.permissions);
|
||||
const canReopenDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_REOPEN_MANAGE]);
|
||||
const canRunSettlement = adminHasAnyPermission(profile?.permissions, [
|
||||
PRD_PAYOUT_MANAGE,
|
||||
@@ -213,12 +214,18 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
return <AdminLoadingState minHeight="6rem" className="py-6" />;
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return <p className="text-sm text-destructive">{error ?? t("states.noData", { ns: "common" })}</p>;
|
||||
if (error) {
|
||||
return <p className="text-sm text-destructive">{error}</p>;
|
||||
}
|
||||
if (!data) {
|
||||
return <AdminNoResourceState />;
|
||||
}
|
||||
|
||||
const batch = data.result_batch_counts;
|
||||
const hasResultActivity = batch.total > 0 || batch.pending_review > 0 || batch.published > 0;
|
||||
const pendingReview = batch.pending_review ?? 0;
|
||||
const totalBatches = batch.total ?? batch.published;
|
||||
const hasResultActivity =
|
||||
(canManageDraw && (totalBatches > 0 || pendingReview > 0)) || batch.published > 0;
|
||||
const showActions =
|
||||
availableActions.length > 0 && (canManageDraw || canReopenDraw || canRunSettlement);
|
||||
|
||||
@@ -264,21 +271,25 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
<h3 className="text-sm font-medium">{t("resultBatchesTitle")}</h3>
|
||||
{hasResultActivity ? (
|
||||
<div className="flex flex-wrap items-center gap-2 text-sm">
|
||||
<span className="rounded-md bg-muted px-2.5 py-1">
|
||||
{t("batchSummaryTotal", { count: batch.total })}
|
||||
</span>
|
||||
{batch.pending_review > 0 ? (
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className="rounded-md bg-amber-500/15 px-2.5 py-1 font-medium text-amber-800 dark:text-amber-200"
|
||||
>
|
||||
{t("batchSummaryPending", { count: batch.pending_review })}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="rounded-md bg-muted px-2.5 py-1 text-muted-foreground">
|
||||
{t("batchSummaryPending", { count: 0 })}
|
||||
{canManageDraw ? (
|
||||
<span className="rounded-md bg-muted px-2.5 py-1">
|
||||
{t("batchSummaryTotal", { count: totalBatches })}
|
||||
</span>
|
||||
)}
|
||||
) : null}
|
||||
{canManageDraw ? (
|
||||
pendingReview > 0 ? (
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className="rounded-md bg-amber-500/15 px-2.5 py-1 font-medium text-amber-800 dark:text-amber-200"
|
||||
>
|
||||
{t("batchSummaryPending", { count: pendingReview })}
|
||||
</Link>
|
||||
) : (
|
||||
<span className="rounded-md bg-muted px-2.5 py-1 text-muted-foreground">
|
||||
{t("batchSummaryPending", { count: 0 })}
|
||||
</span>
|
||||
)
|
||||
) : null}
|
||||
{batch.published > 0 ? (
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/results`}
|
||||
@@ -294,13 +305,18 @@ export function DrawDetailConsole({ drawId }: { drawId: string }) {
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{t("noResultBatchesYet")}{" "}
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className="font-medium text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
{t("goToReviewTab")}
|
||||
</Link>
|
||||
{t("noResultBatchesYet")}
|
||||
{canManageDraw ? (
|
||||
<>
|
||||
{" "}
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className="font-medium text-primary underline-offset-4 hover:underline"
|
||||
>
|
||||
{t("goToReviewTab")}
|
||||
</Link>
|
||||
</>
|
||||
) : null}
|
||||
</p>
|
||||
)}
|
||||
</section>
|
||||
|
||||
@@ -13,6 +13,7 @@ import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
|
||||
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
|
||||
import { DrawStatusBadge } from "@/modules/draws/draw-status-badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import {
|
||||
Table,
|
||||
@@ -96,8 +97,11 @@ export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactE
|
||||
return <AdminLoadingState minHeight="6rem" className="py-6" />;
|
||||
}
|
||||
|
||||
if (err || !data) {
|
||||
return <p className="text-destructive text-sm">{err ?? t("states.noData", { ns: "common" })}</p>;
|
||||
if (err) {
|
||||
return <p className="text-destructive text-sm">{err}</p>;
|
||||
}
|
||||
if (!data) {
|
||||
return <AdminNoResourceState />;
|
||||
}
|
||||
|
||||
const currencyCode = data.currency_code ?? "NPR";
|
||||
@@ -180,7 +184,7 @@ export function DrawFinanceConsole({ drawId }: { drawId: string }): React.ReactE
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{data.settlement_batches.length === 0 ? (
|
||||
<p className="text-muted-foreground text-sm">{t("noSettlementBatches")}</p>
|
||||
<AdminNoResourceState className="py-4" />
|
||||
) : (
|
||||
<div className="overflow-x-auto rounded-md border">
|
||||
<div className="admin-table-toolbar">
|
||||
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import { Button, buttonVariants } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import {
|
||||
Table,
|
||||
@@ -119,8 +120,11 @@ export function DrawPublishConsole({ drawId, batchId }: { drawId: string; batchI
|
||||
return <AdminLoadingState minHeight="6rem" className="py-6" />;
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return <p className="text-sm text-destructive">{error ?? t("states.noData", { ns: "common" })}</p>;
|
||||
if (error) {
|
||||
return <p className="text-sm text-destructive">{error}</p>;
|
||||
}
|
||||
if (!data) {
|
||||
return <AdminNoResourceState />;
|
||||
}
|
||||
|
||||
if (!batch) {
|
||||
|
||||
@@ -9,6 +9,7 @@ import { useTranslationRef } from "@/hooks/use-translation-ref";
|
||||
import { getAdminDrawResultBatches } from "@/api/admin-draws";
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import {
|
||||
Table,
|
||||
@@ -20,22 +21,19 @@ import {
|
||||
} from "@/components/ui/table";
|
||||
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { canManageDrawResults } from "@/lib/draw-access";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawBatchRow, AdminDrawBatchesData } from "@/types/api/admin-draws";
|
||||
|
||||
import { drawPrizeTypeLabel } from "./draw-display";
|
||||
import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
|
||||
import { DrawStatusBadge } from "./draw-status-badge";
|
||||
|
||||
export function DrawResultsConsole({ 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 canManageDraw = canManageDrawResults(profile?.permissions);
|
||||
const idNum = Number(drawId);
|
||||
const [data, setData] = useState<AdminDrawBatchesData | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
@@ -67,8 +65,11 @@ export function DrawResultsConsole({ drawId }: { drawId: string }) {
|
||||
return <AdminLoadingState minHeight="6rem" className="py-6" />;
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return <p className="text-sm text-destructive">{error ?? t("states.noData", { ns: "common" })}</p>;
|
||||
if (error) {
|
||||
return <p className="text-sm text-destructive">{error}</p>;
|
||||
}
|
||||
if (!data) {
|
||||
return <AdminNoResourceState />;
|
||||
}
|
||||
|
||||
const published = data.batches.filter((b) => b.status === "published");
|
||||
@@ -82,41 +83,57 @@ export function DrawResultsConsole({ drawId }: { drawId: string }) {
|
||||
{t("drawNo")} {data.draw_no} · <DrawStatusBadge status={data.draw_status} />
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
|
||||
>
|
||||
{canManageDraw ? t("reviewAndPublish") : t("viewReviewQueue")}
|
||||
</Link>
|
||||
{canManageDraw ? (
|
||||
<Link
|
||||
href={`/admin/draws/${drawId}/review`}
|
||||
className={cn(buttonVariants({ variant: "outline", size: "sm" }))}
|
||||
>
|
||||
{t("reviewAndPublish")}
|
||||
</Link>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{published.length === 0 ? (
|
||||
<Card>
|
||||
<CardContent className="py-8 text-center text-sm text-muted-foreground">
|
||||
{t("noPublishedBatch")}
|
||||
<CardContent className="py-4">
|
||||
<AdminNoResourceState />
|
||||
</CardContent>
|
||||
</Card>
|
||||
) : (
|
||||
published.map((batch) => <BatchTable key={batch.id} batch={batch} />)
|
||||
published.map((batch) => (
|
||||
<BatchTable key={batch.id} batch={batch} showOperationalMeta={canManageDraw} />
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function BatchTable({ batch }: { batch: AdminDrawBatchRow }) {
|
||||
function BatchTable({
|
||||
batch,
|
||||
showOperationalMeta,
|
||||
}: {
|
||||
batch: AdminDrawBatchRow;
|
||||
showOperationalMeta: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation("draws");
|
||||
const formatDt = useAdminDateTimeFormatter();
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-base">{t("version", { version: batch.result_version })}</CardTitle>
|
||||
<p className="font-mono text-xs text-muted-foreground">
|
||||
{t("sourceType", {
|
||||
source: batch.source_type === "manual" ? t("manualEntry") : t("rng"),
|
||||
})}{" "}
|
||||
· {t("rngSummary", { hash: batch.rng_seed_hash ?? "—" })} ·{" "}
|
||||
{t("confirmedAt", { time: formatDt(batch.confirmed_at) })}
|
||||
</p>
|
||||
{showOperationalMeta ? (
|
||||
<p className="font-mono text-xs text-muted-foreground">
|
||||
{t("sourceType", {
|
||||
source: batch.source_type === "manual" ? t("manualEntry") : t("rng"),
|
||||
})}{" "}
|
||||
· {t("rngSummary", { hash: batch.rng_seed_hash ?? "—" })} ·{" "}
|
||||
{t("confirmedAt", { time: formatDt(batch.confirmed_at) })}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("confirmedAt", { time: formatDt(batch.confirmed_at) })}
|
||||
</p>
|
||||
)}
|
||||
</CardHeader>
|
||||
<CardContent className="overflow-x-auto pt-0">
|
||||
<Table>
|
||||
|
||||
@@ -16,6 +16,7 @@ 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,
|
||||
@@ -152,8 +153,11 @@ export function DrawReviewConsole({ drawId }: { drawId: string }) {
|
||||
return <AdminLoadingState minHeight="6rem" className="py-6" />;
|
||||
}
|
||||
|
||||
if (error || !data) {
|
||||
return <p className="text-sm text-destructive">{error ?? t("states.noData", { ns: "common" })}</p>;
|
||||
if (error) {
|
||||
return <p className="text-sm text-destructive">{error}</p>;
|
||||
}
|
||||
if (!data) {
|
||||
return <AdminNoResourceState />;
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -233,9 +237,7 @@ export function DrawReviewConsole({ drawId }: { drawId: string }) {
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{pending.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground py-6 text-center">
|
||||
{t("noPendingBatches")}
|
||||
</p>
|
||||
<AdminNoResourceState className="py-6" />
|
||||
) : (
|
||||
<Table>
|
||||
<TableHeader>
|
||||
|
||||
@@ -2,18 +2,35 @@
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { useMemo } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { buttonVariants } from "@/components/ui/button";
|
||||
import { adminHasAnyPermission } from "@/lib/admin-permissions";
|
||||
import { PRD_DRAW_FINANCE_ACCESS_ANY, PRD_RISK_ACCESS_ANY } from "@/lib/admin-prd";
|
||||
import { canManageDrawResults, canViewDrawFinance, canViewDrawResults } from "@/lib/draw-access";
|
||||
import { useAdminProfile } from "@/stores/admin-session";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const segments = [
|
||||
{ suffix: "", key: "status", label: "subnav.status" },
|
||||
{ suffix: "/results", key: "results", label: "subnav.results" },
|
||||
{ suffix: "/finance", key: "finance", label: "subnav.finance" },
|
||||
{ suffix: "/review", key: "review", label: "subnav.review" },
|
||||
{ suffix: "/risk/occupancy", key: "riskLockLogs", label: "subnav.riskLockLogs" },
|
||||
{ suffix: "/risk/pools", key: "riskPools", label: "subnav.riskPools" },
|
||||
{ suffix: "", key: "status", label: "subnav.status", requiresManage: false },
|
||||
{ suffix: "/results", key: "results", label: "subnav.results", requiresManage: false },
|
||||
{ suffix: "/finance", key: "finance", label: "subnav.finance", requiresManage: false },
|
||||
{ suffix: "/review", key: "review", label: "subnav.review", requiresManage: true },
|
||||
{
|
||||
suffix: "/risk/occupancy",
|
||||
key: "riskLockLogs",
|
||||
label: "subnav.riskLockLogs",
|
||||
requiresManage: false,
|
||||
requiresRisk: true,
|
||||
},
|
||||
{
|
||||
suffix: "/risk/pools",
|
||||
key: "riskPools",
|
||||
label: "subnav.riskPools",
|
||||
requiresManage: false,
|
||||
requiresRisk: true,
|
||||
},
|
||||
] as const;
|
||||
|
||||
function isRiskPoolsTabActive(pathname: string, base: string): boolean {
|
||||
@@ -23,6 +40,7 @@ function isRiskPoolsTabActive(pathname: string, base: string): boolean {
|
||||
}
|
||||
|
||||
const rest = pathname.slice(riskPrefix.length);
|
||||
|
||||
return (
|
||||
rest === "pools"
|
||||
|| rest.startsWith("pools/")
|
||||
@@ -34,21 +52,50 @@ function isRiskPoolsTabActive(pathname: string, base: string): boolean {
|
||||
function isReviewTabActive(pathname: string, base: string): boolean {
|
||||
const reviewPrefix = `${base}/review`;
|
||||
const publishPrefix = `${base}/publish`;
|
||||
|
||||
return (
|
||||
pathname === reviewPrefix ||
|
||||
pathname.startsWith(`${reviewPrefix}/`) ||
|
||||
pathname.startsWith(`${publishPrefix}/`)
|
||||
pathname === reviewPrefix
|
||||
|| pathname.startsWith(`${reviewPrefix}/`)
|
||||
|| pathname.startsWith(`${publishPrefix}/`)
|
||||
);
|
||||
}
|
||||
|
||||
export function DrawSubnav({ drawId }: { drawId: string }) {
|
||||
export function DrawSubnav({ drawId }: { drawId: string }): React.ReactElement {
|
||||
const { t } = useTranslation("draws");
|
||||
const pathname = usePathname();
|
||||
const base = `/admin/draws/${drawId}`;
|
||||
const profile = useAdminProfile();
|
||||
const perms = profile?.permissions ?? [];
|
||||
|
||||
const canViewDraw = canViewDrawResults(perms);
|
||||
const canManageDraw = canManageDrawResults(perms);
|
||||
const canViewFinance = canViewDrawFinance(perms);
|
||||
const canViewRisk = adminHasAnyPermission(perms, [...PRD_RISK_ACCESS_ANY]);
|
||||
|
||||
const visibleSegments = useMemo(
|
||||
() =>
|
||||
segments.filter((segment) => {
|
||||
if (!canViewDraw) {
|
||||
return false;
|
||||
}
|
||||
if (segment.requiresManage && !canManageDraw) {
|
||||
return false;
|
||||
}
|
||||
if (segment.key === "finance" && !canViewFinance) {
|
||||
return false;
|
||||
}
|
||||
if ("requiresRisk" in segment && segment.requiresRisk && !canViewRisk) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
[canManageDraw, canViewDraw, canViewRisk],
|
||||
);
|
||||
|
||||
return (
|
||||
<nav className="mb-6 flex flex-wrap gap-2 border-b border-border pb-3">
|
||||
{segments.map(({ suffix, key, label }) => {
|
||||
{visibleSegments.map(({ suffix, key, label }) => {
|
||||
const href = `${base}${suffix}`;
|
||||
const active =
|
||||
suffix === ""
|
||||
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { formatAdminInstant } from "@/lib/admin-datetime";
|
||||
import { getAdminRequestLocale } from "@/lib/admin-locale";
|
||||
import { AdminTableLoadingRow } from "@/components/admin/admin-loading-state";
|
||||
import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state";
|
||||
import { AdminRowActionsMenu } from "@/components/admin/admin-row-actions-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
|
||||
@@ -58,6 +59,7 @@ import { LotteryApiBizError } from "@/types/api/errors";
|
||||
import type { AdminDrawListData, AdminDrawListItem } from "@/types/api/admin-draws";
|
||||
|
||||
import { drawStatusLabel } from "./draw-display";
|
||||
import { canManageDrawResults, canViewDrawFinance } from "@/lib/draw-access";
|
||||
import { PRD_DRAW_RESULT_MANAGE } from "./draw-prd";
|
||||
import { DrawStatusBadge } from "./draw-status-badge";
|
||||
|
||||
@@ -94,7 +96,8 @@ export function DrawsIndexConsole() {
|
||||
useAdminCurrencyCatalog();
|
||||
const defaultCurrency = "NPR";
|
||||
const profile = useAdminProfile();
|
||||
const canManageDraw = adminHasAnyPermission(profile?.permissions, [PRD_DRAW_RESULT_MANAGE]);
|
||||
const canManageDraw = canManageDrawResults(profile?.permissions);
|
||||
const canViewFinance = canViewDrawFinance(profile?.permissions);
|
||||
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
|
||||
const [data, setData] = useState<AdminDrawListData | null>(null);
|
||||
const formatDt = useCallback(
|
||||
@@ -395,21 +398,21 @@ export function DrawsIndexConsole() {
|
||||
<TableHead>{t("closeTime")}</TableHead>
|
||||
<TableHead>{t("drawTime")}</TableHead>
|
||||
<TableHead>{t("status")}</TableHead>
|
||||
<TableHead className="text-center">{t("betTotal")}</TableHead>
|
||||
<TableHead className="text-center">{t("payoutTotal")}</TableHead>
|
||||
<TableHead className="text-center">{t("profitLoss")}</TableHead>
|
||||
{canViewFinance ? (
|
||||
<>
|
||||
<TableHead className="text-center">{t("betTotal")}</TableHead>
|
||||
<TableHead className="text-center">{t("payoutTotal")}</TableHead>
|
||||
<TableHead className="text-center">{t("profitLoss")}</TableHead>
|
||||
</>
|
||||
) : null}
|
||||
<TableHead className="sticky right-0 z-20 bg-muted w-14 text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">{t("table.actions", { ns: "common" })}</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{loading ? (
|
||||
<AdminTableLoadingRow colSpan={10} />
|
||||
<AdminTableLoadingRow colSpan={canViewFinance ? 10 : 7} />
|
||||
) : data === null || data.items.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={10} className="text-muted-foreground">
|
||||
{t("states.noData", { ns: "common" })}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<AdminTableNoResourceRow colSpan={canViewFinance ? 10 : 7} className="text-muted-foreground" />
|
||||
) : (
|
||||
data.items.map((row: AdminDrawListItem) => (
|
||||
<TableRow key={row.id}>
|
||||
@@ -431,26 +434,30 @@ export function DrawsIndexConsole() {
|
||||
label={drawStatusLabel(row.status, t)}
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs tabular-nums">
|
||||
{row.total_bet_minor != null
|
||||
? formatAdminMinorUnits(row.total_bet_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs tabular-nums">
|
||||
{row.total_payout_minor != null
|
||||
? formatAdminMinorUnits(row.total_payout_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={cn(
|
||||
"text-center text-xs tabular-nums",
|
||||
(row.profit_loss_minor ?? 0) < 0 ? "text-destructive" : "text-emerald-600",
|
||||
)}
|
||||
>
|
||||
{row.profit_loss_minor != null
|
||||
? formatAdminMinorUnits(row.profit_loss_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
{canViewFinance ? (
|
||||
<>
|
||||
<TableCell className="text-center text-xs tabular-nums">
|
||||
{row.total_bet_minor != null
|
||||
? formatAdminMinorUnits(row.total_bet_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center text-xs tabular-nums">
|
||||
{row.total_payout_minor != null
|
||||
? formatAdminMinorUnits(row.total_payout_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
<TableCell
|
||||
className={cn(
|
||||
"text-center text-xs tabular-nums",
|
||||
(row.profit_loss_minor ?? 0) < 0 ? "text-destructive" : "text-emerald-600",
|
||||
)}
|
||||
>
|
||||
{row.profit_loss_minor != null
|
||||
? formatAdminMinorUnits(row.profit_loss_minor, defaultCurrency)
|
||||
: "—"}
|
||||
</TableCell>
|
||||
</>
|
||||
) : null}
|
||||
<TableCell className="sticky right-0 z-10 bg-card text-center shadow-[-1px_0_0_rgba(203,213,225,0.7)]">
|
||||
<AdminRowActionsMenu
|
||||
actions={[
|
||||
|
||||
Reference in New Issue
Block a user