shoc-frontend-new/src/domain/work-orders/use-cases/use-work-order-uplifts.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

89 lines
2.9 KiB
TypeScript

import {
useMutation,
useQuery,
useQueryClient,
type UseMutationResult,
type UseQueryResult,
} from "@tanstack/react-query";
import { toast } from "react-toastify";
import { workOrderUpliftsApi } from "@/domain/work-orders/api/work-order-uplifts-api";
import type {
CreateWorkOrderUpliftInput,
WorkOrderUplift,
} from "@/domain/work-orders/types/work-order-uplift";
import { queryKeys } from "@/infra/query-key/query-key";
import { requireQueryParam } from "@/lib/query/require-query-param";
function invalidateUpliftQueries(
queryClient: ReturnType<typeof useQueryClient>,
workOrderId: string | number,
): void {
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.uplifts(workOrderId) });
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
}
export function useWorkOrderUplifts(
workOrderId: string | number | null | undefined,
meta?: { suppressErrorToast?: boolean },
): UseQueryResult<WorkOrderUplift[], Error> {
return useQuery({
queryKey: queryKeys.workOrders.uplifts(workOrderId ?? ""),
queryFn: () =>
workOrderUpliftsApi.list(requireQueryParam(workOrderId ?? undefined, "Work order id")),
enabled: workOrderId != null && workOrderId !== "",
meta,
});
}
export function useCreateWorkOrderUplift(
workOrderId: string | number,
): UseMutationResult<WorkOrderUplift, Error, CreateWorkOrderUpliftInput> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (input: CreateWorkOrderUpliftInput) =>
workOrderUpliftsApi.create(workOrderId, input),
onSuccess: () => {
invalidateUpliftQueries(queryClient, workOrderId);
toast.success("Uplift request created");
},
onError: (error) => {
toast.error(error.message || "Failed to create uplift");
},
});
}
export function useCancelWorkOrderUplift(
workOrderId: string | number,
): UseMutationResult<WorkOrderUplift, Error, string | number> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: (upliftId: string | number) => workOrderUpliftsApi.cancel(workOrderId, upliftId),
onSuccess: () => {
invalidateUpliftQueries(queryClient, workOrderId);
toast.success("Uplift cancelled");
},
onError: (error) => {
toast.error(error.message || "Failed to cancel uplift");
},
});
}
export function useRevokeWorkOrderUplift(
workOrderId: string | number,
): UseMutationResult<WorkOrderUplift, Error, { upliftId: string | number; reason?: string }> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: ({ upliftId, reason }: { upliftId: string | number; reason?: string }) =>
workOrderUpliftsApi.revoke(workOrderId, upliftId, reason),
onSuccess: () => {
invalidateUpliftQueries(queryClient, workOrderId);
toast.success("Uplift revoked");
},
onError: (error) => {
toast.error(error.message || "Failed to revoke uplift");
},
});
}