refactor: 优化配置与奖池页面多语言编辑及管理端列表布局
This commit is contained in:
@@ -168,9 +168,11 @@ export function PlayerTicketsConsole(): React.ReactElement {
|
||||
<CardTitle className="admin-list-title">{t("playerTicketQuery")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="admin-list-content">
|
||||
<div className="grid gap-3 lg:grid-cols-2 xl:grid-cols-4">
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="pt-player">{t("playerId")}</Label>
|
||||
<div className="admin-list-toolbar">
|
||||
<div className="admin-list-field min-w-[12rem] flex-1 sm:max-w-md">
|
||||
<Label htmlFor="pt-player" className="sm:shrink-0">
|
||||
{t("playerId")}
|
||||
</Label>
|
||||
<Input
|
||||
id="pt-player"
|
||||
className="font-mono"
|
||||
@@ -181,18 +183,22 @@ export function PlayerTicketsConsole(): React.ReactElement {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="pt-draw">{t("drawNoOptional")}</Label>
|
||||
<div className="admin-list-field">
|
||||
<Label htmlFor="pt-draw" className="sm:shrink-0">
|
||||
{t("drawNoOptional")}
|
||||
</Label>
|
||||
<Input
|
||||
id="pt-draw"
|
||||
className="font-mono text-sm"
|
||||
className="font-mono text-sm sm:w-44"
|
||||
placeholder={t("drawNoPlaceholder")}
|
||||
value={draft.drawNo}
|
||||
onChange={(e) => setDraft((current) => ({ ...current, drawNo: e.target.value }))}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor="pt-number">{t("numberKeyword")}</Label>
|
||||
<div className="admin-list-field min-w-[12rem] flex-1 sm:max-w-md">
|
||||
<Label htmlFor="pt-number" className="sm:shrink-0">
|
||||
{t("numberKeyword")}
|
||||
</Label>
|
||||
<Input
|
||||
id="pt-number"
|
||||
className="font-mono text-sm"
|
||||
@@ -203,62 +209,67 @@ export function PlayerTicketsConsole(): React.ReactElement {
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5">
|
||||
<AdminDateRangeField
|
||||
id="pt-date-range"
|
||||
label={t("placedDateRange")}
|
||||
from={draft.startDate}
|
||||
to={draft.endDate}
|
||||
onRangeChange={(range) =>
|
||||
setDraft((current) => ({
|
||||
...current,
|
||||
startDate: range.from,
|
||||
endDate: range.to,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
<div className="admin-list-field">
|
||||
<Label htmlFor="pt-date-range" className="sm:shrink-0">
|
||||
{t("placedDateRange")}
|
||||
</Label>
|
||||
<div className="min-w-0 w-full sm:w-56">
|
||||
<AdminDateRangeField
|
||||
id="pt-date-range"
|
||||
from={draft.startDate}
|
||||
to={draft.endDate}
|
||||
onRangeChange={(range) =>
|
||||
setDraft((current) => ({
|
||||
...current,
|
||||
startDate: range.from,
|
||||
endDate: range.to,
|
||||
}))
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<span className="text-sm font-medium leading-none">{t("statusFilterLabel")}</span>
|
||||
<span className="text-muted-foreground text-xs">{t("statusHint")}</span>
|
||||
<div className="admin-list-field">
|
||||
<Label htmlFor="pt-status" className="sm:shrink-0">
|
||||
{t("statusFilterLabel")}
|
||||
</Label>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
id="pt-status"
|
||||
title={t("statusHint")}
|
||||
className="inline-flex h-8 w-full min-w-0 items-center justify-between rounded-md border border-border bg-card px-3 text-left text-sm font-normal shadow-sm outline-none transition-all hover:bg-accent focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 sm:w-44"
|
||||
>
|
||||
<span className="truncate">{ticketStatusSummary(draft.statuses, t)}</span>
|
||||
<ChevronDown className="size-4 shrink-0 opacity-60" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="start" className="w-56">
|
||||
{TICKET_STATUS_OPTIONS.map((status) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={status}
|
||||
checked={draft.statuses.includes(status)}
|
||||
onCheckedChange={(checked) => toggleStatus(status, checked === true)}
|
||||
>
|
||||
{ticketStatusText(status, t)}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger className="inline-flex h-11 w-full items-center justify-between rounded-md border border-border bg-card px-4 text-left text-sm font-normal text-primary shadow-sm outline-none transition-all hover:bg-accent hover:text-primary focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50">
|
||||
<span className="truncate">{ticketStatusSummary(draft.statuses, t)}</span>
|
||||
<ChevronDown className="size-4 shrink-0 opacity-60" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent className="w-[min(28rem,calc(100vw-2rem))]">
|
||||
{TICKET_STATUS_OPTIONS.map((status) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={status}
|
||||
checked={draft.statuses.includes(status)}
|
||||
onCheckedChange={(checked) => toggleStatus(status, checked === true)}
|
||||
>
|
||||
{ticketStatusText(status, t)}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<AdminTableExportButton
|
||||
tableId="tickets-table"
|
||||
filename={exportLabels.filename}
|
||||
<div className="admin-list-actions">
|
||||
<AdminTableExportButton
|
||||
tableId="tickets-table"
|
||||
filename={exportLabels.filename}
|
||||
sheetName={exportLabels.sheetName}
|
||||
/>
|
||||
<Button type="button" size="sm" onClick={() => runSearch()}>
|
||||
{t("query")}
|
||||
</Button>
|
||||
<Button type="button" size="sm" variant="outline" onClick={() => resetFilters()}>
|
||||
{t("resetFilters")}
|
||||
</Button>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void load()}>
|
||||
{t("refreshCurrentPage")}
|
||||
</Button>
|
||||
/>
|
||||
<Button type="button" size="sm" onClick={() => runSearch()}>
|
||||
{t("query")}
|
||||
</Button>
|
||||
<Button type="button" size="sm" variant="outline" onClick={() => resetFilters()}>
|
||||
{t("resetFilters")}
|
||||
</Button>
|
||||
<Button type="button" size="sm" variant="secondary" onClick={() => void load()}>
|
||||
{t("refreshCurrentPage")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{applied.playerQuery || applied.drawNo || applied.numberKeyword || applied.startDate || applied.endDate || applied.statuses.length > 0 ? (
|
||||
|
||||
Reference in New Issue
Block a user