import { useEffect, useState, type Dispatch, type SetStateAction } from "react"; import { Link, useLocation, useNavigate, useParams } from "react-router-dom"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import { appendContractRegistryCorrection, createContractRegistry, fetchContractRegistryDetail, patchContractRegistry, renewContractRegistry, terminateContractRegistry, type ContractRegistryDetail, type ContractRegistryItem, type ContractRegistryType, type ContractRegistryWritableStatus, } from "../api/contractsRegistry"; import { fetchEmployees } from "../api/employees"; import { fetchProjects, type ConfidentialityLevel } from "../api/projects"; import { useAuth } from "../auth/AuthContext"; import { hasAnyPermission } from "../auth/permissions"; import { ActionConfirmDialog } from "../components/common/ActionConfirmDialog"; import { PageHeader } from "../components/common/PageHeader"; import { StatusBadge } from "../components/common/StatusBadge"; import { usePostSaveNavigation, useUnsavedChangesGuard, } from "../components/common/UnsavedChangesGuard"; import { BusinessDetailActionPanel, BusinessDetailAside, BusinessDetailFact, BusinessDetailFactGrid, BusinessDetailMain, BusinessDetailWorkspace, } from "../components/patterns/BusinessDetailTemplate"; import { MOBILE_STICKY_ACTION_PAGE_CLASS, MobileStickyActionBar, } from "../components/patterns/MobileStickyActionBar"; import { PageScaffold } from "../components/patterns/PageScaffold"; import { SectionPanel } from "../components/patterns/SectionPanel"; import { Button } from "../components/ui/button"; import { Input } from "../components/ui/input"; import { Textarea } from "../components/ui/textarea"; import { CONTRACT_TYPE_OPTIONS, confidentialityLabel, contractStatusLabel, contractTypeLabel, documentStorageLabel, } from "../features/contractsRegistry/presentation"; import { formatDisplayDate, formatDisplayDateTime, formatMoney } from "../lib/displayFormat"; import { buildPcDetailSource, resolvePcDetailSource, type PcDetailNavigationState, } from "../lib/pcDetailNavigation"; import { resolveErrorMessage } from "../lib/resolveErrorMessage"; import { toast } from "sonner"; const CONFIDENTIALITY_OPTIONS: ConfidentialityLevel[] = ["public", "internal", "confidential", "restricted"]; const AMOUNT_PATTERN = /^\d{1,12}(\.\d{1,2})?$/; type ContractEditorState = { contractNo: string; projectId: string; contractType: ContractRegistryType; counterpartyName: string; summary: string; amount: string; currency: string; signedDate: string; effectiveDate: string; expiryDate: string; confidentialityLevel: ConfidentialityLevel; archiveLocation: string; offlineArchiveNo: string; ownerEmployeeId: string; status: ContractRegistryWritableStatus; }; function emptyEditor(ownerEmployeeId = ""): ContractEditorState { return { contractNo: "", projectId: "", contractType: "other", counterpartyName: "", summary: "", amount: "", currency: "CNY", signedDate: "", effectiveDate: "", expiryDate: "", confidentialityLevel: "internal", archiveLocation: "", offlineArchiveNo: "", ownerEmployeeId, status: "active", }; } function editorFromDetail(item: ContractRegistryDetail): ContractEditorState { return { contractNo: item.contractNo, projectId: item.projectId ?? "", contractType: item.contractType, counterpartyName: item.counterpartyName, summary: item.summary, amount: item.amount ?? "", currency: item.currency, signedDate: item.signedDate ?? "", effectiveDate: item.effectiveDate ?? "", expiryDate: item.expiryDate ?? "", confidentialityLevel: item.confidentialityLevel, archiveLocation: item.archiveLocation, offlineArchiveNo: item.offlineArchiveNo ?? "", ownerEmployeeId: item.ownerEmployeeId, status: item.persistedStatus === "draft" ? "draft" : "active", }; } function nextStepText(item: ContractRegistryDetail) { if (item.persistedStatus === "terminated") return "合同已终止,仅保留事实与审计记录。"; if (item.persistedStatus === "archived") return "普通字段已锁定;如需纠错,请追加修正记录。"; if (item.status === "expired") return "合同已过期,可评估续约、归档或终止。"; if (item.persistedStatus === "draft") return "补齐合同信息后,可转为生效合同或归档。"; return "合同处于生效状态,请关注到期和归档安排。"; } function buildPayload(form: ContractEditorState) { return { contractNo: form.contractNo.trim(), projectId: form.projectId || null, contractType: form.contractType, counterpartyName: form.counterpartyName.trim(), summary: form.summary.trim(), amount: form.amount.trim() || null, currency: form.currency.trim().toUpperCase() || "CNY", signedDate: form.signedDate || null, effectiveDate: form.effectiveDate || null, expiryDate: form.expiryDate || null, confidentialityLevel: form.confidentialityLevel, archiveLocation: form.archiveLocation.trim(), offlineArchiveNo: form.offlineArchiveNo.trim() || null, ownerEmployeeId: form.ownerEmployeeId, }; } function validateForm(form: ContractEditorState) { if (!form.contractNo.trim()) throw new Error("请填写合同编号"); if (!form.counterpartyName.trim()) throw new Error("请填写相对方"); if (!form.summary.trim()) throw new Error("请填写合同摘要"); if (!form.archiveLocation.trim()) throw new Error("请填写归档位置"); if (!form.ownerEmployeeId) throw new Error("请选择负责人"); if (form.amount.trim() && !AMOUNT_PATTERN.test(form.amount.trim())) { throw new Error("合同金额最多 12 位整数和 2 位小数"); } } function ContractEditorFields({ form, setForm, projects, employees, creating, }: { form: ContractEditorState; setForm: Dispatch>; projects: Array<{ id: string; projectNo: string; name: string }>; employees: Array<{ id: string; employeeNo: string; name: string }>; creating: boolean; }) { return (
{creating ? ( ) : null}