Files
lotteryFront/src/features/hall/hall-desktop-workspace.tsx

185 lines
6.5 KiB
TypeScript

import type { ReactNode } from "react";
import { AlertTriangle, CheckCircle2, Lock, Ticket } from "lucide-react";
import { Button } from "@/components/ui/button";
import { cn } from "@/lib/utils";
type HallDesktopWorkflowBarProps = {
gameControls: ReactNode;
fillControls: ReactNode;
reviewControls: ReactNode;
stepGame: string;
stepFill: string;
stepReview: string;
};
export function HallDesktopWorkflowBar({
gameControls,
fillControls,
reviewControls,
stepGame,
stepFill,
stepReview,
}: HallDesktopWorkflowBarProps) {
const steps = [
{ label: stepGame, content: gameControls },
{ label: stepFill, content: fillControls },
{ label: stepReview, content: reviewControls },
];
return (
<div className="hidden overflow-hidden rounded-xl border border-[#dfe8f5] bg-white shadow-[0_8px_24px_rgba(15,23,42,0.04)] lg:grid lg:grid-cols-[minmax(10rem,0.8fr)_minmax(16rem,1.5fr)_minmax(10rem,0.65fr)] xl:grid-cols-[minmax(15rem,0.8fr)_minmax(24rem,1.5fr)_minmax(13rem,0.65fr)]">
{steps.map((step, index) => (
<section
key={step.label}
className={cn(
"min-w-0 px-4 py-3",
index > 0 && "border-l border-[#e7edf6]",
)}
>
<div className="mb-2 flex items-center gap-2">
<span className="inline-flex size-6 shrink-0 items-center justify-center rounded-full bg-[#eaf2ff] text-xs font-black text-[#1658c4]">
{index + 1}
</span>
<h2 className="text-sm font-black text-[#23395f]">{step.label}</h2>
</div>
{step.content}
</section>
))}
</div>
);
}
export type HallDesktopReviewModel = {
lineCount: number;
providerCount: number;
actualAmount: string;
availableCredit: string;
remainingCredit: string;
warningCount: number;
creditSufficient: boolean;
};
type HallDesktopReviewPanelProps = {
model: HallDesktopReviewModel;
labels: {
title: string;
lineCount: string;
providerCount: string;
actualTotal: string;
availableCredit: string;
remainingCredit: string;
noWarnings: string;
warningCount: string;
creditEnough: string;
creditInsufficient: string;
submit: string;
};
disabled: boolean;
loading: boolean;
isBettable: boolean;
onSubmit: () => void;
};
export function HallDesktopReviewPanel({
model,
labels,
disabled,
loading,
isBettable,
onSubmit,
}: HallDesktopReviewPanelProps) {
return (
<aside className="w-full shrink-0 self-start overflow-hidden rounded-xl border border-[#dfe8f5] bg-white text-[#304f86] shadow-[0_8px_24px_rgba(15,23,42,0.05)] min-[1500px]:sticky min-[1500px]:top-3 min-[1500px]:w-80">
<div className="border-b border-[#e7edf6] bg-[#f7faff] px-4 py-3.5">
<p className="text-[15px] font-black text-[#20385f]">{labels.title}</p>
</div>
<div className="grid gap-3 p-4 lg:grid-cols-[repeat(4,minmax(0,1fr))_auto] lg:items-center min-[1500px]:block min-[1500px]:space-y-4">
<dl className="grid grid-cols-2 gap-x-5 gap-y-3 lg:contents min-[1500px]:grid">
<div>
<dt className="text-xs font-semibold text-slate-500">{labels.lineCount}</dt>
<dd className="mt-1 font-mono text-lg font-black tabular-nums text-[#143f86]">
{model.lineCount}
</dd>
</div>
<div>
<dt className="text-xs font-semibold text-slate-500">{labels.providerCount}</dt>
<dd className="mt-1 font-mono text-lg font-black tabular-nums text-[#143f86]">
{model.providerCount}
</dd>
</div>
<div>
<dt className="text-xs font-semibold text-slate-500">{labels.availableCredit}</dt>
<dd className="mt-1 text-sm font-black tabular-nums text-[#143f86]">
{model.availableCredit}
</dd>
</div>
<div>
<dt className="text-xs font-semibold text-slate-500">{labels.remainingCredit}</dt>
<dd className="mt-1 text-sm font-black tabular-nums text-[#143f86]">
{model.remainingCredit}
</dd>
</div>
</dl>
<div className="rounded-lg border border-[#d8e5f7] bg-[#f7fbff] px-3 py-2.5 lg:min-w-36 min-[1500px]:px-4 min-[1500px]:py-3.5">
<p className="text-xs font-semibold text-slate-500">{labels.actualTotal}</p>
<p className="mt-1 text-lg font-black tabular-nums text-[#0b3f96] min-[1500px]:text-2xl">
{model.actualAmount}
</p>
</div>
<div className="space-y-2 lg:min-w-48 min-[1500px]:border-t min-[1500px]:border-[#e7edf6] min-[1500px]:pt-4">
<div
className={cn(
"flex items-center gap-2 text-xs font-bold",
model.creditSufficient ? "text-emerald-700" : "text-red-600",
)}
>
{model.creditSufficient ? (
<CheckCircle2 className="size-4 shrink-0" aria-hidden />
) : (
<AlertTriangle className="size-4 shrink-0" aria-hidden />
)}
<span>{model.creditSufficient ? labels.creditEnough : labels.creditInsufficient}</span>
</div>
<div className="flex items-center gap-2 text-xs font-semibold text-slate-500">
{model.warningCount > 0 ? (
<AlertTriangle className="size-4 shrink-0 text-amber-600" aria-hidden />
) : (
<CheckCircle2 className="size-4 shrink-0 text-slate-400" aria-hidden />
)}
<span>
{model.warningCount > 0
? labels.warningCount
: labels.noWarnings}
</span>
</div>
</div>
<div className="lg:min-w-52 min-[1500px]:border-t min-[1500px]:border-[#e7edf6] min-[1500px]:pt-4">
<Button
type="button"
disabled={disabled || loading}
onClick={onSubmit}
className={cn(
"h-11 w-full gap-2 rounded-lg border-0 text-sm font-black text-white",
!isBettable
? "bg-slate-500 shadow-none hover:bg-slate-500 disabled:opacity-100"
: "bg-[#e5002c] shadow-[0_8px_20px_rgba(229,0,44,0.24)] hover:bg-[#d10028]",
)}
>
{!isBettable ? (
<Lock className="size-4 shrink-0" aria-hidden />
) : (
<Ticket className="size-4 shrink-0" aria-hidden />
)}
{labels.submit}
</Button>
</div>
</div>
</aside>
);
}