Added new types for downline share breakdown and settlement period open hints to enhance the agent settlement API. Updated the admin console components to support these new features, improving the user experience with better data presentation and interaction. Additionally, refined the date range field to accommodate new calendar markers and hints, ensuring a more intuitive interface for managing settlement periods.
162 lines
5.9 KiB
TypeScript
162 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import type { ReactElement } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { ArrowRight, Clock, Ticket } from "lucide-react";
|
|
|
|
import { buttonVariants } from "@/components/ui/button";
|
|
import { AdminNoResourceState } from "@/components/admin/admin-no-resource-state";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { Skeleton } from "@/components/ui/skeleton";
|
|
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
|
import { cn } from "@/lib/utils";
|
|
import type { DrawCurrentSnapshot } from "@/types/api/public-draw";
|
|
|
|
function formatDashboardDrawStatus(
|
|
status: string,
|
|
t: (key: string, options?: Record<string, unknown>) => string,
|
|
): string {
|
|
const lower = status.trim().toLowerCase();
|
|
|
|
switch (lower) {
|
|
case "pending":
|
|
return t("statusOptions.pending", { ns: "draws" });
|
|
case "open":
|
|
return t("statusOptions.open", { ns: "draws" });
|
|
case "closing":
|
|
return t("statusOptions.closing", { ns: "draws" });
|
|
case "closed":
|
|
return t("statusOptions.closed", { ns: "draws" });
|
|
case "drawing":
|
|
return t("statusOptions.drawing", { ns: "draws" });
|
|
case "review":
|
|
return t("statusOptions.review", { ns: "draws" });
|
|
case "cooldown":
|
|
return t("statusOptions.cooldown", { ns: "draws" });
|
|
case "settling":
|
|
return t("statusOptions.settling", { ns: "draws" });
|
|
case "settled":
|
|
return t("statusOptions.settled", { ns: "draws" });
|
|
case "cancelled":
|
|
return t("statusOptions.cancelled", { ns: "draws" });
|
|
default:
|
|
return status;
|
|
}
|
|
}
|
|
|
|
function isOpenLikeStatus(status: string): boolean {
|
|
const lower = status.toLowerCase();
|
|
return lower.includes("open") || lower.includes("sale");
|
|
}
|
|
|
|
type DashboardCurrentDrawCardProps = {
|
|
hall: DrawCurrentSnapshot | null;
|
|
drawId: number | null;
|
|
loading?: boolean;
|
|
};
|
|
|
|
export function DashboardCurrentDrawCard({
|
|
hall,
|
|
drawId,
|
|
loading = false,
|
|
}: DashboardCurrentDrawCardProps): ReactElement {
|
|
const { t } = useTranslation(["dashboard", "draws"]);
|
|
const formatDt = useAdminDateTimeFormatter();
|
|
|
|
if (loading) {
|
|
return (
|
|
<Card className="admin-list-card overflow-hidden py-0">
|
|
<CardContent className="p-5">
|
|
<Skeleton className="h-[4.5rem] w-full rounded-xl" />
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
if (!hall) {
|
|
return (
|
|
<Card className="admin-list-card overflow-hidden py-0">
|
|
<CardContent className="flex min-h-[5.5rem] flex-col items-center justify-center gap-2 p-6 text-center">
|
|
<p className="text-sm font-medium text-muted-foreground">{t("sections.currentDraw")}</p>
|
|
<AdminNoResourceState compact className="py-4" />
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|
|
|
|
const openLike = isOpenLikeStatus(hall.status);
|
|
const statusLabel = formatDashboardDrawStatus(hall.status, t);
|
|
|
|
return (
|
|
<Card className="admin-list-card overflow-hidden border-primary/15 py-0 shadow-sm">
|
|
<CardContent className="relative p-0">
|
|
<div
|
|
aria-hidden
|
|
className="pointer-events-none absolute inset-0 bg-gradient-to-r from-primary/[0.07] via-transparent to-transparent"
|
|
/>
|
|
<div className="relative flex flex-col gap-4 p-5 sm:flex-row sm:items-center sm:justify-between sm:gap-6">
|
|
<div className="flex min-w-0 items-center gap-4">
|
|
<div
|
|
className="flex size-12 shrink-0 items-center justify-center rounded-2xl bg-primary text-primary-foreground shadow-sm"
|
|
aria-hidden
|
|
>
|
|
<Ticket className="size-5" />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<p className="text-xs font-medium uppercase tracking-wide text-muted-foreground">
|
|
{t("sections.currentDraw")}
|
|
</p>
|
|
<p className="mt-1 font-mono text-2xl font-bold leading-none tracking-tight text-foreground sm:text-[1.65rem]">
|
|
{hall.draw_no}
|
|
</p>
|
|
<div className="mt-2.5 flex flex-wrap items-center gap-2">
|
|
<span className="text-xs text-muted-foreground">
|
|
{t("drawSequence", { sequence: hall.sequence_no ?? "—" })}
|
|
</span>
|
|
<span
|
|
className={cn(
|
|
"inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium",
|
|
openLike
|
|
? "bg-emerald-500/12 text-emerald-700 ring-1 ring-emerald-500/20 dark:text-emerald-400"
|
|
: "bg-muted/80 text-muted-foreground ring-1 ring-border/60",
|
|
)}
|
|
>
|
|
<span
|
|
className={cn(
|
|
"size-1.5 rounded-full",
|
|
openLike ? "bg-emerald-500" : "bg-muted-foreground/70",
|
|
)}
|
|
/>
|
|
{statusLabel}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap items-center gap-3 sm:justify-end">
|
|
{hall.draw_time ? (
|
|
<p className="inline-flex items-center gap-1.5 rounded-lg bg-muted/40 px-3 py-2 text-xs text-muted-foreground ring-1 ring-border/50">
|
|
<Clock className="size-3.5 shrink-0" aria-hidden />
|
|
<span>{t("scheduledDrawTime", { time: formatDt(hall.draw_time) })}</span>
|
|
</p>
|
|
) : null}
|
|
{drawId != null ? (
|
|
<Link
|
|
href={`/admin/draws/${drawId}/finance`}
|
|
className={cn(
|
|
buttonVariants({ variant: "default", size: "sm" }),
|
|
"h-9 gap-1.5 px-4 shadow-sm",
|
|
)}
|
|
>
|
|
{t("drawFinanceDetails")}
|
|
<ArrowRight className="size-3.5" aria-hidden />
|
|
</Link>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|