feat: 新增账号设置页面并优化报表与管理端交互样式
This commit is contained in:
@@ -39,6 +39,7 @@ import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
@@ -773,11 +774,27 @@ export function ReportsConsole() {
|
||||
const value =
|
||||
kind === "draw" ? filters.drawNo : kind === "player" ? filters.player : filters.operator;
|
||||
const labelKey = kind === "draw" ? "drawNo" : kind === "player" ? "player" : "operator";
|
||||
const open = search.open === kind;
|
||||
|
||||
return (
|
||||
<div className="relative grid gap-1.5">
|
||||
<div className="grid gap-1.5">
|
||||
<Label htmlFor={`report-${kind}`}>{t(`fields.${labelKey}`)}</Label>
|
||||
<div className="flex gap-2">
|
||||
<Popover
|
||||
open={open}
|
||||
onOpenChange={(nextOpen) => {
|
||||
setSearch((prev) =>
|
||||
nextOpen
|
||||
? {
|
||||
...prev,
|
||||
open: kind,
|
||||
query: value,
|
||||
}
|
||||
: emptySearch,
|
||||
);
|
||||
}}
|
||||
modal={false}
|
||||
>
|
||||
<div className="flex gap-2">
|
||||
<Input
|
||||
id={`report-${kind}`}
|
||||
value={value}
|
||||
@@ -793,25 +810,17 @@ export function ReportsConsole() {
|
||||
}}
|
||||
placeholder={t(`placeholders.${labelKey}`)}
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="shrink-0"
|
||||
onClick={() => {
|
||||
setSearch((prev) => ({
|
||||
...prev,
|
||||
open: prev.open === kind ? null : kind,
|
||||
query: value,
|
||||
}));
|
||||
}}
|
||||
aria-label={t("searchPicker.open")}
|
||||
<PopoverTrigger render={<Button type="button" variant="outline" className="shrink-0" aria-label={t("searchPicker.open")} />}>
|
||||
<Search data-icon="inline-start" />
|
||||
{t("searchPicker.select")}
|
||||
</PopoverTrigger>
|
||||
</div>
|
||||
<PopoverContent
|
||||
align="start"
|
||||
side="bottom"
|
||||
sideOffset={6}
|
||||
className="z-[80] w-[var(--anchor-width)] min-w-[min(24rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)] p-2"
|
||||
>
|
||||
<Search data-icon="inline-start" />
|
||||
{t("searchPicker.select")}
|
||||
</Button>
|
||||
</div>
|
||||
{search.open === kind ? (
|
||||
<div className="absolute left-0 right-0 top-[4.55rem] z-20 rounded-md border border-border bg-popover p-2 shadow-lg">
|
||||
<Input
|
||||
value={search.query}
|
||||
placeholder={t("searchPicker.keyword")}
|
||||
@@ -878,8 +887,8 @@ export function ReportsConsole() {
|
||||
))
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -1156,7 +1165,6 @@ export function ReportsConsole() {
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="min-w-0">
|
||||
<h1 className="text-lg font-semibold tracking-tight text-[#13315f]">{t("title")}</h1>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{t("subtitle")}</p>
|
||||
</div>
|
||||
<Badge variant="outline" className="h-7 px-3">
|
||||
{resultRowCount(result)} {t("preview.exportableRows")}
|
||||
@@ -1168,7 +1176,7 @@ export function ReportsConsole() {
|
||||
<CardHeader className="admin-list-header pb-4">
|
||||
<CardTitle className="admin-list-title">{t("chooseReport")}</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-2 pt-4">
|
||||
<CardContent className="space-y-1.5 pt-3">
|
||||
{REPORTS.map((report) => {
|
||||
const Icon = report.icon;
|
||||
const active = report.key === selectedReport.key;
|
||||
@@ -1178,7 +1186,7 @@ export function ReportsConsole() {
|
||||
type="button"
|
||||
onClick={() => setSelectedKey(report.key)}
|
||||
className={cn(
|
||||
"flex w-full min-w-0 items-start gap-3 rounded-md border px-3 py-3 text-left transition",
|
||||
"flex w-full min-w-0 items-center gap-3 rounded-md border px-3 py-2.5 text-left transition",
|
||||
active
|
||||
? "border-primary bg-primary/[0.05] shadow-sm ring-1 ring-primary/15"
|
||||
: "border-border/80 bg-card hover:border-primary/35 hover:bg-muted/30",
|
||||
@@ -1188,10 +1196,7 @@ export function ReportsConsole() {
|
||||
<Icon className="size-4" aria-hidden />
|
||||
</span>
|
||||
<span className="min-w-0 flex-1">
|
||||
<span className="block truncate text-sm font-semibold text-foreground">{t(`items.${report.key}.title`)}</span>
|
||||
<span className="mt-1 block text-xs text-muted-foreground">
|
||||
{t(`categories.${report.category}`)} · {formatKind(report.filterKind, t)}
|
||||
</span>
|
||||
<span className="block truncate text-sm font-medium text-foreground">{t(`items.${report.key}.title`)}</span>
|
||||
</span>
|
||||
</button>
|
||||
);
|
||||
@@ -1204,17 +1209,13 @@ export function ReportsConsole() {
|
||||
<CardHeader className="admin-list-header flex flex-col gap-3 pb-4 lg:flex-row lg:items-start lg:justify-between">
|
||||
<div className="min-w-0">
|
||||
<CardTitle className="admin-list-title">{t("filterPanel")}</CardTitle>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{t(`items.${selectedReport.key}.summary`)}</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4 pt-4">
|
||||
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
|
||||
{selectedReport.fields.map(renderField)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3 border-t border-border/60 pt-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{selectedReport.connected ? t("queryHint") : t("backendPending")}
|
||||
</p>
|
||||
<div className="flex flex-col gap-3 border-t border-border/60 pt-4 sm:flex-row sm:items-center sm:justify-end">
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<Button type="button" variant="outline" onClick={resetFilters}>
|
||||
{t("reset")}
|
||||
@@ -1253,7 +1254,6 @@ export function ReportsConsole() {
|
||||
<CardHeader className="admin-list-header flex flex-col gap-3 pb-4 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div>
|
||||
<CardTitle className="admin-list-title">{t("preview.title")}</CardTitle>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{t("preview.subtitle")}</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 gap-2">
|
||||
<Button type="button" variant="outline" disabled={!result || exporting !== null} onClick={() => exportReport("csv")}>
|
||||
|
||||
Reference in New Issue
Block a user