refactor: 优化管理端配置页紧凑布局与时间展示
This commit is contained in:
@@ -16,7 +16,7 @@ export function AdminShell({ children }: { children: ReactNode }) {
|
|||||||
return (
|
return (
|
||||||
<SidebarProvider defaultOpen>
|
<SidebarProvider defaultOpen>
|
||||||
<AdminAppSidebar />
|
<AdminAppSidebar />
|
||||||
<SidebarInset className="min-w-0 overflow-x-hidden max-md:overflow-x-hidden">
|
<SidebarInset className="min-w-0 overflow-x-clip max-md:overflow-x-clip">
|
||||||
<header className="sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border bg-card/90 px-4 shadow-[0_1px_0_rgb(216_230_251_/_45%)] backdrop-blur-md">
|
<header className="sticky top-0 z-30 flex h-14 shrink-0 items-center gap-3 border-b border-border bg-card/90 px-4 shadow-[0_1px_0_rgb(216_230_251_/_45%)] backdrop-blur-md">
|
||||||
<SidebarTrigger />
|
<SidebarTrigger />
|
||||||
<Separator orientation="vertical" className="mr-1.5 h-4" />
|
<Separator orientation="vertical" className="mr-1.5 h-4" />
|
||||||
@@ -25,7 +25,7 @@ export function AdminShell({ children }: { children: ReactNode }) {
|
|||||||
<ShellToolbar />
|
<ShellToolbar />
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="flex min-w-0 flex-1 flex-col overflow-x-hidden px-4 pt-4 pb-6 md:px-5 md:pt-4 md:pb-6">
|
<div className="flex min-w-0 flex-1 flex-col overflow-x-clip px-4 pt-4 pb-6 md:px-5 md:pt-4 md:pb-6">
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
</SidebarInset>
|
</SidebarInset>
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ const buttonVariants = cva(
|
|||||||
link: "text-primary underline-offset-4 hover:underline",
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
},
|
},
|
||||||
size: {
|
size: {
|
||||||
default:
|
|
||||||
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
|
||||||
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
|
||||||
|
default:
|
||||||
|
"h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
||||||
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
|
||||||
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
|
||||||
icon: "size-8",
|
icon: "size-8",
|
||||||
|
|||||||
@@ -1,7 +1,26 @@
|
|||||||
import type { AdminApiLocale } from "@/lib/admin-locale";
|
import type { AdminApiLocale } from "@/lib/admin-locale";
|
||||||
|
|
||||||
function pad2(n: number): string {
|
function formatParts(date: Date, timeZone?: string): string {
|
||||||
return String(n).padStart(2, "0");
|
const parts = new Intl.DateTimeFormat("en-CA", {
|
||||||
|
timeZone,
|
||||||
|
year: "numeric",
|
||||||
|
month: "2-digit",
|
||||||
|
day: "2-digit",
|
||||||
|
hour: "2-digit",
|
||||||
|
minute: "2-digit",
|
||||||
|
second: "2-digit",
|
||||||
|
hourCycle: "h23",
|
||||||
|
}).formatToParts(date);
|
||||||
|
|
||||||
|
const map = new Map(parts.map((part) => [part.type, part.value]));
|
||||||
|
const year = map.get("year") ?? "0000";
|
||||||
|
const month = map.get("month") ?? "00";
|
||||||
|
const day = map.get("day") ?? "00";
|
||||||
|
const hour = map.get("hour") ?? "00";
|
||||||
|
const minute = map.get("minute") ?? "00";
|
||||||
|
const second = map.get("second") ?? "00";
|
||||||
|
|
||||||
|
return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -21,12 +40,30 @@ export function formatAdminInstant(
|
|||||||
if (Number.isNaN(ms)) {
|
if (Number.isNaN(ms)) {
|
||||||
return "—";
|
return "—";
|
||||||
}
|
}
|
||||||
const date = new Date(ms);
|
return formatParts(new Date(ms));
|
||||||
const y = date.getFullYear();
|
}
|
||||||
const m = pad2(date.getMonth() + 1);
|
|
||||||
const d = pad2(date.getDate());
|
/**
|
||||||
const h = pad2(date.getHours());
|
* 将接口返回的 ISO 时间串格式化到指定时区,便于并列展示多个地区的时间。
|
||||||
const min = pad2(date.getMinutes());
|
*/
|
||||||
const s = pad2(date.getSeconds());
|
export function formatAdminInstantInTimeZone(
|
||||||
return `${y}-${m}-${d} ${h}:${min}:${s}`;
|
iso: string | null | undefined,
|
||||||
|
options: {
|
||||||
|
locale: AdminApiLocale;
|
||||||
|
timeZone: string;
|
||||||
|
},
|
||||||
|
): string {
|
||||||
|
void options.locale;
|
||||||
|
if (iso == null || iso === "") {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
const ms = Date.parse(iso);
|
||||||
|
if (Number.isNaN(ms)) {
|
||||||
|
return "—";
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
return formatParts(new Date(ms), options.timeZone);
|
||||||
|
} catch {
|
||||||
|
return formatParts(new Date(ms));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export function ConfigVersionActions({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-9 border-slate-300 bg-white px-3 text-slate-700 hover:bg-slate-50 hover:text-slate-950"
|
className="border-slate-300 bg-white text-slate-700 hover:bg-slate-50 hover:text-slate-950"
|
||||||
disabled={loadingList}
|
disabled={loadingList}
|
||||||
onClick={onRefresh}
|
onClick={onRefresh}
|
||||||
>
|
>
|
||||||
@@ -49,7 +49,7 @@ export function ConfigVersionActions({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-9 bg-slate-950 px-3 text-white hover:bg-slate-800"
|
className="bg-slate-950 text-white hover:bg-slate-800"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
onClick={onNewDraft}
|
onClick={onNewDraft}
|
||||||
>
|
>
|
||||||
@@ -61,7 +61,7 @@ export function ConfigVersionActions({
|
|||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-9 border-amber-300 bg-amber-50 px-3 text-amber-900 hover:bg-amber-100 hover:text-amber-950"
|
className="border-amber-300 bg-amber-50 text-amber-900 hover:bg-amber-100 hover:text-amber-950"
|
||||||
disabled={draftActionBusy}
|
disabled={draftActionBusy}
|
||||||
onClick={onSaveDraft}
|
onClick={onSaveDraft}
|
||||||
>
|
>
|
||||||
@@ -70,7 +70,7 @@ export function ConfigVersionActions({
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
className="h-9 bg-emerald-600 px-3 text-white hover:bg-emerald-700"
|
className="bg-emerald-600 text-white hover:bg-emerald-700"
|
||||||
disabled={draftActionBusy}
|
disabled={draftActionBusy}
|
||||||
onClick={onPublish}
|
onClick={onPublish}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ export function ConfigVersionSwitcher({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
disabled={loading || sortedVersions.length === 0}
|
disabled={loading || sortedVersions.length === 0}
|
||||||
onClick={() => setSheetOpen(true)}
|
onClick={() => setSheetOpen(true)}
|
||||||
className="h-9 shrink-0 border-slate-300 bg-white px-3 text-slate-800 hover:bg-slate-50 hover:text-slate-950"
|
className="shrink-0 border-slate-300 bg-white text-slate-800 hover:bg-slate-50 hover:text-slate-950"
|
||||||
>
|
>
|
||||||
<Layers className="size-4" aria-hidden />
|
<Layers className="size-4" aria-hidden />
|
||||||
{t("versionSwitcher.switch", { ns: "config" })}
|
{t("versionSwitcher.switch", { ns: "config" })}
|
||||||
@@ -278,7 +278,7 @@ export function ConfigVersionSwitcher({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8 rounded-full px-3 text-xs text-slate-600 hover:bg-slate-100 hover:text-slate-950"
|
className="rounded-full text-xs text-slate-600 hover:bg-slate-100 hover:text-slate-950"
|
||||||
disabled={rollbackBusy}
|
disabled={rollbackBusy}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onRollbackVersion(v);
|
onRollbackVersion(v);
|
||||||
@@ -293,7 +293,7 @@ export function ConfigVersionSwitcher({
|
|||||||
type="button"
|
type="button"
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="sm"
|
size="sm"
|
||||||
className="h-8 rounded-full px-3 text-xs text-rose-600 hover:bg-rose-50 hover:text-rose-700"
|
className="rounded-full text-xs text-rose-600 hover:bg-rose-50 hover:text-rose-700"
|
||||||
disabled={deletingId === v.id}
|
disabled={deletingId === v.id}
|
||||||
onClick={() => setDeleteTarget(v)}
|
onClick={() => setDeleteTarget(v)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export function ConfigWorkspaceShell({ children }: { children: ReactNode }) {
|
|||||||
return (
|
return (
|
||||||
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6">
|
<div className="mx-auto flex w-full max-w-7xl flex-col gap-6">
|
||||||
<div className="flex flex-col gap-6 lg:flex-row lg:items-start">
|
<div className="flex flex-col gap-6 lg:flex-row lg:items-start">
|
||||||
<aside className="shrink-0 lg:sticky lg:top-20 lg:h-[calc(100vh-6rem)] lg:w-56 lg:self-start">
|
<aside className="shrink-0 lg:sticky lg:top-[72px] lg:w-56 lg:self-start">
|
||||||
<div className="h-full rounded-2xl border border-border/70 bg-card/80 p-3 shadow-sm backdrop-blur lg:overflow-auto">
|
<div className="rounded-2xl border border-border/70 bg-card/80 p-3 shadow-sm backdrop-blur lg:max-h-[calc(100vh-7rem)] lg:overflow-y-auto">
|
||||||
<div className="mb-3 px-1">
|
<div className="mb-3 px-1">
|
||||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-muted-foreground">
|
||||||
{t("nav.sidebarTitle")}
|
{t("nav.sidebarTitle")}
|
||||||
|
|||||||
@@ -407,7 +407,11 @@ export function OddsConfigDocScreen() {
|
|||||||
key={t.id}
|
key={t.id}
|
||||||
type="button"
|
type="button"
|
||||||
variant={catTab === t.id ? "default" : "outline"}
|
variant={catTab === t.id ? "default" : "outline"}
|
||||||
className={cn(catTab === t.id && "shadow-sm")}
|
size="xs"
|
||||||
|
className={cn(
|
||||||
|
"h-7 rounded-full px-3 text-xs font-medium",
|
||||||
|
catTab === t.id ? "shadow-sm" : "bg-white text-slate-900",
|
||||||
|
)}
|
||||||
onClick={() => setCatTab(t.id)}
|
onClick={() => setCatTab(t.id)}
|
||||||
>
|
>
|
||||||
{t.label}
|
{t.label}
|
||||||
@@ -427,7 +431,7 @@ export function OddsConfigDocScreen() {
|
|||||||
type="button"
|
type="button"
|
||||||
variant={resolvedPlayCode === t.play_code ? "secondary" : "outline"}
|
variant={resolvedPlayCode === t.play_code ? "secondary" : "outline"}
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-9 border-slate-300 px-5 text-[18px] font-medium",
|
"h-8 rounded-full border-slate-300 px-4 text-sm font-medium",
|
||||||
resolvedPlayCode === t.play_code
|
resolvedPlayCode === t.play_code
|
||||||
? "border-slate-950 bg-slate-950 text-white shadow-sm hover:bg-slate-900"
|
? "border-slate-950 bg-slate-950 text-white shadow-sm hover:bg-slate-900"
|
||||||
: "bg-white text-slate-900 hover:border-slate-400 hover:bg-slate-50",
|
: "bg-white text-slate-900 hover:border-slate-400 hover:bg-slate-50",
|
||||||
@@ -470,20 +474,25 @@ export function OddsConfigDocScreen() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{detail ? (
|
{detail ? (
|
||||||
<p className="text-sm text-muted-foreground">
|
<div className="space-y-1 text-sm">
|
||||||
{t("odds.activeVersionPrefix", { ns: "config" })}
|
<p className="text-muted-foreground">
|
||||||
{activeHead ? (
|
{t("odds.activeVersionPrefix", { ns: "config" })}
|
||||||
<>
|
{activeHead ? (
|
||||||
v{activeHead.version_no}
|
<>
|
||||||
{activeHead.effective_at ? ` · ${formatDt(activeHead.effective_at)}` : ""}
|
v{activeHead.version_no}
|
||||||
</>
|
{activeHead.effective_at ? ` · ${formatDt(activeHead.effective_at)}` : ""}
|
||||||
) : (
|
</>
|
||||||
"—"
|
) : (
|
||||||
)}
|
"—"
|
||||||
{!isDraft ? (
|
)}
|
||||||
<span className="text-amber-600 dark:text-amber-400"> - {t("odds.readOnlyHint", { ns: "config" })}</span>
|
{!isDraft ? (
|
||||||
) : null}
|
<span className="text-amber-600 dark:text-amber-400">
|
||||||
</p>
|
{" "}
|
||||||
|
- {t("odds.readOnlyHint", { ns: "config" })}
|
||||||
|
</span>
|
||||||
|
) : null}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{error ? <p className="text-sm text-destructive">{error}</p> : null}
|
{error ? <p className="text-sm text-destructive">{error}</p> : null}
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import {
|
|||||||
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
|
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
|
||||||
import { ConfigVersionActions } from "@/modules/config/config-version-actions";
|
import { ConfigVersionActions } from "@/modules/config/config-version-actions";
|
||||||
import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher";
|
import { ConfigVersionSwitcher } from "@/modules/config/config-version-switcher";
|
||||||
|
import { useAdminDateTimeFormatter } from "@/hooks/use-admin-datetime-formatter";
|
||||||
import { LotteryApiBizError } from "@/types/api/errors";
|
import { LotteryApiBizError } from "@/types/api/errors";
|
||||||
import type {
|
import type {
|
||||||
ConfigVersionSummary,
|
ConfigVersionSummary,
|
||||||
@@ -129,6 +130,7 @@ function buildPlayConfigSavePayload(
|
|||||||
|
|
||||||
export function PlayConfigDocScreen() {
|
export function PlayConfigDocScreen() {
|
||||||
const { t } = useTranslation(["config", "adminUsers", "common"]);
|
const { t } = useTranslation(["config", "adminUsers", "common"]);
|
||||||
|
const formatDt = useAdminDateTimeFormatter();
|
||||||
const [list, setList] = useState<ConfigVersionSummary[]>([]);
|
const [list, setList] = useState<ConfigVersionSummary[]>([]);
|
||||||
const [selectedId, setSelectedId] = useState("");
|
const [selectedId, setSelectedId] = useState("");
|
||||||
const [detail, setDetail] = useState<PlayConfigVersionDetail | null>(null);
|
const [detail, setDetail] = useState<PlayConfigVersionDetail | null>(null);
|
||||||
@@ -408,7 +410,7 @@ export function PlayConfigDocScreen() {
|
|||||||
{activeHead ? (
|
{activeHead ? (
|
||||||
<>
|
<>
|
||||||
{t("play.activeVersion", { ns: "config", version: activeHead.version_no })}
|
{t("play.activeVersion", { ns: "config", version: activeHead.version_no })}
|
||||||
{activeHead.effective_at ? ` · ${activeHead.effective_at}` : ""}
|
{activeHead.effective_at ? ` · ${formatDt(activeHead.effective_at)}` : ""}
|
||||||
</>
|
</>
|
||||||
) : null}
|
) : null}
|
||||||
{!isDraft ? (
|
{!isDraft ? (
|
||||||
@@ -425,9 +427,6 @@ export function PlayConfigDocScreen() {
|
|||||||
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
<div className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||||
<div>
|
<div>
|
||||||
<p className="text-sm font-medium">{t("play.batchSwitchesTitle", { ns: "config" })}</p>
|
<p className="text-sm font-medium">{t("play.batchSwitchesTitle", { ns: "config" })}</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("play.batchSwitchesDesc", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
{!isDraft ? (
|
{!isDraft ? (
|
||||||
<span className="text-xs text-amber-600 dark:text-amber-400">
|
<span className="text-xs text-amber-600 dark:text-amber-400">
|
||||||
|
|||||||
@@ -377,9 +377,6 @@ export function RebateConfigDocScreen() {
|
|||||||
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
|
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
|
||||||
{t("rebate.winEnjoy.label", { ns: "config" })}
|
{t("rebate.winEnjoy.label", { ns: "config" })}
|
||||||
</Label>
|
</Label>
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("rebate.winEnjoy.description", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -364,7 +364,7 @@ export function RiskCapDocScreen() {
|
|||||||
|
|
||||||
{detail ? (
|
{detail ? (
|
||||||
<p className="text-sm text-muted-foreground">
|
<p className="text-sm text-muted-foreground">
|
||||||
{t("riskCap.effectiveAt", { ns: "config", value: detail.effective_at ? formatDt(detail.effective_at) : "—" })} · {t("riskCap.note", { ns: "config", value: detail.reason ?? "—" })}
|
{t("riskCap.effectiveAt", { ns: "config", value: detail.effective_at ? formatDt(detail.effective_at) : "—" })}
|
||||||
{!isDraft ? (
|
{!isDraft ? (
|
||||||
<span className="text-amber-600 dark:text-amber-400"> - {t("riskCap.readOnlyHint", { ns: "config" })}</span>
|
<span className="text-amber-600 dark:text-amber-400"> - {t("riskCap.readOnlyHint", { ns: "config" })}</span>
|
||||||
) : null}
|
) : null}
|
||||||
@@ -376,9 +376,6 @@ export function RiskCapDocScreen() {
|
|||||||
|
|
||||||
<section className="space-y-3 rounded-lg border bg-muted/20 p-4">
|
<section className="space-y-3 rounded-lg border bg-muted/20 p-4">
|
||||||
<h3 className="text-sm font-medium">{t("riskCap.defaultCap.title", { ns: "config" })}</h3>
|
<h3 className="text-sm font-medium">{t("riskCap.defaultCap.title", { ns: "config" })}</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("riskCap.defaultCap.description", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-wrap items-end gap-2">
|
<div className="flex flex-wrap items-end gap-2">
|
||||||
<div className="grid gap-1">
|
<div className="grid gap-1">
|
||||||
<Label htmlFor="default-cap">{t("riskCap.defaultCap.fieldLabel", { ns: "config" })}</Label>
|
<Label htmlFor="default-cap">{t("riskCap.defaultCap.fieldLabel", { ns: "config" })}</Label>
|
||||||
@@ -503,9 +500,6 @@ export function RiskCapDocScreen() {
|
|||||||
|
|
||||||
<section className="space-y-3">
|
<section className="space-y-3">
|
||||||
<h3 className="text-sm font-medium">{t("riskCap.occupancy.title", { ns: "config" })}</h3>
|
<h3 className="text-sm font-medium">{t("riskCap.occupancy.title", { ns: "config" })}</h3>
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("riskCap.occupancy.description", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-wrap gap-3 items-end">
|
<div className="flex flex-wrap gap-3 items-end">
|
||||||
<div className="grid gap-1">
|
<div className="grid gap-1">
|
||||||
<Label htmlFor="occ-search">{t("riskCap.occupancy.searchLabel", { ns: "config" })}</Label>
|
<Label htmlFor="occ-search">{t("riskCap.occupancy.searchLabel", { ns: "config" })}</Label>
|
||||||
|
|||||||
@@ -115,9 +115,6 @@ export function WalletConfigDocScreen() {
|
|||||||
<CardTitle>{t("wallet.title", { ns: "config" })}</CardTitle>
|
<CardTitle>{t("wallet.title", { ns: "config" })}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("wallet.description", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
<div className="grid gap-6 sm:grid-cols-2">
|
<div className="grid gap-6 sm:grid-cols-2">
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="in-min">{t("wallet.fields.inMin", { ns: "config" })}</Label>
|
<Label htmlFor="in-min">{t("wallet.fields.inMin", { ns: "config" })}</Label>
|
||||||
@@ -131,9 +128,6 @@ export function WalletConfigDocScreen() {
|
|||||||
onChange={(e) => handleChange("inMin", e.target.value)}
|
onChange={(e) => handleChange("inMin", e.target.value)}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("wallet.hints.inMin", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="in-max">{t("wallet.fields.inMax", { ns: "config" })}</Label>
|
<Label htmlFor="in-max">{t("wallet.fields.inMax", { ns: "config" })}</Label>
|
||||||
@@ -147,9 +141,6 @@ export function WalletConfigDocScreen() {
|
|||||||
onChange={(e) => handleChange("inMax", e.target.value)}
|
onChange={(e) => handleChange("inMax", e.target.value)}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("wallet.hints.inMax", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="out-min">{t("wallet.fields.outMin", { ns: "config" })}</Label>
|
<Label htmlFor="out-min">{t("wallet.fields.outMin", { ns: "config" })}</Label>
|
||||||
@@ -163,9 +154,6 @@ export function WalletConfigDocScreen() {
|
|||||||
onChange={(e) => handleChange("outMin", e.target.value)}
|
onChange={(e) => handleChange("outMin", e.target.value)}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("wallet.hints.outMin", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2">
|
<div className="space-y-2">
|
||||||
<Label htmlFor="out-max">{t("wallet.fields.outMax", { ns: "config" })}</Label>
|
<Label htmlFor="out-max">{t("wallet.fields.outMax", { ns: "config" })}</Label>
|
||||||
@@ -179,9 +167,6 @@ export function WalletConfigDocScreen() {
|
|||||||
onChange={(e) => handleChange("outMax", e.target.value)}
|
onChange={(e) => handleChange("outMax", e.target.value)}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("wallet.hints.outMax", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
|
|||||||
@@ -120,14 +120,6 @@ export function SystemSettingsScreen() {
|
|||||||
</CardTitle>
|
</CardTitle>
|
||||||
</div>
|
</div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-3 text-sm text-muted-foreground">
|
|
||||||
<p>
|
|
||||||
{t("system.runtimeIntro1", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
<p>
|
|
||||||
{t("system.runtimeIntro2", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
@@ -135,18 +127,9 @@ export function SystemSettingsScreen() {
|
|||||||
<CardTitle>{t("system.title", { ns: "config" })}</CardTitle>
|
<CardTitle>{t("system.title", { ns: "config" })}</CardTitle>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6">
|
<CardContent className="space-y-6">
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t("system.description", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
<div className="grid gap-6 md:grid-cols-2">
|
||||||
<div className="space-y-3 rounded-xl border border-border/70 p-4">
|
<div className="space-y-3 rounded-xl border border-border/70 p-4">
|
||||||
<div className="space-y-1">
|
<Label htmlFor="manual-review">{t("system.fields.manualReview", { ns: "config" })}</Label>
|
||||||
<Label htmlFor="manual-review">{t("system.fields.manualReview", { ns: "config" })}</Label>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("system.hints.manualReview", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="manual-review"
|
id="manual-review"
|
||||||
@@ -163,12 +146,7 @@ export function SystemSettingsScreen() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-3 rounded-xl border border-border/70 p-4">
|
<div className="space-y-3 rounded-xl border border-border/70 p-4">
|
||||||
<div className="space-y-1">
|
<Label htmlFor="auto-settlement">{t("system.fields.autoSettlement", { ns: "config" })}</Label>
|
||||||
<Label htmlFor="auto-settlement">{t("system.fields.autoSettlement", { ns: "config" })}</Label>
|
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("system.hints.autoSettlement", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-3">
|
||||||
<Checkbox
|
<Checkbox
|
||||||
id="auto-settlement"
|
id="auto-settlement"
|
||||||
@@ -196,9 +174,6 @@ export function SystemSettingsScreen() {
|
|||||||
onChange={(e) => updateDraft("cooldownMinutes", e.target.value)}
|
onChange={(e) => updateDraft("cooldownMinutes", e.target.value)}
|
||||||
disabled={loading || saving}
|
disabled={loading || saving}
|
||||||
/>
|
/>
|
||||||
<p className="text-xs text-muted-foreground">
|
|
||||||
{t("system.hints.cooldownMinutes", { ns: "config" })}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex items-center gap-4">
|
||||||
|
|||||||
@@ -496,7 +496,7 @@ export function TransferOrdersPanel(): React.ReactElement {
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="destructive"
|
variant="destructive"
|
||||||
className="h-6 px-2 text-xs"
|
className="text-xs"
|
||||||
disabled={actionLoading.has(row.transfer_no)}
|
disabled={actionLoading.has(row.transfer_no)}
|
||||||
onClick={() => handleReverse(row.transfer_no)}
|
onClick={() => handleReverse(row.transfer_no)}
|
||||||
>
|
>
|
||||||
@@ -507,7 +507,7 @@ export function TransferOrdersPanel(): React.ReactElement {
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="h-6 px-2 text-xs"
|
className="text-xs"
|
||||||
disabled={actionLoading.has(row.transfer_no)}
|
disabled={actionLoading.has(row.transfer_no)}
|
||||||
onClick={() => handleManuallyProcess(row.transfer_no)}
|
onClick={() => handleManuallyProcess(row.transfer_no)}
|
||||||
>
|
>
|
||||||
|
|||||||
Reference in New Issue
Block a user