mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-07 10:28:59 +00:00
Keep the slide-over uplift tab and the evidence upload while taking the current board, dashboard, and vendor changes from main.
145 lines
4.8 KiB
TypeScript
145 lines
4.8 KiB
TypeScript
import {
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient,
|
|
type UseMutationResult,
|
|
type UseQueryResult,
|
|
} from "@tanstack/react-query";
|
|
import { toast } from "react-toastify";
|
|
import { upliftsApi } from "@/domain/uplifts/api/uplifts-api";
|
|
import type { UpliftRequest } from "@/domain/uplifts/types/uplift";
|
|
import { queryKeys } from "@/infra/query-key/query-key";
|
|
import { requireQueryParam } from "@/lib/query/require-query-param";
|
|
|
|
export function useUpliftsForDispatch(
|
|
dispatchId: string | number | undefined,
|
|
): UseQueryResult<UpliftRequest[], Error> {
|
|
return useQuery({
|
|
queryKey: queryKeys.uplifts.forDispatch(dispatchId ?? ""),
|
|
queryFn: () => upliftsApi.listForDispatch(requireQueryParam(dispatchId, "Dispatch id")),
|
|
enabled: dispatchId !== undefined && dispatchId !== "",
|
|
});
|
|
}
|
|
|
|
function invalidateUpliftQueries(
|
|
queryClient: ReturnType<typeof useQueryClient>,
|
|
dispatchId?: string | number,
|
|
) {
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.uplifts.all });
|
|
// why: Queue decisions update the same uplift records rendered by Work Order views. Invalidate that
|
|
// shared cache even when the queue has no dispatch context.
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
|
|
if (dispatchId !== undefined && dispatchId !== "") {
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.uplifts.forDispatch(dispatchId) });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.dispatch(dispatchId) });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.vendorPos.detail(dispatchId) });
|
|
}
|
|
}
|
|
|
|
function invalidateWorkOrderUpliftQueries(queryClient: ReturnType<typeof useQueryClient>) {
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.uplifts.all });
|
|
void queryClient.invalidateQueries({ queryKey: queryKeys.workOrders.all });
|
|
}
|
|
|
|
export interface ApproveUpliftInput {
|
|
id: string | number;
|
|
note?: string;
|
|
amount?: number;
|
|
woNumber?: string;
|
|
}
|
|
|
|
export interface DecisionUpliftInput {
|
|
id: string | number;
|
|
note: string;
|
|
amount?: number;
|
|
woNumber?: string;
|
|
}
|
|
|
|
function decisionAmount(amount: number | undefined): string {
|
|
if (amount == null || !Number.isFinite(amount)) return "the requested amount";
|
|
return new Intl.NumberFormat("en-US", { style: "currency", currency: "USD" }).format(amount);
|
|
}
|
|
|
|
function decisionWorkOrder(woNumber: string | undefined): string {
|
|
return woNumber?.trim() || "the work order";
|
|
}
|
|
|
|
export function useApproveUplift(
|
|
dispatchId?: string | number,
|
|
): UseMutationResult<void, Error, ApproveUpliftInput> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ id, note }: ApproveUpliftInput) => upliftsApi.approve(id, note),
|
|
onSuccess: (_, variables) => {
|
|
invalidateUpliftQueries(queryClient, dispatchId);
|
|
toast.success(
|
|
`Uplift approved — ${decisionAmount(variables.amount)} on ${decisionWorkOrder(variables.woNumber)}`,
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
toast.error(error.message || "Failed to approve uplift");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRejectUplift(
|
|
dispatchId?: string | number,
|
|
): UseMutationResult<void, Error, DecisionUpliftInput> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ id, note }: DecisionUpliftInput) => upliftsApi.reject(id, note),
|
|
onSuccess: (_, variables) => {
|
|
invalidateUpliftQueries(queryClient, dispatchId);
|
|
toast.success(
|
|
`Uplift rejected — ${decisionAmount(variables.amount)} on ${decisionWorkOrder(variables.woNumber)}`,
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
toast.error(error.message || "Failed to reject uplift");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRequestChangesUplift(
|
|
dispatchId?: string | number,
|
|
): UseMutationResult<void, Error, DecisionUpliftInput> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ id, note }: { id: string | number; note: string }) =>
|
|
upliftsApi.requestChanges(id, note),
|
|
onSuccess: () => {
|
|
invalidateUpliftQueries(queryClient, dispatchId);
|
|
toast.success("Changes requested");
|
|
},
|
|
onError: (error: Error) => {
|
|
toast.error(error.message || "Failed to request changes");
|
|
},
|
|
});
|
|
}
|
|
|
|
export interface RevokeUpliftInput {
|
|
upliftId: string | number;
|
|
reason: string;
|
|
amount?: number;
|
|
woNumber?: string;
|
|
}
|
|
|
|
export function useRevokeUplift(): UseMutationResult<void, Error, RevokeUpliftInput> {
|
|
const queryClient = useQueryClient();
|
|
|
|
return useMutation({
|
|
mutationFn: ({ upliftId, reason }: RevokeUpliftInput) => upliftsApi.revoke(upliftId, reason),
|
|
onSuccess: (_, variables) => {
|
|
invalidateWorkOrderUpliftQueries(queryClient);
|
|
toast.success(
|
|
`Uplift revoked — ${decisionAmount(variables.amount)} on ${decisionWorkOrder(variables.woNumber)}`,
|
|
);
|
|
},
|
|
onError: (error: Error) => {
|
|
toast.error(error.message || "Failed to revoke uplift");
|
|
},
|
|
});
|
|
}
|