/** * 对话区里**还在等人回**的卡 —— 工作区据此决定摆什么。 * * decide / entry 发的是对话区的卡(chat_card),不进 `work_order`;它们挂起时 * `runtime` 同样是 `awaiting-event`,与「等外部回音」一个值。工作区要分清这两条岔, * 只能读一眼 `detail.chat`。 * * **录入卡在场时工作区摆占位页,不摆件**(2026-08-28 用户定):建档那一步要人做的事 * 全在对话区那张卡上,工作区里上一拍留下的那份 HTML 件与这件事无关 —— 摆着只会让人 * 以为「要在这份工单上办点什么」。决策卡不同:人要决定的对象常常就是件上那份计划 / 审批稿, * 件得留着给他看。 * * 纯函数,抽出来是为了可测:这段判断写在 JSX 里改一次漏一处(`pendingChatCard` 从前就是内联的)。 */ import type { TaskDetail } from '@contracts'; export type PendingCards = { /** 有未选的决策卡 */ readonly decision: boolean; /** 有未确认的数据录入卡 */ readonly entry: boolean; /** 那张录入卡要落的表的人读名(卡面同一个名字);没有录入卡时缺席 */ readonly entryLedger?: string; }; export function pendingCardsOf(chat: TaskDetail['chat']): PendingCards { let decision = false; let entryLedger: string | undefined; for (const m of chat) { if (m.k === 'decision' && m.chosen === undefined) decision = true; // 多张未回的录入卡,占位页点名最早那一张(对话区也是按时间排的,人先看到它) if (m.k === 'entry' && m.done === undefined && entryLedger === undefined) entryLedger = m.ledgerLabel ?? m.ledger; } return entryLedger === undefined ? { decision, entry: false } : { decision, entry: true, entryLedger }; }