Files
pikasTech-unidesk/scripts/decision-center-workspace-contract-test.ts
T
Codex 2fcdc26ce4 feat(decision-center): add文书工作台 three-panel workspace (issue #51)
- Add DocTypeTree left panel with type grouping (DCSN|GOAL|PLAN|...) and tag grouping
- Add DocWorkspace with three-column layout: tree (260px) | list | detail (~50%)
- Add DocDetailSidebar with read/edit mode toggle, full markdown display
- Add DocDetailHeader with doc-no, title, type, priority, status, evidence links
- Add DocEditor for editing title, type, level, status, tags, body
- Extract doc-no from title prefix (e.g. DCSN-2025-001) and doc-no:* tag
- Support collapsible left/right panels for narrow screens
- Add decision-center-workspace-contract-test.ts for doc-no extraction,
  tree grouping, selection, detail display, edit sync
- Add .doc-workspace, .doc-sidebar, .doc-type-tree, .doc-tree-* CSS
2026-05-21 07:12:40 +00:00

159 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { readFileSync } from "node:fs";
type JsonRecord = Record<string, unknown>;
function assertCondition(condition: unknown, message: string, detail: JsonRecord = {}): void {
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
}
function source(path: string): string {
return readFileSync(path, "utf8");
}
function includesAll(text: string, snippets: string[]): boolean {
return snippets.every((snippet) => text.includes(snippet));
}
export function runDecisionCenterWorkspaceContract(): JsonRecord {
const frontend = source("src/components/frontend/src/decision-center.tsx");
const css = source("src/components/frontend/public/style.css");
assertCondition(
includesAll(frontend, [
"function extractDocNo(record: any): string",
"function buildDocTypeTree(records: any[]): Array<{ type: DocTypeCode",
"function buildTagGroups(records: any[]): Array<{ tag: string",
"DOC_TYPE_CODES = [\"DCSN\", \"GOAL\", \"PLAN\", \"RPRT\", \"ACTN\", \"ISSU\", \"RETR\", \"RQST\", \"RESP\", \"MINS\"]",
"docTypeLabels: Record<DocTypeCode, string>",
]),
"frontend must implement doc-no extraction, doc-type tree, and tag grouping",
);
assertCondition(
includesAll(frontend, [
"function DocTreeNode({ record, depth, onSelect, selectedId }: AnyRecord)",
"function DocTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)",
"function TagTreeGroup({ group, onSelect, selectedId, defaultOpen }: AnyRecord)",
"function DocTypeTree({ records, onSelect, selectedId, activeGrouping }: AnyRecord)",
]),
"frontend must implement DocTreeNode, DocTreeGroup, TagTreeGroup, and DocTypeTree components",
);
assertCondition(
includesAll(frontend, [
"function DocDetailSidebar({ record, onSelect, onSave, saving, error, saveMsg, onRaw }: AnyRecord)",
"function DocEditor({ form, saving, onChange, onSubmit }: AnyRecord)",
"function DocViewer({ record }: AnyRecord)",
"function DocDetailHeader({ record, onBack }: AnyRecord)",
]),
"frontend must implement DocDetailSidebar, DocEditor, DocViewer, and DocDetailHeader",
);
assertCondition(
includesAll(frontend, [
"function DocWorkspace({ records, selectedRecord, onSelect, onSave, saving, saveError, saveMsg, onRaw }: AnyRecord)",
"h(DocWorkspace, {",
"activeView === \"workspace\"",
"decision-tab-workspace",
]),
"frontend must implement DocWorkspace component and workspace tab",
);
assertCondition(
includesAll(frontend, [
"mode === \"view\"",
"mode === \"edit\"",
"setMode(\"view\")",
"setMode(\"edit\")",
"doc-sidebar-mode-tabs",
]),
"frontend must implement read/edit mode toggle in DocDetailSidebar",
);
assertCondition(
includesAll(frontend, [
"selectedRecord?.id",
"setState((prev: any) => ({ ...prev, selectedDoc: record",
"state.selectedDoc",
]),
"frontend must track selectedDoc in state and sync on selection",
);
assertCondition(
includesAll(frontend, [
"await onSave(form)",
"recordPayloadFromForm(form)",
"setRecordSaveState({ saving: false, error: \"\", message:",
"setState((prev: any) => ({ ...prev, selectedDoc: saved",
]),
"frontend must call onSave, payload conversion, error handling, and sync selectedDoc after save",
);
assertCondition(
css.includes(".doc-workspace"),
"CSS must include .doc-workspace layout",
);
assertCondition(
css.includes(".doc-sidebar"),
"CSS must include .doc-sidebar",
);
assertCondition(
css.includes("grid-template-columns: minmax(200px, 260px) minmax(0, 1fr) minmax(0, 50%)"),
"CSS must implement three-column layout with right sidebar at 50%",
);
assertCondition(
includesAll(css, [
".doc-type-tree",
".doc-tree-group",
".doc-tree-node",
".doc-list-item",
".doc-full-markdown",
".doc-editor-form",
]),
"CSS must include all workspace component styles",
);
assertCondition(
includesAll(frontend, [
"extractDocNo(record)",
"title.match(/^([A-Z]{2,5})[-]?(\\d+)/u)",
"doc-no:([A-Z]{2,5})[-]?(\\d+)",
]),
"extractDocNo must extract from title prefix and doc-no tag",
);
assertCondition(
includesAll(frontend, [
"grouping === \"type\"",
"grouping === \"tag\"",
"setGrouping(\"type\")",
"setGrouping(\"tag\")",
]),
"workspace must support type and tag grouping toggle",
);
return {
ok: true,
checks: [
"doc-no-extraction-functions",
"doc-type-tree-components",
"doc-detail-sidebar-components",
"doc-workspace-component",
"read-edit-mode-toggle",
"selected-doc-state-sync",
"save-and-sync",
"workspace-css-layout",
"workspace-css-components",
"three-column-layout-50-percent",
"doc-no-regex-from-title",
"type-tag-grouping-toggle",
],
};
}
if (import.meta.main) {
process.stdout.write(`${JSON.stringify(runDecisionCenterWorkspaceContract(), null, 2)}\n`);
}