fix(report): make density facts readable and printable
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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user