fix(report): make density facts readable and printable
Independent Staging Quality Gate / validate (push) Failing after 11m4s
Independent Staging Quality Gate / publish (push) Skipped

Unify reader cleanup rules, lock writer table guards, and register the exact fictional timestamp collision. Preserve existing ordinary-report safety contracts and source-data gaps.

Validation: report Node 165/165, final safety 29/29, Python 101/101, Chrome 28/28; both PDFs retain all 130 rows. Full Node 3704 tests with the same 91 baseline failures. Privacy test: 62 passed, 1 failed due to 17 protected-file READ_ERRORs; not a green gate. Build, DB, manual checklist and controlled-login gaps remain documented. User explicitly authorized staging push with these gaps disclosed.

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
jesse-ux
2026-09-23 15:26:25 +08:00
co-authored by Claude Code
parent 0d37bec15a
commit a12f2c0d26
27 changed files with 1220 additions and 210 deletions
@@ -1,7 +1,8 @@
"use client";
import { memo } from "react";
import { memo, useEffect, useRef } from "react";
import type { ReportFactTable } from "@/lib/report-fact-table-schema";
import { bindFactTablePrint } from "@/lib/report-fact-table-print";
const STATUS_LABELS = {
multi_system_consensus: "多系统共识",
@@ -12,28 +13,45 @@ const STATUS_LABELS = {
blocked: "暂不可用",
} as const;
function FactTable({ table }: { table: Pick<ReportFactTable, "title" | "columns" | "rows"> }) {
if (!table.rows.length) return null;
return (
<div className="personal-report-fact-table-scroll overflow-x-auto" role="region" aria-label={table.title} tabIndex={0}>
<table>
<caption className="sr-only">{table.title}</caption>
<thead><tr>{table.columns.map((column) => <th scope="col" key={column}>{column}</th>)}</tr></thead>
<tbody>{table.rows.map((row) => (
<tr key={row.sourcePath}>{row.cells.map((cell, index) => (
<td key={index}>{cell === null ? "—" : typeof cell === "boolean" ? (cell ? "是" : "否") : typeof cell === "number" ? String(Number(cell.toFixed(2))) : cell}</td>
))}</tr>
))}</tbody>
</table>
</div>
);
}
/** Server-owned facts, separate from the ordinary Markdown and its TOC state. */
export const ReportFactTables = memo(function ReportFactTables({ tables }: { tables: readonly ReportFactTable[] }) {
const root = useRef<HTMLElement>(null);
useEffect(() => {
if (root.current) return bindFactTablePrint(root.current, window);
}, []);
return (
<section className="personal-report-fact-tables" aria-label="计算数据核对表">
<section ref={root} className="personal-report-fact-tables" aria-label="计算数据核对表">
<h2>计算数据核对表</h2>
{tables.map((table) => (
<details key={table.id}>
<summary>{table.title} · {STATUS_LABELS[table.claimStatus]}</summary>
<p>{table.note}</p>
{table.rows.length > 0 && (
<div className="overflow-x-auto" role="region" aria-label={table.title} tabIndex={0}>
<table>
<caption className="sr-only">{table.title}</caption>
<thead><tr>{table.columns.map((column) => <th scope="col" key={column}>{column}</th>)}</tr></thead>
<tbody>{table.rows.map((row) => (
<tr key={row.sourcePath}>{row.cells.map((cell, index) => (
<td key={index}>{cell === null ? "—" : typeof cell === "boolean" ? (cell ? "是" : "否") : String(cell)}</td>
))}</tr>
))}</tbody>
</table>
</div>
)}
<FactTable table={table} />
{table.subtables?.map((subtable) => (
<section key={subtable.id}>
<h3>{subtable.title}</h3>
{subtable.note && <p>{subtable.note}</p>}
<FactTable table={subtable} />
</section>
))}
</details>
))}
</section>