import { FC, useEffect } from 'react'; import { X, ShieldCheck, FileText, Briefcase, ExternalLink, Info, } from 'lucide-react'; import { UserProfile, DocPermissionRole } from '../../types/docgov'; import { DeliverableItem } from '../../types/workbench'; import { resolveCurrentDocRole } from '../../lib/currentDocRole'; import { DOC_DUTY_BADGE_TESTID, DOC_DUTY_ROW_TESTID, DOC_DUTY_SELECT_TESTID, } from '../../lib/dutyCellAssertion'; interface UserProjectRightsModalProps { isOpen: boolean; onClose: () => void; currentUser: UserProfile; currentProjectTitle: string; deliverables: DeliverableItem[]; activeDocId: string; canAssignLead?: boolean; onSelectDoc: (docId: string) => void; onRoleChangeForDoc?: (docId: string, role: DocPermissionRole) => void; } export const UserProjectRightsModal: FC = ({ isOpen, onClose, currentUser, currentProjectTitle, deliverables, activeDocId, canAssignLead = true, onSelectDoc, onRoleChangeForDoc, }) => { useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { if (e.key === 'Escape' && isOpen) { onClose(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [isOpen, onClose]); if (!isOpen) return null; // Compute stats const permissions = currentUser.projectPermissions || {}; let authorCount = 0; let reviewerCount = 0; let approverCount = 0; let viewerCount = 0; const roleForItem = (item: DeliverableItem): DocPermissionRole => resolveCurrentDocRole({ currentUser, authorId: item.authorId, reviewerId: item.reviewerId, approverId: item.approverId, author: item.author, reviewer: item.reviewer, approver: item.approver, projectPermission: permissions[item.docId], dutiesAuthoritative: item.dutiesAuthoritative, governanceMode: item.governanceMode, isLegacyLocalDoc: item.dutiesAuthoritative !== true && item.governanceMode !== 'controlled' && /^doc-p\d+-/.test(item.docId), }); deliverables.forEach((item) => { const role = roleForItem(item); if (role === 'author') authorCount++; else if (role === 'reviewer') reviewerCount++; else if (role === 'approver') approverCount++; else viewerCount++; }); const getRoleBadge = (role: DocPermissionRole) => { switch (role) { case 'author': return { label: '✍️ 编制负责人', color: '#3B82F6', bg: 'rgba(59, 130, 246, 0.15)', desc: '拥有正文就地编辑、向智能体发起修改、认领与送审权限', }; case 'reviewer': return { label: '🔍 审核负责人', color: '#F59E0B', bg: 'rgba(245, 158, 11, 0.15)', desc: '文档锁定只读,拥有行内批注、评审通过或驳回权限', }; case 'approver': return { label: '👑 批准负责人', color: '#A855F7', bg: 'rgba(168, 85, 247, 0.15)', desc: '终审决策人,拥有最终批准盖章或否决驳回权限', }; default: return { label: '👁️ 查阅者 (只读)', color: '#94A3B8', bg: 'rgba(148, 163, 184, 0.15)', desc: '无该文档编制或评审职责,拥有全局受控版本查阅权', }; } }; return (
{ if (e.target === e.currentTarget) onClose(); }} >
{/* Header */}
当前账号与项目职责权限矩阵
用户身份与职位全局固定 · 在不同项目与交付物中履行独立法定职责
{/* Content Body */}
{/* User Profile Card */}
{currentUser.name.slice(0, 1)}
{currentUser.name} 工号: {currentUser.employeeId || 'SMDF-0821'}
{currentUser.department || '机器人与微创内窥镜研发中心'} · {currentUser.position || '资深医疗器械方案工程师'}
{/* Global Position Pill */}
法定资质有效
ISO 13485 / GMP 认证
{/* Active Project Banner & Stats */}
当前激活项目:{currentProjectTitle} 交付物总计: {deliverables.length} 份
{/* Quick Stats Badges */}
待我编制 {authorCount} 份
待我审核 {reviewerCount} 份
待我批准 {approverCount} 份
查阅权限 {viewerCount} 份
{/* Document Responsibilities Table */}
本项目的交付物分工与法定操作权限
{deliverables.map((item, idx) => { const role = roleForItem(item); const badge = getRoleBadge(role); const isCurrentActive = item.docId === activeDocId; return ( ); })}
阶段 受控交付物 您在此文档的法定职责 快捷操作
{item.stage}
{item.title} {isCurrentActive && ( 当前打开 )}
状态: {item.lifecycleState}
{onRoleChangeForDoc ? ( ) : ( {badge.label} )}
{/* Explanatory Tip */}
权限与操作联动说明: 当您切换查看某份文档时,右侧工作栏将实时根据您在该文档上的法定职责赋予对应权限——如在《注册评估》中作为编制人可就地编辑与送审;在《竞品调研》中作为审核人则只读锁定并开放批注与通过/驳回动作。
{/* Footer */}
); };