shoc-frontend-new/src/domain/work-orders/use-cases/use-work-order-detail.ts
Codex Review Integration 4bffdac175 fix(uplifts): restrict revoke to admins and quiet permission failures (SH-207)
- Gate the Revoke action on the approved queue row and the detail modal
  to admin users; non-admins get a disabled button with a plain
  'Only admins can revoke uplifts' tooltip and the dialog cannot open.
- Stop nesting an h2 inside DialogTitle in the uplift detail modal
  (hydration error) by rendering the title text as a non-heading.
- Handle work-order detail/uplift query 403s quietly in the detail
  modal: sections fall back to 'Unavailable' and the queries suppress
  the global error toast (QueryCache now honors suppressErrorToast).
- Extract UpliftDecisionDialogs and openUpliftEvidence from the page to
  meet the changed-file maintainability gate (complexity 23 -> pass).
2026-09-17 00:13:54 -03:00

96 lines
3.3 KiB
TypeScript

import { keepPreviousData, useQuery, type UseQueryResult } from "@tanstack/react-query";
import { workOrdersApi } from "@/domain/work-orders/api/work-orders-api";
import { fetchWorkOrderEmployees } from "@/domain/work-orders/use-cases/fetch-work-order-employees";
import type {
DispatchDetail,
EmployeeOption,
LocationOption,
TaskListTemplate,
VendorDropdownItem,
WorkOrderDetail,
} from "@/domain/work-orders/types/work-order";
import type { WorkOrderBoardDetail } from "@/domain/work-orders/types/work-order-board-detail";
import { queryKeys } from "@/infra/query-key/query-key";
import { requireQueryParam } from "@/lib/query/require-query-param";
export function useWorkOrderDetail(
id: string | number | undefined,
): UseQueryResult<WorkOrderDetail, Error> {
return useQuery({
queryKey: queryKeys.workOrders.detail(id ?? ""),
queryFn: () => workOrdersApi.getById(requireQueryParam(id, "Work order id")),
enabled: id !== undefined && id !== "",
placeholderData: keepPreviousData,
});
}
export function useWorkOrderBoardDetail(
id: string | number | undefined,
enabled = true,
meta?: { suppressErrorToast?: boolean },
): UseQueryResult<WorkOrderBoardDetail, Error> {
return useQuery({
queryKey: queryKeys.workOrders.boardDetail(id ?? ""),
queryFn: () => workOrdersApi.getBoardDetail(requireQueryParam(id, "Work order id")),
enabled: enabled && id !== undefined && id !== "",
meta,
// Do not keepPreviousData — placeholder detail from another WO leaks into the slide-over.
});
}
export function useWorkOrderMedia(
id: string | number | undefined,
enabled = true,
): UseQueryResult<WorkOrderBoardDetail["media"], Error> {
return useQuery({
queryKey: queryKeys.workOrders.boardMedia(id ?? ""),
queryFn: () => workOrdersApi.listMedia(requireQueryParam(id, "Work order id")),
enabled: enabled && id !== undefined && id !== "",
});
}
export function useWorkOrderLocations(): UseQueryResult<LocationOption[], Error> {
return useQuery({
queryKey: queryKeys.locations.dropdown(),
queryFn: () => workOrdersApi.fetchLocations(),
staleTime: 5 * 60 * 1000,
});
}
export function useWorkOrderEmployeesDropdown(): UseQueryResult<EmployeeOption[], Error> {
return useQuery({
queryKey: [...queryKeys.employees.all, "dropdown"] as const,
queryFn: () => fetchWorkOrderEmployees(),
staleTime: 5 * 60 * 1000,
});
}
export function useWorkOrderVendors(
trade?: string,
siteZip?: string,
): UseQueryResult<VendorDropdownItem[], Error> {
return useQuery({
queryKey: queryKeys.vendors.dropdown(trade, siteZip),
queryFn: () => workOrdersApi.fetchVendorsDropdown(trade, siteZip),
staleTime: 60 * 1000,
});
}
export function useTaskListTemplates(): UseQueryResult<TaskListTemplate[], Error> {
return useQuery({
queryKey: queryKeys.workOrders.taskTemplates(),
queryFn: () => workOrdersApi.fetchTaskTemplates(),
staleTime: 5 * 60 * 1000,
});
}
export function useDispatchDetail(
dispatchId: string | number | undefined,
): UseQueryResult<DispatchDetail, Error> {
return useQuery({
queryKey: queryKeys.workOrders.dispatch(dispatchId ?? ""),
queryFn: () => workOrdersApi.getDispatchById(requireQueryParam(dispatchId, "Dispatch id")),
enabled: dispatchId !== undefined && dispatchId !== "",
placeholderData: keepPreviousData,
});
}