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
@@ -264,3 +264,19 @@ export const opts = <K extends string>(rec: Record<K, string | { label: string }
|
||||
const v = rec[value];
|
||||
return { value, label: typeof v === "string" ? v : v.label };
|
||||
});
|
||||
|
||||
/** 版本目标 / 候选的原因串(如 `blocked:no_main_video`、`deferred:slot_busy;inflight:<id>`)→ 中文;不认识的原样显示 */
|
||||
const SLOT_REASON: Record<string, string> = {
|
||||
"blocked:no_main_video": "没有可入库的主视频",
|
||||
"deferred:slot_busy": "已有计划在途,等它结束再评估",
|
||||
verified: "已核验入库",
|
||||
verified_partial: "部分核验入库",
|
||||
hard_conditions_met: "硬条件全部满足",
|
||||
};
|
||||
export function slotReasonText(raw: string): string {
|
||||
return raw
|
||||
.split(";")
|
||||
.filter((p) => !p.startsWith("inflight:"))
|
||||
.map((p) => SLOT_REASON[p] ?? p)
|
||||
.join(";");
|
||||
}
|
||||
@@ -2,7 +2,7 @@ import type { AcquisitionSlot, CandidateEvaluation } from "@umt/contracts";
|
||||
import { Link2 } from "lucide-react";
|
||||
import type { ReactNode } from "react";
|
||||
import { formatDateTime } from "~/lib/utils.ts";
|
||||
import { CANDIDATE_DECISION, SLOT_STATUS } from "./labels.ts";
|
||||
import { CANDIDATE_DECISION, SLOT_STATUS, slotReasonText } from "./labels.ts";
|
||||
import { Chip, ChipList, Tone } from "./parts.tsx";
|
||||
import { summarizeCompletion, summarizeHard, summarizeSoft, summarizeUpgrade } from "./slot-rules.tsx";
|
||||
|
||||
@@ -45,7 +45,9 @@ export function SlotCard({
|
||||
<span className="ml-auto text-meta text-muted-foreground tabular-nums">r{slot.revision}</span>
|
||||
{actions}
|
||||
</header>
|
||||
{slot.statusReason ? <p className="mt-2 text-caption text-muted-foreground">{slot.statusReason}</p> : null}
|
||||
{slot.statusReason ? (
|
||||
<p className="mt-2 text-caption text-muted-foreground">{slotReasonText(slot.statusReason)}</p>
|
||||
) : null}
|
||||
<dl className="mt-3 grid gap-3 text-sm sm:grid-cols-2">
|
||||
<div className="min-w-0">
|
||||
<dt className="mb-1 text-meta text-muted-foreground">硬条件</dt>
|
||||
@@ -94,7 +96,9 @@ export function SlotCard({
|
||||
</ul>
|
||||
) : null}
|
||||
{c.reasons.length ? (
|
||||
<p className="mt-1 text-meta break-words text-muted-foreground">{c.reasons.join(";")}</p>
|
||||
<p className="mt-1 text-meta break-words text-muted-foreground">
|
||||
{c.reasons.map(slotReasonText).filter(Boolean).join(";")}
|
||||
</p>
|
||||
) : null}
|
||||
<p className="text-micro text-muted-foreground tabular-nums">评估于 {formatDateTime(c.evaluatedAt)}</p>
|
||||
</li>
|
||||
|
||||
@@ -30,6 +30,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "~
|
||||
import { Switch } from "~/components/ui/switch.tsx";
|
||||
import { Textarea } from "~/components/ui/textarea.tsx";
|
||||
import { errorMessage } from "~/lib/api.ts";
|
||||
import { actorText } from "~/lib/labels.ts";
|
||||
import { cn, formatDateTime } from "~/lib/utils.ts";
|
||||
import { CANDIDATE_DECISION, MEDIA_KIND, POLICY_STATUS } from "./labels.ts";
|
||||
import { ChipList, ChipToggleGroup, Panel, Tone } from "./parts.tsx";
|
||||
@@ -80,7 +81,8 @@ function draftFromRevision(r: PolicyRevision | null): PolicyDraft {
|
||||
const rules = r ? AcquisitionPolicyRules.safeParse(r.rules).data : undefined;
|
||||
const kinds = r ? z.array(z.string()).safeParse((r.scope as { mediaKinds?: unknown }).mediaKinds).data : undefined;
|
||||
return {
|
||||
policyKey: r?.policyKey ?? "",
|
||||
// 新草案预填 default:占位符看起来像已填的值,留空提交会报"填写策略键"
|
||||
policyKey: r?.policyKey ?? "default",
|
||||
title: r?.title ?? "",
|
||||
summary: r?.summary ?? "",
|
||||
scopeKinds: kinds ?? [],
|
||||
@@ -293,7 +295,7 @@ function PolicyGroup({
|
||||
}
|
||||
description={
|
||||
published
|
||||
? `当前生效 r${published.revision} · ${formatDateTime(published.publishedAt)} 由 ${published.publishedBy ?? "—"} 发布`
|
||||
? `当前生效 r${published.revision} · ${formatDateTime(published.publishedAt)} 由 ${actorText(published.publishedBy)} 发布`
|
||||
: "未发布,不会自动执行"
|
||||
}
|
||||
actions={
|
||||
@@ -492,10 +494,12 @@ function PolicyDetailSheet({
|
||||
items={[
|
||||
{ label: "状态", value: <Tone v={POLICY_STATUS[rev.status]} /> },
|
||||
{ label: "作用范围", value: scopeText(rev.scope) },
|
||||
{ label: "创建", value: `${formatDateTime(rev.createdAt)} · ${rev.createdBy ?? "—"}` },
|
||||
{ label: "创建", value: `${formatDateTime(rev.createdAt)} · ${actorText(rev.createdBy)}` },
|
||||
{
|
||||
label: "发布",
|
||||
value: rev.publishedAt ? `${formatDateTime(rev.publishedAt)} · ${rev.publishedBy ?? "—"}` : "未发布",
|
||||
value: rev.publishedAt
|
||||
? `${formatDateTime(rev.publishedAt)} · ${actorText(rev.publishedBy)}`
|
||||
: "未发布",
|
||||
},
|
||||
{ label: "说明", value: rev.summary, wide: true },
|
||||
]}
|
||||
|
||||
Reference in new issue
Block a user