From 7f3538da229fcc59b4e251928d2f126a9325cedd Mon Sep 17 00:00:00 2001 From: weichen Date: Sat, 17 Oct 2026 02:32:45 +0000 Subject: [PATCH] Add review decisions, rule binding and package download to the render workspace Why: Task 11 requires the document render workspace to offer approve, reject and final decisions, rule bind/unbind for document, document type and project default, and the full delivery package, so reviewers and settings holders no longer need other screens. Evidence: vitest 38 files / 419 tests passed (new test/document-render-actions.test.ts covers decision entries from frozen duties, decision payloads without identity fields, package availability, refusal codes, binding candidates and payloads, perm gating and SSR visibility); tsc --noEmit clean; knip unused exports 51 <= baseline 51. Impact: decision entries show only when the command is enabled and the frozen submission lists the session user; binding needs rdm.perm.check project.settings allowed=true; package appears only for ready deliveries; organization-default binding is not offered from the workspace (domain admin only). Refs: unified renderer plan Task 11, 05 section 9, 06 section 6, 07 sections 1 and 5; RDM plugins/rdm/plugin.go review.doc.* and render.profile.* schemas (7169944, 4b7ac2a, 75ba50c). Co-authored-by: Cursor --- src/components/DocumentRenderActions.tsx | 125 ++++++++++ src/components/DocumentRenderWorkspace.tsx | 97 +++++--- src/components/renderFeedback.tsx | 33 +++ src/documentRenderPresentation.ts | 211 +++++++++++++++- src/styles.css | 5 + test/document-render-actions.test.ts | 267 +++++++++++++++++++++ test/document-render-workspace.test.ts | 8 +- 7 files changed, 710 insertions(+), 36 deletions(-) create mode 100644 src/components/DocumentRenderActions.tsx create mode 100644 src/components/renderFeedback.tsx create mode 100644 test/document-render-actions.test.ts diff --git a/src/components/DocumentRenderActions.tsx b/src/components/DocumentRenderActions.tsx new file mode 100644 index 0000000..5b10930 --- /dev/null +++ b/src/components/DocumentRenderActions.tsx @@ -0,0 +1,125 @@ +// 统一渲染 Task 11:整文审签与规则绑定面板。纯展示:入口、载荷与校验来自 documentRenderPresentation, +// 异步操作状态由工作区唯一持有并经 props 注入;入口只在服务端允许(能力 ∧ 冻结职责/项目设置权限)时出现。 +import { useState } from 'react' +import { Btn, Chip, SectionLabel } from '@ui' +import { + BIND_SOURCE_LABELS, validateReviewText, ruleLabel, + type BindSource, type BindableRule, type DecisionEntries, type DecisionKind, type DocSubmissionView, type RuleRef, +} from '../documentRenderPresentation' +import { OperationStatus, type OperationState } from './renderFeedback' + +export const ACTION_OP_LABELS: Record = { + approve: '整文审核通过', reject: '整文驳回', final: '整文终批', bind: '绑定规则', unbind: '解绑规则', +} + +const DECISION_LABELS: Record = { approve: '审核通过', reject: '驳回', final: '终批' } +const SUBMISSION_STATE: Record = { + submitted: { label: '审签中', tone: 'info' }, + rejected: { label: '已驳回', tone: 'danger' }, + final: { label: '已终批', tone: 'ok' }, +} + +export function ReviewDecisionPanel({ sub, entries, busy, status, onRetry, onDecide }: { + sub: DocSubmissionView + entries: DecisionEntries + busy: boolean + status: OperationState + onRetry: () => void + onDecide: (kind: DecisionKind, text: string) => void +}) { + const [text, setText] = useState('') + const [hint, setHint] = useState('') + const reviewerIds = Object.keys(sub.reviews) + const approvedCount = reviewerIds.filter(id => sub.reviews[id] === true).length + const stateView = SUBMISSION_STATE[sub.state] ?? { label: '状态未知', tone: 'plain' as const } + const anyEntry = entries.approve || entries.reject || entries.final + + const decide = (kind: DecisionKind) => { + // 驳回必须给出原因;审核通过与终批的说明可选(服务端同样可选)。 + if (kind === 'reject') { + const bad = validateReviewText(text) + if (bad) { setHint(bad); return } + } + setHint('') + onDecide(kind, text) + } + + return ( + <> +
+ 整文审签 + + {stateView.label} + 第 {sub.round} 轮 · 审核 {approvedCount}/{reviewerIds.length} + +
+ {anyEntry && ( + <> +