refactor(layout, i18n, admin): 优化布局结构与多语言支持
调整 AdminShell 组件的子组件顺序,提升代码可读性。更新 admin-breadcrumb 组件,简化导航标签翻译逻辑,确保多语言支持的一致性。重构 admin-language-switcher 组件,优化语言切换的用户体验,增强界面交互性。更新多语言配置,新增登录界面的副标题,提升用户体验。
This commit is contained in:
128
src/modules/dashboard/dashboard-current-draw-card.tsx
Normal file
128
src/modules/dashboard/dashboard-current-draw-card.tsx
Normal file
@@ -0,0 +1,128 @@
|
||||
"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 { 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 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");
|
||||
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">
|
||||
<Ticket className="size-9 text-muted-foreground/40" aria-hidden />
|
||||
<p className="text-sm font-medium text-muted-foreground">{t("sections.currentDraw")}</p>
|
||||
<p className="text-xs text-muted-foreground">{t("states.noData", { ns: "common" })}</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const openLike = isOpenLikeStatus(hall.status);
|
||||
|
||||
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",
|
||||
)}
|
||||
/>
|
||||
{hall.status}
|
||||
</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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user