shoc-frontend-new/src/app/(protected)/workorders/index.tsx

168 lines
6.6 KiB
TypeScript

import { useMemo, useState } from "react";
import { Box } from "@mui/material";
import { useNavigate } from "react-router";
import { PageHeader } from "@/components/ui/page-header";
import { QueryErrorPanel } from "@/components/ui/query-error-panel";
import { WorkOrdersListMediaOverlay } from "@/app/(protected)/workorders/_components/list/work-orders-list-media-overlay";
import { WorkOrdersListWizard } from "@/app/(protected)/workorders/_components/list/work-orders-list-wizard";
import { WorkOrdersAdvancedSearchSheet } from "@/app/(protected)/workorders/_components/list/advanced-search/work-orders-advanced-search-sheet";
import { WorkOrdersAdvancedFiltersBanner } from "@/app/(protected)/workorders/_components/list/work-orders-advanced-filters-banner";
import { WorkOrdersFilterBar } from "@/app/(protected)/workorders/_components/list/filter-bar/work-orders-filter-bar";
import { WorkOrdersListHeader } from "@/app/(protected)/workorders/_components/list/work-orders-list-header";
import { WoTable } from "@/app/(protected)/workorders/_components/list/table/wo-table";
import { useWorkOrdersListFilters } from "@/app/(protected)/workorders/_hooks/use-work-orders-list-filters";
import { useWorkOrdersTableData } from "@/app/(protected)/workorders/_hooks/use-work-orders-table-data";
import { useDropdownOptionsByCategory } from "@/domain/settings/dropdown-options/use-cases/use-dropdown-options-by-category";
import {
useWorkOrderLocations,
useWorkOrderVendors,
} from "@/domain/work-orders/use-cases/use-work-order-detail";
import { useWorkOrderUsers } from "@/domain/work-orders/use-cases/use-work-orders-list";
import type { MediaFile } from "@/domain/work-orders/types/work-order-media";
import type { WorkOrderTableRow } from "@/domain/work-orders/types/work-order-table-row";
import { useWorkOrderTableMutations } from "@/domain/work-orders/use-cases/use-work-order-table-mutations";
import { useAuthContext } from "@/providers/auth-context";
export default function WorkOrdersListPage() {
const navigate = useNavigate();
const { user } = useAuthContext();
const [wizardOpen, setWizardOpen] = useState(false);
const [mediaRow, setMediaRow] = useState<WorkOrderTableRow | null>(null);
const [mediaFiles, setMediaFiles] = useState<Record<string, MediaFile[]>>({});
const filters = useWorkOrdersListFilters();
const tableData = useWorkOrdersTableData(filters);
const { patchField } = useWorkOrderTableMutations({
onPatch: tableData.patchRow,
clearPatch: tableData.clearRowPatch,
getRow: (id) => tableData.rows.find((row) => String(row.id) === String(id)),
});
const { data: users = [], isLoading: usersLoading } = useWorkOrderUsers();
const { data: locations = [] } = useWorkOrderLocations();
const { data: vendors = [] } = useWorkOrderVendors();
const { data: problemOptions = [] } = useDropdownOptionsByCategory("Problem");
const pmTypes = useMemo(
() => problemOptions.map((option) => option.value).filter(Boolean),
[problemOptions],
);
const vendorTechs = useMemo(
() => [...new Set(vendors.map((vendor) => vendor.companyName).filter(Boolean))],
[vendors],
);
const totalCount = tableData.totalCount;
const scopeTotalCount = tableData.scopeTotalCount;
const activeMediaRow = useMemo(() => {
if (!mediaRow) return null;
return tableData.rows.find((row) => row.id === mediaRow.id) ?? mediaRow;
}, [mediaRow, tableData.rows]);
return (
<Box className="flex h-full flex-col overflow-hidden p-2.5">
<PageHeader
surface
eyebrow="OPERATIONS"
title="Work Orders"
actions={
<WorkOrdersListHeader
search={filters.search}
onSearchChange={filters.handleSearchChange}
onOpenAdvancedFilters={() => filters.setAdvOpen(true)}
onNewWorkOrder={() => setWizardOpen(true)}
activeFilterCount={filters.activeFilterCount}
isFetching={tableData.isFetching && !tableData.isLoading}
/>
}
/>
<WorkOrdersFilterBar
weekMonday={filters.weekMonday}
onWeekChange={filters.setWeekMonday}
dispatcherIds={filters.dispatcherIds}
onDispatcherChange={filters.setDispatcherIds}
typeFilter={filters.typeFilter}
onTypeChange={filters.setTypeFilter}
users={users}
usersLoading={usersLoading}
currentUserId={user?.id}
filteredCount={totalCount}
scopeTotalCount={scopeTotalCount}
/>
{filters.advApplied != null && (
<WorkOrdersAdvancedFiltersBanner
applied={filters.advApplied}
totalCount={totalCount}
sites={locations}
users={users}
onClearChip={filters.clearAdvancedFilterChip}
onClearAll={filters.clearAdvancedFilters}
/>
)}
{Boolean(tableData.error) && (
<QueryErrorPanel
className="mb-2 shrink-0"
compact
message={
tableData.error instanceof Error
? tableData.error.message
: "Failed to load work orders."
}
onRetry={() => void tableData.refetch()}
/>
)}
<WoTable
tableData={tableData}
users={users}
sites={locations}
vendors={vendors}
pmTypes={pmTypes}
weekMonday={filters.weekMonday}
onWeekChange={filters.setWeekMonday}
onOpenSlideOver={(row, edit) => {
tableData.setSelectedId(row.id);
navigate(edit ? `/workorders/edit/${row.id}` : `/workorders/${row.id}`);
}}
onOpenMedia={setMediaRow}
onClearSearch={() => filters.handleSearchChange("")}
onOpenAdvancedFilters={() => filters.setAdvOpen(true)}
onPatch={patchField}
/>
<WorkOrdersAdvancedSearchSheet
open={filters.advOpen}
onOpenChange={filters.setAdvOpen}
initial={filters.advInitial}
onApply={filters.applyAdvancedFilters}
onClearAll={filters.clearAdvancedFilters}
sites={locations}
users={users}
pmTypes={pmTypes}
vendorTechs={vendorTechs}
/>
<WorkOrdersListWizard
open={wizardOpen}
onOpenChange={setWizardOpen}
users={users}
rows={tableData.rows}
onSelectRow={tableData.setSelectedId}
/>
<WorkOrdersListMediaOverlay
activeMediaRow={activeMediaRow}
onCloseMedia={() => setMediaRow(null)}
mediaFiles={mediaFiles}
onMediaChange={(rowId, files) => {
setMediaFiles((prev) => ({ ...prev, [String(rowId)]: files }));
patchField(rowId, { mediaCount: files.length });
}}
/>
</Box>
);
}