feat(ui): enhance table and admin components with improved layout and status display

- Updated global CSS to center-align table headers and cells, ensuring a consistent layout.
- Modified admin table components to replace switches with status badges for better clarity.
- Enhanced internationalization support by adding new strings for version actions and validation messages in multiple locales.
- Refactored configuration document screens to include version selection and improved user feedback on status changes.
This commit is contained in:
2026-05-26 11:13:16 +08:00
parent 05fa0cbeec
commit 4080f0b601
38 changed files with 788 additions and 608 deletions

View File

@@ -14,9 +14,12 @@ import {
publishOddsVersion,
putOddsItems,
} from "@/api/admin-config";
import { ConfigContextBanner, ConfigContextEmphasis } from "@/modules/config/config-context-banner";
import { ConfigDocPage, ConfigDocToolbar } from "@/modules/config/config-doc-page";
import { Switch } from "@/components/ui/switch";
import {
ConfigVersionToolbarMeta,
ConfigVersionToolbarMetaEmphasis,
} from "@/modules/config/config-version-toolbar-meta";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { ConfigReadonlyValue } from "@/modules/config/config-readonly-value";
@@ -54,9 +57,15 @@ function inferPercentFrom(dim: 2 | 3 | 4, rows: OddsItemRow[], typeList: AdminPl
type RebateConfigDocScreenProps = {
embedded?: boolean;
versionId?: string;
onVersionIdChange?: (id: string) => void;
};
export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScreenProps) {
export function RebateConfigDocScreen({
embedded = false,
versionId: controlledVersionId,
onVersionIdChange,
}: RebateConfigDocScreenProps) {
const { t } = useTranslation(["config", "common"]);
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
const profile = useAdminProfile();
@@ -65,7 +74,9 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
const [types, setTypes] = useState<AdminPlayTypeRow[]>([]);
const [listRows, setListRows] = useState<ConfigVersionSummary[]>([]);
const [selectedId, setSelectedId] = useState("");
const [internalSelectedId, setInternalSelectedId] = useState("");
const selectedId = controlledVersionId ?? internalSelectedId;
const setSelectedId = onVersionIdChange ?? setInternalSelectedId;
const [detail, setDetail] = useState<OddsVersionDetail | null>(null);
const [draftRows, setDraftRows] = useState<OddsItemRow[]>([]);
const [loading, setLoading] = useState(true);
@@ -313,31 +324,34 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
}
/>
}
footer={
embedded || !detail ? null : (
<ConfigVersionToolbarMeta emphasis={!isDraft}>
<span>
{t("rebate.editingVersion", {
ns: "config",
version: detail.version_no,
status:
detail.status === "draft"
? t("versionStatus.draft", { ns: "config" })
: detail.status === "active"
? t("versionStatus.active", { ns: "config" })
: t("versionStatus.archived", { ns: "config" }),
})}
</span>
{!isDraft ? (
<ConfigVersionToolbarMetaEmphasis>
{t("rebate.readOnlyHint", { ns: "config" })}
</ConfigVersionToolbarMetaEmphasis>
) : (
<span>{t("versionToolbar.draftEditing", { ns: "config" })}</span>
)}
</ConfigVersionToolbarMeta>
)
}
/>
);
const contextBlock =
embedded || !detail ? null : (
<ConfigContextBanner emphasis={!isDraft}>
{t("rebate.editingVersion", {
ns: "config",
version: detail.version_no,
status:
detail.status === "draft"
? t("versionStatus.draft", { ns: "config" })
: detail.status === "active"
? t("versionStatus.active", { ns: "config" })
: t("versionStatus.archived", { ns: "config" }),
})}
{!isDraft ? (
<>
{" "}
<ConfigContextEmphasis>{t("rebate.readOnlyHint", { ns: "config" })}</ConfigContextEmphasis>
</>
) : null}
</ConfigContextBanner>
);
const fieldsBlock = (
<>
<div className="grid gap-5 sm:grid-cols-3">
@@ -392,10 +406,10 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
</div>
<div className="flex items-center justify-between gap-3 rounded-xl border border-border/60 px-4 py-3">
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
{t("rebate.winEnjoy.label", { ns: "config" })}
</Label>
<Switch id="win-enjoy" checked disabled aria-label={t("rebate.winEnjoy.label", { ns: "config" })} />
<p className="text-sm font-medium">{t("rebate.winEnjoy.label", { ns: "config" })}</p>
<AdminStatusBadge status="enabled">
{t("system.states.enabled", { ns: "config" })}
</AdminStatusBadge>
</div>
{!embedded ? (
@@ -415,8 +429,7 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
if (embedded) {
return (
<div className="space-y-6">
{contextBlock}
<div className="space-y-4">
{fieldsBlock}
<ConfirmDialog />
</div>
@@ -427,7 +440,6 @@ export function RebateConfigDocScreen({ embedded = false }: RebateConfigDocScree
<ConfigDocPage
title={t("nav.items.rebate", { ns: "config" })}
toolbar={toolbarBlock}
context={contextBlock}
>
{fieldsBlock}
<ConfirmDialog />