diff --git a/scripts/governance-baseline.json b/scripts/governance-baseline.json index 401e1e70..3862e898 100644 --- a/scripts/governance-baseline.json +++ b/scripts/governance-baseline.json @@ -2,16 +2,5 @@ "version": 1, "purpose": "Frozen grandfathered debt inventory for the frontend governance checks. New entries and cap increases fail the governance comparison. Existing caps may only decrease and entries must be removed when compliant.", "maxFileLines": 500, - "godfileDebt": [ - { - "path": "src/app/(protected)/workorders/[id].tsx", - "maxLines": 797, - "reason": "Work-order detail route page; legacy godfile targeted for decomposition into focused state components." - }, - { - "path": "src/app/(protected)/vendors/index.tsx", - "maxLines": 612, - "reason": "Vendors list route; legacy page with mixed query/filter/table state pending extraction." - } - ] + "godfileDebt": [] } diff --git a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-draft-actions.tsx b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-draft-actions.tsx new file mode 100644 index 00000000..86749598 --- /dev/null +++ b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-draft-actions.tsx @@ -0,0 +1,70 @@ +import type { RefObject } from "react"; +import { Ban, Save, Sparkles, Upload } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { COMP_DOC_FOOTER_BTN_SX } from "./comp-doc-footer-button-sx"; + +type CompDocDialogFooterDraftActionsProps = { + rowVersion?: string | number | null; + isUploading: boolean; + pdfInputRef: RefObject; + onMarkNotRequired: () => void; + onSaveDraft: () => void; + onGenerate: () => void; +}; + +export function CompDocDialogFooterDraftActions({ + rowVersion, + isUploading, + pdfInputRef, + onMarkNotRequired, + onSaveDraft, + onGenerate, +}: CompDocDialogFooterDraftActionsProps) { + return ( + <> + +
+ + + +
+ + ); +} diff --git a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-generated-actions.tsx b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-generated-actions.tsx new file mode 100644 index 00000000..5c006a97 --- /dev/null +++ b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-generated-actions.tsx @@ -0,0 +1,62 @@ +import type { RefObject } from "react"; +import { CheckCircle2, Download, Upload } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { COMP_DOC_FOOTER_BTN_SX } from "./comp-doc-footer-button-sx"; + +type CompDocDialogFooterGeneratedActionsProps = { + generatedAt: string; + signOffAttachment?: string | null; + isDocumentsReadOnly: boolean; + rowVersion?: string | number | null; + isUploading: boolean; + pdfInputRef: RefObject; + onDownloadPdf: () => void; +}; + +export function CompDocDialogFooterGeneratedActions({ + generatedAt, + signOffAttachment, + isDocumentsReadOnly, + rowVersion, + isUploading, + pdfInputRef, + onDownloadPdf, +}: CompDocDialogFooterGeneratedActionsProps) { + return ( + <> + + {" "} + {signOffAttachment ? "Document on file" : `Generated ${generatedAt}`} + +
+ {!isDocumentsReadOnly && ( + + )} + +
+ + ); +} diff --git a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-not-required-actions.tsx b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-not-required-actions.tsx new file mode 100644 index 00000000..79d16bca --- /dev/null +++ b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer-not-required-actions.tsx @@ -0,0 +1,53 @@ +import { Ban, Sparkles, Undo2 } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { COMP_DOC_FOOTER_BTN_SX } from "./comp-doc-footer-button-sx"; + +type CompDocDialogFooterNotRequiredActionsProps = { + isDocumentsReadOnly: boolean; + onUndo: () => void; +}; + +export function CompDocDialogFooterNotRequiredActions({ + isDocumentsReadOnly, + onUndo, +}: CompDocDialogFooterNotRequiredActionsProps) { + return ( + <> + + Marked{" "} + not required for this work order + +
+ {!isDocumentsReadOnly && ( + + )} + +
+ + ); +} diff --git a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer.tsx b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer.tsx index 4e35987c..b21cbd05 100644 --- a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer.tsx +++ b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-footer.tsx @@ -1,27 +1,48 @@ +import type { RefObject } from "react"; import type { DocState } from "@/domain/work-orders/types/completion-doc"; -import { CompDocDialogFooterDraft } from "./comp-doc-dialog-footer-draft"; -import { CompDocDialogFooterGenerated } from "./comp-doc-dialog-footer-generated"; -import { CompDocDialogFooterNotRequired } from "./comp-doc-dialog-footer-not-required"; +import { CompDocDialogFooterDraftActions } from "./comp-doc-dialog-footer-draft-actions"; +import { CompDocDialogFooterGeneratedActions } from "./comp-doc-dialog-footer-generated-actions"; +import { CompDocDialogFooterNotRequiredActions } from "./comp-doc-dialog-footer-not-required-actions"; type CompDocDialogFooterProps = { docState: DocState; + isDocumentsReadOnly: boolean; + rowVersion?: string | number | null; + isUploading: boolean; + signOffAttachment?: string | null; generatedAt: string; + pdfInputRef: RefObject; onMarkNotRequired: () => void; - onUndo: () => void; onSaveDraft: () => void; onGenerate: () => void; + onUndo: () => void; onDownloadPdf: () => void; + onUploadPdf: (files: FileList | null) => void; }; export function CompDocDialogFooter({ docState, + isDocumentsReadOnly, + rowVersion, + isUploading, + signOffAttachment, generatedAt, + pdfInputRef, onMarkNotRequired, - onUndo, onSaveDraft, onGenerate, + onUndo, onDownloadPdf, + onUploadPdf, }: CompDocDialogFooterProps) { + // Exclusive footer branches (prototype: one docState at a time). A signed PDF + // on file implies Generated even if local docState has not caught up yet — never + // render Draft actions alongside Generated. + const showNotRequired = docState === "not-required"; + const showGenerated = + !showNotRequired && (docState === "generated" || Boolean(signOffAttachment)); + const showDraft = !showNotRequired && !showGenerated && docState === "draft"; + return (
- {docState === "draft" && ( -
); } diff --git a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-header.tsx b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-header.tsx index d014c7e4..08a9cb20 100644 --- a/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-header.tsx +++ b/src/app/(protected)/workorders/_components/comp-doc/comp-doc-dialog-header.tsx @@ -4,12 +4,13 @@ import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-ta import { serviceTitle } from "@/domain/work-orders/utils/completion-doc-service-title"; type CompDocDialogHeaderProps = { - row: WorkOrderTableRow; + info: WorkOrderTableRow; cfg: CompletionDocStateConfig; + isUploading: boolean; onClose: () => void; }; -export function CompDocDialogHeader({ row, cfg, onClose }: CompDocDialogHeaderProps) { +export function CompDocDialogHeader({ info, cfg, isUploading, onClose }: CompDocDialogHeaderProps) { return (
WO{" "} - #{row.woNumber} + #{info.woNumber} {" · "} - {serviceTitle(row)} + {serviceTitle(info)}
@@ -77,8 +78,9 @@ export function CompDocDialogHeader({ row, cfg, onClose }: CompDocDialogHeaderPr