185 lines
6.5 KiB
TypeScript
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>
|
|
);
|
|
}
|