fix: replace raw codes and internal IDs in the console with Chinese labels
Found in v0.2 visual QA (docs/plan/09-visual-qa.md): - actor strings (admin_session:<uuid>, user:<uuid>, system) -> 管理员/系统/API Key in policy revisions, subscriptions, inbox, trash, business tasks, organize detail - cache volume without key shows the downloader name instead of instance:<uuid> - download timeline/plan: task and organize statuses, plan purpose, priority and status reason localized; audit gets labels for capability/files/site/feed actions - organize record reason uses conflict detail (没有可入库的主视频) instead of blocked:no_main_video; slot/candidate reasons drop inflight:<uuid> - identity decision summary no longer shows no_viable_candidate - upstream test: 'Connection error.' classified as network, with a Chinese hint - new policy draft prefills key default (placeholder looked like a value) - dialog/sheet close sr-only text in Chinese Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d7eb6a45d0
commit
46d37d87fe
23 files changed
+176
-43
No files matched your search
@@ -8,18 +8,12 @@ import { FilterBar, FilterSelect } from "~/components/console/filter-bar.tsx";
|
||||
import { JsonView } from "~/components/console/json-view.tsx";
|
||||
import { KvList } from "~/components/console/kv-list.tsx";
|
||||
import { PageHeader } from "~/components/console/page-header.tsx";
|
||||
import { formatRelative } from "~/lib/labels.ts";
|
||||
import { ACTOR_TYPES, actorTypeLabel, formatRelative } from "~/lib/labels.ts";
|
||||
import { formatDateTime } from "~/lib/utils.ts";
|
||||
|
||||
export const meta = () => [{ title: "审计日志 · UHD 媒体自动化控制台" }];
|
||||
|
||||
const ACTOR_TYPES = [
|
||||
{ value: "user", label: "管理员" },
|
||||
{ value: "api_key", label: "API Key" },
|
||||
{ value: "agent", label: "Agent" },
|
||||
{ value: "system", label: "系统" },
|
||||
];
|
||||
const actorLabel = (t: string) => ACTOR_TYPES.find((a) => a.value === t)?.label ?? t;
|
||||
const actorLabel = actorTypeLabel;
|
||||
|
||||
const ACTION_LABEL: Record<string, string> = {
|
||||
"admin.setup": "初始化管理员",
|
||||
@@ -43,6 +37,20 @@ const ACTION_LABEL: Record<string, string> = {
|
||||
"schedule.run_now": "立即执行定时任务",
|
||||
"schedule.skip": "跳过定时任务",
|
||||
"acquisition_plan.manual_submit": "手动提交下载计划",
|
||||
"engine.run_retry": "重跑引擎运行",
|
||||
"capability.invoke": "Agent 调用能力",
|
||||
"site.created": "登记站点",
|
||||
"site.updated": "修改站点",
|
||||
"feed_source.created": "新建 Feed",
|
||||
"identity.release_assigned": "指定资源身份",
|
||||
"schedule.update": "修改定时任务",
|
||||
"files.mkdir": "新建目录",
|
||||
"files.rename": "重命名",
|
||||
"files.move": "移动文件",
|
||||
"files.copy": "复制文件",
|
||||
"files.organize": "整理文件",
|
||||
"files.delete": "删除文件",
|
||||
"files.trash_restore": "从回收站还原",
|
||||
};
|
||||
const VERB: Record<string, string> = { create: "新建", update: "修改", delete: "删除", test: "测试", verify: "校验" };
|
||||
const NOUN: Record<string, string> = {
|
||||
|
||||
@@ -12,8 +12,10 @@ import { consolePath } from "~/components/console/nav.ts";
|
||||
import { PageHeader } from "~/components/console/page-header.tsx";
|
||||
import { StatusBadge } from "~/components/console/status-badge.tsx";
|
||||
import { useQueryState } from "~/components/console/use-query-state.ts";
|
||||
import { PRIORITY } from "~/components/library/labels.ts";
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { relTime } from "~/components/workbench/release-fields.ts";
|
||||
import { planReasonText } from "~/lib/labels.ts";
|
||||
import { formatBytes, formatDateTime } from "~/lib/utils.ts";
|
||||
|
||||
export const meta = () => [{ title: "下载计划 · UHD 媒体自动化控制台" }];
|
||||
@@ -41,7 +43,7 @@ export default function DownloadPlansPage() {
|
||||
cell: (p) => (
|
||||
<div className="min-w-0">
|
||||
<p className="text-foreground">
|
||||
{PURPOSE[p.purpose]} · 优先级 {p.priority}
|
||||
{PURPOSE[p.purpose]} · 优先级 {PRIORITY[p.priority as keyof typeof PRIORITY] ?? p.priority}
|
||||
</p>
|
||||
<p className="truncate font-mono text-meta text-muted-foreground">{p.id.slice(0, 8)}</p>
|
||||
</div>
|
||||
@@ -63,7 +65,7 @@ export default function DownloadPlansPage() {
|
||||
key: "reason",
|
||||
header: "说明",
|
||||
hideBelow: "lg",
|
||||
cell: (p) => <span className="line-clamp-1 text-muted-foreground">{p.statusReason ?? "—"}</span>,
|
||||
cell: (p) => <span className="line-clamp-1 text-muted-foreground">{planReasonText(p.statusReason) ?? "—"}</span>,
|
||||
},
|
||||
{
|
||||
key: "at",
|
||||
@@ -107,7 +109,7 @@ export default function DownloadPlansPage() {
|
||||
open={!!openPlan}
|
||||
onOpenChange={(o) => !o && setOpenPlan(null)}
|
||||
title={openPlan ? `${PURPOSE[openPlan.purpose]}计划` : ""}
|
||||
description={openPlan?.statusReason ?? undefined}
|
||||
description={planReasonText(openPlan?.statusReason) ?? undefined}
|
||||
footer={
|
||||
linkedDownload ? (
|
||||
<Button asChild size="sm">
|
||||
|
||||
@@ -31,6 +31,7 @@ import {
|
||||
} from "~/components/ui/dialog.tsx";
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { relTime } from "~/components/workbench/release-fields.ts";
|
||||
import { actorText } from "~/lib/labels.ts";
|
||||
import { formatBytes, formatDateTime } from "~/lib/utils.ts";
|
||||
|
||||
const DAY = 86_400_000;
|
||||
@@ -160,9 +161,7 @@ export default function FilesTrashPage() {
|
||||
title={`${formatDateTime(r.deletedAt)}${r.actor ? ` · ${r.actor}` : ""}`}
|
||||
>
|
||||
<span className="block whitespace-nowrap">{relTime(r.deletedAt)}</span>
|
||||
{r.actor ? (
|
||||
<span className="block truncate text-meta">{r.actor.replace(/^admin_session:/, "管理员 ")}</span>
|
||||
) : null}
|
||||
{r.actor ? <span className="block truncate text-meta">{actorText(r.actor)}</span> : null}
|
||||
</span>
|
||||
),
|
||||
},
|
||||
|
||||
@@ -26,6 +26,7 @@ import { consolePath } from "~/components/console/nav.ts";
|
||||
import { PageHeader } from "~/components/console/page-header.tsx";
|
||||
import { StatTile } from "~/components/console/stat-tile.tsx";
|
||||
import { StatusBadge } from "~/components/console/status-badge.tsx";
|
||||
import { useVolumeLabel } from "~/components/downloads/cache-volumes.tsx";
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { Switch } from "~/components/ui/switch.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
@@ -302,6 +303,7 @@ function NeedsAttention({ n }: { n: Dashboard["needsAttention"] }) {
|
||||
}
|
||||
|
||||
function CacheSection({ c }: { c: Dashboard["cache"] }) {
|
||||
const volumeLabel = useVolumeLabel();
|
||||
return (
|
||||
<Section title="缓存水位" icon={HardDrive} to={consolePath("downloads")}>
|
||||
{c.volumes.length === 0 ? (
|
||||
@@ -321,7 +323,7 @@ function CacheSection({ c }: { c: Dashboard["cache"] }) {
|
||||
return (
|
||||
<li key={v.volumeKey} className="min-w-0">
|
||||
<div className="flex items-center justify-between gap-2 text-sm">
|
||||
<span className="truncate font-medium">{v.volumeKey}</span>
|
||||
<span className="truncate font-medium">{volumeLabel(v)}</span>
|
||||
<StatusBadge tone={pct === null ? "neutral" : over ? "destructive" : near ? "warning" : "success"}>
|
||||
{pct === null ? "用量未知" : `${pct.toFixed(0)}%${over ? " 已达阈值" : ""}`}
|
||||
</StatusBadge>
|
||||
@@ -441,7 +443,7 @@ function RecentOutput({ items }: { items: Dashboard["recentOutput"] }) {
|
||||
<li key={r.recordId} className="min-w-0 py-2">
|
||||
<div className="flex items-center justify-between gap-3 text-sm">
|
||||
<Link to={consolePath("organize", r.recordId)} className="min-w-0 truncate hover:underline">
|
||||
{r.title ?? "未命名"}
|
||||
{r.title ?? "未关联作品"}
|
||||
</Link>
|
||||
<StatusBadge tone={s.tone}>{s.label}</StatusBadge>
|
||||
</div>
|
||||
|
||||
@@ -20,6 +20,7 @@ import { Button } from "~/components/ui/button.tsx";
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { Textarea } from "~/components/ui/textarea.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { actorText } from "~/lib/labels.ts";
|
||||
import { useSse, useSseStatus } from "~/lib/sse.ts";
|
||||
import { formatDateTime } from "~/lib/utils.ts";
|
||||
|
||||
@@ -249,7 +250,7 @@ function DecisionCard({ d }: { d: Decision }) {
|
||||
</div>
|
||||
) : (
|
||||
<p className="mt-3 text-meta text-muted-foreground">
|
||||
{d.resolvedBy ? `${d.resolvedBy} ` : ""}于 {formatDateTime(d.resolvedAt)}
|
||||
{d.resolvedBy ? `${actorText(d.resolvedBy)} ` : ""}于 {formatDateTime(d.resolvedAt)}
|
||||
{resolution?.optionKey
|
||||
? ` 选择「${d.options.find((o) => o.key === resolution.optionKey)?.label ?? resolution.optionKey}」`
|
||||
: " 关闭"}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { KvList } from "~/components/console/kv-list.tsx";
|
||||
import { PageHeader } from "~/components/console/page-header.tsx";
|
||||
import { StatusBadge, type StatusTone } from "~/components/console/status-badge.tsx";
|
||||
import { Button } from "~/components/ui/button.tsx";
|
||||
import { actorTypeLabel } from "~/lib/labels.ts";
|
||||
import { cn, formatBytes, formatDateTime } from "~/lib/utils.ts";
|
||||
|
||||
const ACTION_LABEL = {
|
||||
@@ -194,7 +195,10 @@ export default function OrganizeRecordPage() {
|
||||
{ label: "开始", value: formatDateTime(r.startedAt) },
|
||||
{ label: "完成", value: formatDateTime(r.finishedAt) },
|
||||
{ label: "用时", value: duration(r.startedAt, r.finishedAt) },
|
||||
{ label: "触发者", value: `${r.actor.type}${r.actor.id ? ` · ${r.actor.id}` : ""}` },
|
||||
{
|
||||
label: "触发者",
|
||||
value: `${actorTypeLabel(r.actor.type)}${r.actor.id && r.actor.type !== "system" ? ` · ${r.actor.id}` : ""}`,
|
||||
},
|
||||
{
|
||||
label: "外部身份",
|
||||
value: r.media?.externalIds.map((e) => `${e.provider}:${e.entityType}:${e.externalId}`).join(", "),
|
||||
|
||||
@@ -55,6 +55,7 @@ import {
|
||||
import { Input } from "~/components/ui/input.tsx";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~/components/ui/select.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { actorText } from "~/lib/labels.ts";
|
||||
import { cn, formatDateTime } from "~/lib/utils.ts";
|
||||
import { legacyRedirectLoader } from "./redirects.ts";
|
||||
|
||||
@@ -317,7 +318,7 @@ function SubscriptionSheet({ sub, onClose }: { sub: Subscription | null; onClose
|
||||
? `上次 ${formatDateTime(sub.lastSearchedAt)}${sub.nextSearchAt ? ` · 下次 ${formatDateTime(sub.nextSearchAt)}` : " · 周期搜索已关"}`
|
||||
: "尚未搜索",
|
||||
},
|
||||
{ label: "创建", value: `${formatDateTime(sub.createdAt)} · ${sub.createdBy ?? "—"}` },
|
||||
{ label: "创建", value: `${formatDateTime(sub.createdAt)} · ${actorText(sub.createdBy)}` },
|
||||
{ label: "策略版本", value: sub.policyRevisionId ?? "按当前已发布策略", mono: !!sub.policyRevisionId },
|
||||
]}
|
||||
/>
|
||||
|
||||
Reference in new issue
Block a user