shoc-frontend-new/src/app/(protected)/workorders/[id].tsx
Arthur Bassi 4af826a1e0
feat(work-orders): query broadcast, remove auth bypass, dispatch polish (#100)
* fix(work-orders): address wizard create review findings

[recover] remove malicious eslint payload (was 166c63e4)

* fix(work-orders): remove legacy completedDate EditWorkorder path

[recover] remove malicious eslint payload (was f4e6132b)

* fix(work-orders): use local calendar day for completedDate

Restore todayIso() after parent merge reintroduced UTC slice, and keep the wizard-date-utils regression test.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(work-orders): send wizard status and clear POC notes on site change

Map draft.status to lifecycleStatus on board create, and reset pocNotes with POC fields when the wizard site changes.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(query): scope broadcast channel per account and clear on auth

Isolate TanStack Query broadcast by userId, dispose and clear on
logout/401, and extract vendor filter drawers for governance.

* feat(work-orders): board wizard create with service notes

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(work-orders): enforce single-service wizard create per SH-118

Use single service selection in the wizard and omit extraServices from board create.

* fix(vendors): restore filter drawer labels for e2e and visual CI

Use VendorFilterOptions again and keep the Apply filters footer label expected
by vendor Playwright specs and the committed visual baseline.

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-14 11:52:48 -03:00

101 lines
3.6 KiB
TypeScript

import { useParams } from "react-router";
import { Box } from "@mui/material";
import { QueryErrorPanel } from "@/components/ui/query-error-panel";
import { DetailPageSkeleton } from "./_components/loading/detail-page-skeleton";
import { DispatchCreateModal } from "./_components/dispatch-create-modal";
import { DispatchDetailModal } from "./_components/dispatch-detail-modal";
import { WorkOrderActivityPanel } from "./_components/view/work-order-activity-panel";
import { WorkOrderDispatchesAccordion } from "./_components/view/work-order-dispatches-accordion";
import { WorkOrderVendorEditorDialog } from "./_components/view/work-order-vendor-editor-dialog";
import { WorkOrderViewAccordions } from "./_components/view/work-order-view-accordions";
import { WorkOrderViewDetails } from "./_components/view/work-order-view-details";
import { WorkOrderViewHeader } from "./_components/view/work-order-view-header";
import { useWorkOrderViewPage } from "./_hooks/use-work-order-view-page";
export default function WorkOrderViewPage() {
const { id } = useParams();
const workOrderId = id ?? "";
const view = useWorkOrderViewPage(workOrderId);
if (view.isLoading && !view.wo) {
return <DetailPageSkeleton variant="view" />;
}
if (!view.wo) {
return (
<Box className="p-4">
<QueryErrorPanel
message={view.error instanceof Error ? view.error.message : "Work order not found."}
onRetry={() => void view.refetch()}
/>
</Box>
);
}
return (
<Box className="flex flex-col gap-4 p-4">
<WorkOrderViewHeader
wo={view.wo}
workOrderId={workOrderId}
statusAnchor={view.statusAnchor}
onStatusAnchorChange={view.setStatusAnchor}
onStatusChange={(status) => view.statusMutation.mutate(status)}
onOpenDispatch={() => view.setDispatchOpen(true)}
/>
<WorkOrderViewDetails
wo={view.wo}
users={view.users}
assignAnchor={view.assignAnchor}
onAssignAnchorChange={view.setAssignAnchor}
onAssign={(userId, userName) => view.assignmentMutation.mutate({ userId, userName })}
onUnassign={() => view.assignmentMutation.mutate({ userId: "", userName: "" })}
/>
<WorkOrderViewAccordions wo={view.wo} />
<WorkOrderDispatchesAccordion
wo={view.wo}
onOpenDispatchDetail={view.setDispatchDetailId}
onOpenVendorEditor={view.openVendorEditor}
/>
<WorkOrderActivityPanel
customerComments={view.customerComments}
internalComments={view.internalComments}
auditLog={view.auditLog}
mentionUsers={view.mentionUsers}
isSubmitting={view.commentMutation.isPending}
onSubmitComment={view.submitComment}
/>
{view.dispatchOpen && (
<DispatchCreateModal
workOrder={view.wo}
onClose={() => view.setDispatchOpen(false)}
onDispatched={() => void view.refetch()}
/>
)}
{view.dispatchDetailId != null && (
<DispatchDetailModal
dispatchId={view.dispatchDetailId}
workOrderId={workOrderId}
onClose={() => view.setDispatchDetailId(null)}
onUpdated={() => void view.refetch()}
/>
)}
<WorkOrderVendorEditorDialog
editor={view.vendorEditor}
loading={view.vendorEditorLoading}
error={view.vendorEditorError}
isSaving={view.vendorMutation.isPending}
onClose={() => {
if (!view.vendorMutation.isPending) view.setVendorEditor(null);
}}
onSave={view.saveVendorEditor}
onEditorChange={view.setVendorEditor}
/>
</Box>
);
}