fix(awooop): compact stockplatform receipt panel
All checks were successful
CD Pipeline / workflow-shape (push) Successful in 1s
CD Pipeline / cancel-stale-cd (push) Has been skipped
CD Pipeline / tests (push) Successful in 55s
CD Pipeline / build-and-deploy (push) Successful in 4m54s
CD Pipeline / post-deploy-checks (push) Successful in 1m46s

This commit is contained in:
Your Name
2026-07-02 18:57:31 +08:00
parent 0b61b6fe4d
commit fe8318170d
3 changed files with 178 additions and 33 deletions

View File

@@ -9183,21 +9183,50 @@
"stockplatformP0Receipt": {
"eyebrow": "P0-006 Runtime receipt",
"title": "StockPlatform data dependency readback",
"subtitle": "Shows the public API data readiness receipt, dependency classification, and next controlled step.",
"subtitle": "The public endpoints are receipted; the blocker is now source contract readback plus the public verifier.",
"preflight": "Controlled preflight",
"stage": "Current stage",
"next": "Current next step",
"empty": "Waiting for priority readback",
"details": "Show technical details",
"rawNext": "Raw order signal",
"metrics": {
"runtime": "Runtime",
"receipt": "Receipt",
"dataDependency": "Data dependency",
"postgres": "Postgres"
},
"metricDetails": {
"runtime": "API is readable; data is not green",
"receipt": "Converted into machine-readable receipt",
"dataDependency": "Source contract verifier is still missing",
"postgres": "No manual DB write to fake green"
},
"values": {
"received": "received",
"missing": "missing",
"ready": "ready",
"notReady": "not ready"
"notReady": "not ready",
"blocked": "blocked",
"review": "review",
"sourceContractGap": "source contract gap",
"postgresGap": "Postgres gap"
},
"actions": {
"sourceContractReadback": "Run source contract readback",
"productionMigrationReadback": "Run migration control readback",
"runtimeReconcile": "Restore runtime control readback",
"monitor": "Keep monitoring"
},
"stages": {
"sourceContract": "Source contract",
"migration": "Migration path",
"runtime": "Runtime path"
},
"chips": {
"publicProbe": "public probe",
"readOnlyDiff": "read-only diff",
"publicVerifier": "public verifier"
},
"boundaries": {
"publicHttps": "public HTTPS only",

View File

@@ -9183,21 +9183,50 @@
"stockplatformP0Receipt": {
"eyebrow": "P0-006 Runtime receipt",
"title": "StockPlatform 資料依賴回讀",
"subtitle": "顯示公開 API 回讀出的 data readiness receipt、資料依賴分類與下一個受控步驟。",
"subtitle": "公開端點已收件;目前卡在資料來源合約與 public verifier。",
"preflight": "Controlled preflight",
"stage": "目前階段",
"next": "目前下一步",
"empty": "等待 priority readback",
"details": "展開技術細節",
"rawNext": "原始排序訊號",
"metrics": {
"runtime": "Runtime",
"receipt": "Receipt",
"dataDependency": "Data dependency",
"postgres": "Postgres"
},
"metricDetails": {
"runtime": "API 可讀,資料仍未綠燈",
"receipt": "已轉成機器可讀 receipt",
"dataDependency": "缺資料來源合約驗證",
"postgres": "不靠手寫 DB 造綠燈"
},
"values": {
"received": "已收件",
"missing": "缺收件",
"ready": "ready",
"notReady": "not ready"
"notReady": "not ready",
"blocked": "blocked",
"review": "review",
"sourceContractGap": "來源合約缺口",
"postgresGap": "Postgres 缺口"
},
"actions": {
"sourceContractReadback": "補資料來源合約回讀",
"productionMigrationReadback": "跑 migration 控制通道回讀",
"runtimeReconcile": "補 runtime 控制通道回讀",
"monitor": "持續監控"
},
"stages": {
"sourceContract": "Source contract",
"migration": "Migration path",
"runtime": "Runtime path"
},
"chips": {
"publicProbe": "public probe",
"readOnlyDiff": "read-only diff",
"publicVerifier": "public verifier"
},
"boundaries": {
"publicHttps": "public HTTPS only",

View File

@@ -8710,14 +8710,14 @@ function AiLoopLogSourceTagsPanel({
);
}
function stockplatformReceiptTone(value: string | boolean | null | undefined) {
function stockplatformCompactTone(value: string | boolean | null | undefined) {
if (value === true || value === "ready" || String(value).includes("_ready")) {
return "border-[#b9d9c2] bg-[#f2fbf3] text-[#236332]";
}
if (String(value).includes("blocked") || value === false) {
return "border-[#f0c6a8] bg-[#fff8f1] text-[#9a4d16]";
}
return "border-[#d8d3c7] bg-[#faf9f3] text-[#5f5b52]";
return "border-[#c9d8ea] bg-[#eef5ff] text-[#1f5b9b]";
}
function StockPlatformP0ReceiptPanel({
@@ -8763,30 +8763,70 @@ function StockPlatformP0ReceiptPanel({
evidence?.stockplatform_postgres_readiness_or_data_source_postgres_ready ??
false;
const nextOrder = priority?.next_execution_order?.[0] ?? "";
const runtimeLabel = runtimeStatus.includes("_ready")
? t("values.ready")
: t("values.blocked");
const receiptLabel = receiptProvided ? t("values.received") : t("values.missing");
const dataDependencyLabel =
dataDependency === "freshness_or_ingestion_not_ready"
? t("values.sourceContractGap")
: dataDependency === "postgres_not_ready"
? t("values.postgresGap")
: dataDependency === "none"
? t("values.ready")
: t("values.review");
const postgresLabel = postgresReady ? t("values.ready") : t("values.notReady");
const nextActionLabel =
dataDependency === "freshness_or_ingestion_not_ready"
? t("actions.sourceContractReadback")
: dataDependency === "postgres_not_ready"
? t("actions.productionMigrationReadback")
: runtimeStatus.includes("runtime_drift")
? t("actions.runtimeReconcile")
: t("actions.monitor");
const nextActionStage =
dataDependency === "freshness_or_ingestion_not_ready"
? t("stages.sourceContract")
: dataDependency === "postgres_not_ready"
? t("stages.migration")
: t("stages.runtime");
const visibleChips = [
t("chips.publicProbe"),
t("chips.readOnlyDiff"),
t("chips.publicVerifier"),
];
const metrics = [
{
key: "runtime",
label: t("metrics.runtime"),
value: runtimeStatus,
tone: stockplatformReceiptTone(runtimeStatus),
value: runtimeLabel,
detail: t("metricDetails.runtime"),
icon: Activity,
tone: stockplatformCompactTone(runtimeStatus),
},
{
key: "receipt",
label: t("metrics.receipt"),
value: receiptProvided ? t("values.received") : t("values.missing"),
tone: stockplatformReceiptTone(receiptProvided),
value: receiptLabel,
detail: t("metricDetails.receipt"),
icon: CheckCircle2,
tone: stockplatformCompactTone(receiptProvided),
},
{
key: "data",
label: t("metrics.dataDependency"),
value: dataDependency,
tone: stockplatformReceiptTone(readbackStatus),
value: dataDependencyLabel,
detail: t("metricDetails.dataDependency"),
icon: SearchCheck,
tone: stockplatformCompactTone(readbackStatus),
},
{
key: "postgres",
label: t("metrics.postgres"),
value: postgresReady ? t("values.ready") : t("values.notReady"),
tone: stockplatformReceiptTone(postgresReady),
value: postgresLabel,
detail: t("metricDetails.postgres"),
icon: Database,
tone: stockplatformCompactTone(postgresReady),
},
];
@@ -8813,43 +8853,60 @@ function StockPlatformP0ReceiptPanel({
</div>
<span
className={cn(
"border px-3 py-2 font-mono text-[11px] font-semibold",
stockplatformReceiptTone(readbackStatus)
"border px-3 py-2 text-[11px] font-semibold",
stockplatformCompactTone(readbackStatus)
)}
>
{loading ? "--" : readbackStatus}
{loading ? "--" : dataDependencyLabel}
</span>
</div>
<div className="mt-4 grid gap-2 sm:grid-cols-2 xl:grid-cols-4">
{metrics.map((metric) => (
<div key={metric.key} className={cn("border px-3 py-2", metric.tone)}>
<div className="text-[11px] font-semibold">{metric.label}</div>
<div className="mt-1 break-words font-mono text-xs font-semibold text-[#141413]">
{loading ? "--" : metric.value}
{metrics.map((metric) => {
const Icon = metric.icon;
return (
<div key={metric.key} className={cn("border px-3 py-2", metric.tone)}>
<div className="flex items-center justify-between gap-2">
<div className="text-[11px] font-semibold">{metric.label}</div>
<Icon className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
</div>
<div className="mt-2 text-sm font-semibold text-[#141413]">
{loading ? "--" : metric.value}
</div>
<div className="mt-1 text-[11px] leading-4 text-[#5f5b52]">
{metric.detail}
</div>
</div>
</div>
))}
);
})}
</div>
<div className="mt-3 grid gap-2 border border-[#c9d8ea] bg-[#eef5ff] p-3 lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.8fr)]">
<div className="mt-3 grid gap-3 border border-[#c9d8ea] bg-[#eef5ff] p-3 lg:grid-cols-[minmax(0,0.75fr)_minmax(0,1.4fr)]">
<div className="min-w-0">
<div className="text-[11px] font-semibold text-[#1f5b9b]">
{t("preflight")}
{t("stage")}
</div>
<div className="mt-1 break-words font-mono text-xs font-semibold text-[#141413]">
{loading ? "--" : preflightStatus}
</div>
<div className="mt-2 break-words font-mono text-[10px] leading-4 text-[#5f5b52]">
{loading ? "--" : recoveryStatus}
<div className="mt-1 flex items-center gap-2 text-sm font-semibold text-[#141413]">
<ArrowRight className="h-4 w-4 shrink-0 text-[#1f5b9b]" aria-hidden="true" />
{loading ? "--" : nextActionStage}
</div>
</div>
<div className="min-w-0">
<div className="text-[11px] font-semibold text-[#1f5b9b]">
{t("next")}
</div>
<div className="mt-1 break-words text-xs leading-5 text-[#141413]">
{loading ? "--" : nextOrder || t("empty")}
<div className="mt-1 text-sm font-semibold leading-5 text-[#141413]">
{loading ? "--" : nextActionLabel}
</div>
<div className="mt-2 flex flex-wrap gap-1.5">
{visibleChips.map((chip) => (
<span
key={chip}
className="border border-[#c9d8ea] bg-white px-2 py-0.5 text-[11px] font-semibold text-[#1f5b9b]"
>
{chip}
</span>
))}
</div>
</div>
</div>
@@ -8865,6 +8922,36 @@ function StockPlatformP0ReceiptPanel({
{t("boundaries.noSecret")}
</span>
</div>
<details className="mt-3 border border-[#d8d3c7] bg-[#faf9f3]">
<summary className="cursor-pointer px-3 py-2 text-xs font-semibold text-[#5f5b52]">
{t("details")}
</summary>
<div className="grid gap-2 border-t border-[#d8d3c7] bg-white p-3 md:grid-cols-2">
<div className="min-w-0">
<div className="text-[11px] font-semibold text-[#77736a]">
{t("preflight")}
</div>
<div className="mt-1 break-all font-mono text-[10px] leading-4 text-[#141413]">
{loading ? "--" : preflightStatus}
</div>
<div className="mt-2 break-all font-mono text-[10px] leading-4 text-[#5f5b52]">
{loading ? "--" : recoveryStatus}
</div>
</div>
<div className="min-w-0">
<div className="text-[11px] font-semibold text-[#77736a]">
{t("rawNext")}
</div>
<div className="mt-1 break-words text-[11px] leading-5 text-[#141413]">
{loading ? "--" : nextOrder || t("empty")}
</div>
<div className="mt-2 break-all font-mono text-[10px] leading-4 text-[#5f5b52]">
{readbackStatus}
</div>
</div>
</div>
</details>
</div>
</section>
);