diff --git a/web/src/pages/MyKbs.tsx b/web/src/pages/MyKbs.tsx
index e767ffb16..7e57bd7c8 100644
--- a/web/src/pages/MyKbs.tsx
+++ b/web/src/pages/MyKbs.tsx
@@ -25,17 +25,16 @@ import {
Td,
Th,
Tr,
+ localDate,
} from "../ui";
import { fetchMe } from "../authSession";
-const ymd = (iso: string) => iso.slice(0, 10);
-
function joinInfo(row: MyKb): string {
if (row.my_role === "owner") return S.account.deploymentAdmin;
if (row.joined_at) {
return row.added_by_name
- ? S.account.addedBy(row.added_by_name, ymd(row.joined_at))
- : S.account.joinedOn(ymd(row.joined_at));
+ ? S.account.addedBy(row.added_by_name, localDate(row.joined_at))
+ : S.account.joinedOn(localDate(row.joined_at));
}
return S.account.openToEveryone;
}
diff --git a/web/src/pages/Review.tsx b/web/src/pages/Review.tsx
index 86ade8975..88e3a1eb7 100644
--- a/web/src/pages/Review.tsx
+++ b/web/src/pages/Review.tsx
@@ -48,6 +48,7 @@ import {
SearchSelect,
Status,
cn,
+ localDate,
type ChipTone,
} from "../ui";
@@ -478,7 +479,7 @@ function MergeRow({
? S.review.mergedBy(merge.merged_by_name)
: S.review.mergedByAi}
{" · "}
- {merge.created_at.slice(0, 10)}
+ {localDate(merge.created_at)}
{merge.reason ? ` · ${escalationText(merge.reason)}` : ""}
@@ -523,7 +524,7 @@ function precedentText(p: AgentPrecedent): string {
: p.action === "review.keep"
? S.review.agentPrecedentKept
: S.review.agentPrecedentMerged;
- const line = `${p.left} ≟ ${p.right} · ${verb} · ${p.at.slice(0, 10)}`;
+ const line = `${p.left} ≟ ${p.right} · ${verb} · ${localDate(p.at)}`;
// 人写的那一句(0026)跟在后面:先例不只是结果
return p.why ? `${line} · “${p.why}”` : line;
}
@@ -596,8 +597,8 @@ function AgentRow({
{d.decided_by_name
- ? S.review.agentAnsweredBy(d.decided_by_name, (d.decided_at ?? d.created_at).slice(0, 10))
- : d.created_at.slice(0, 10)}
+ ? S.review.agentAnsweredBy(d.decided_by_name, localDate(d.decided_at ?? d.created_at))
+ : localDate(d.created_at)}
{/* 回答 agent 也能带一句理由(0026)——它走的正是人的裁决路径 */}
@@ -691,7 +692,7 @@ function DecisionRow({ e }: { e: ReviewHistoryEvent }) {
{e.actor_name ?? S.review.aiActor}
{" · "}
- {e.created_at.slice(0, 10)}
+ {localDate(e.created_at)}
);
diff --git a/web/src/pages/RulesPanel.tsx b/web/src/pages/RulesPanel.tsx
index ff3077b97..12fc5b267 100644
--- a/web/src/pages/RulesPanel.tsx
+++ b/web/src/pages/RulesPanel.tsx
@@ -37,6 +37,7 @@ import {
Th,
THead,
Tr,
+ localDate,
} from "../ui";
import { toast } from "../toast";
import { expressionText, metadataOnly, metadataPatch } from "./ruleExpressions";
@@ -307,7 +308,7 @@ function History({ kbId, ruleId, attributes }: { kbId: string; ruleId: string; a
{S.ontology.ruleVersion(v.seq)}
{!v.superseded_at &&
{S.ontology.ruleVersionCurrent}}
- {S.ontology.ruleVersionSince(v.recorded_at.slice(0, 10), v.superseded_at ? v.superseded_at.slice(0, 10) : null)}
+ {S.ontology.ruleVersionSince(localDate(v.recorded_at), v.superseded_at ? localDate(v.superseded_at) : null)}
{S.ontology.ruleVersionStanding(v.derived_count)}
diff --git a/web/src/pages/Tokens.tsx b/web/src/pages/Tokens.tsx
index c1224628c..72eec8528 100644
--- a/web/src/pages/Tokens.tsx
+++ b/web/src/pages/Tokens.tsx
@@ -27,11 +27,12 @@ import {
Th,
THead,
Tr,
- buttonLike,} from "../ui";
+ buttonLike,
+ localDate,
+} from "../ui";
import { toast } from "../toast";
import { copyAndSay } from "../clipboard";
-const ymd = (iso: string) => iso.slice(0, 10);
const EXPIRY_CHOICES = [30, 90, 365, 0] as const;
/** Claude Code / Claude Desktop 一族的 Streamable HTTP 写法。每个库一个端点(0014:
@@ -164,15 +165,15 @@ function TokenRow({
{t.kb_ids?.length ? S.account.nBases(t.kb_ids.length) : S.account.allBases}
- {t.last_used_at ? ymd(t.last_used_at) : S.account.neverUsed}
+ {t.last_used_at ? localDate(t.last_used_at) : S.account.neverUsed}
|
- {t.expires_at ? ymd(t.expires_at) : S.account.noExpiry}
+ {t.expires_at ? localDate(t.expires_at) : S.account.noExpiry}
|
- {ymd(t.created_at)} |
+ {localDate(t.created_at)} |
{revoked ? (
- {S.account.revokedOn(ymd(t.revoked_at!))}
+ {S.account.revokedOn(localDate(t.revoked_at!))}
) : arm ? (
|