mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 04:08:11 +00:00
changes
This commit is contained in:
@@ -125,12 +125,27 @@ export function useContractMutations(contractId: string) {
|
||||
};
|
||||
|
||||
const staffAccept = useMutation({
|
||||
mutationFn: (validityDays: number) =>
|
||||
contractsService.staffAccept(contractId, validityDays),
|
||||
mutationFn: (payload: {
|
||||
validityDays: number;
|
||||
documentSnapshot?: Freight.IContractDocumentSnapshot;
|
||||
}) =>
|
||||
contractsService.staffAccept(
|
||||
contractId,
|
||||
payload.validityDays,
|
||||
payload.documentSnapshot,
|
||||
),
|
||||
onSuccess: (data) => onSuccess(data, "Contract accepted for approval"),
|
||||
onError: () => toast.error("Failed to accept contract"),
|
||||
});
|
||||
|
||||
// Edit THIS contract's document articles (per-contract; never the templates).
|
||||
const updateDocument = useMutation({
|
||||
mutationFn: (snapshot: Freight.IContractDocumentSnapshot) =>
|
||||
contractsService.updateContractDocument(contractId, snapshot),
|
||||
onSuccess: (data) => onSuccess(data, "Contract document updated"),
|
||||
onError: () => toast.error("Failed to update contract document"),
|
||||
});
|
||||
|
||||
const requestChanges = useMutation({
|
||||
mutationFn: (note: string) =>
|
||||
contractsService.requestChanges(contractId, note),
|
||||
@@ -144,11 +159,6 @@ export function useContractMutations(contractId: string) {
|
||||
onError: () => toast.error("Failed to reject contract"),
|
||||
});
|
||||
|
||||
// Statuses that mean every approval step is done and the contract is ready to
|
||||
// be generated. Once the final approval lands we generate the PDF
|
||||
// automatically — staff no longer click a separate "Generate" button.
|
||||
const READY_TO_GENERATE = ["APPROVED", "APPROVED_PENDING_SIGNATURE"];
|
||||
|
||||
const approveStep = useMutation({
|
||||
mutationFn: ({
|
||||
stepId,
|
||||
@@ -158,25 +168,15 @@ export function useContractMutations(contractId: string) {
|
||||
requiredRole: string;
|
||||
}) =>
|
||||
contractsService.approveStep({ id: contractId, stepId, requiredRole }),
|
||||
onSuccess: async (data) => {
|
||||
// If this was the LAST approval, auto-generate the contract so it goes
|
||||
// straight to CONTRACT_READY without a manual step.
|
||||
const alreadyGenerated = Boolean(
|
||||
(data as Freight.IContract).contractGeneratedAt,
|
||||
);
|
||||
if (READY_TO_GENERATE.includes(data.status) && !alreadyGenerated) {
|
||||
toast.success("Final approval complete — generating contract…");
|
||||
try {
|
||||
const generated = await contractsService.generateContract(data.id);
|
||||
onSuccess(generated, "Contract generated and ready to sign");
|
||||
return;
|
||||
} catch {
|
||||
toast.error("Approved, but contract generation failed. Retry below.");
|
||||
void invalidateContractDetail(qc, data.id);
|
||||
return;
|
||||
}
|
||||
}
|
||||
onSuccess(data, "Approval step completed");
|
||||
onSuccess: (data) => {
|
||||
// The document is generated at the accept stage and reviewed during
|
||||
// approval, so the final approval moves the contract straight to
|
||||
// CONTRACT_READY on the server — no client-side generate call here.
|
||||
const message =
|
||||
data.status === "CONTRACT_READY"
|
||||
? "Final approval complete — contract ready to sign"
|
||||
: "Approval step completed";
|
||||
onSuccess(data, message);
|
||||
},
|
||||
onError: () => toast.error("Failed to approve step"),
|
||||
});
|
||||
@@ -239,6 +239,7 @@ export function useContractMutations(contractId: string) {
|
||||
|
||||
const isPending =
|
||||
staffAccept.isPending ||
|
||||
updateDocument.isPending ||
|
||||
requestChanges.isPending ||
|
||||
reject.isPending ||
|
||||
approveStep.isPending ||
|
||||
@@ -249,6 +250,7 @@ export function useContractMutations(contractId: string) {
|
||||
|
||||
return {
|
||||
staffAccept,
|
||||
updateDocument,
|
||||
requestChanges,
|
||||
reject,
|
||||
approveStep,
|
||||
|
||||
@@ -3,7 +3,11 @@ import toast from "react-hot-toast";
|
||||
|
||||
import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
|
||||
import { api } from "@/services/api";
|
||||
import { ruleEngineService, type RuleEngineListParams } from "@/services/ruleEngine/ruleEngine.service";
|
||||
import {
|
||||
ruleEngineService,
|
||||
type RuleEngineListParams,
|
||||
type SubmitPriorityRuleChangePayload,
|
||||
} from "@/services/ruleEngine/ruleEngine.service";
|
||||
import { RULE_ENGINE_SELECT_NONE } from "@/pages/ruleEngine/config/resources";
|
||||
import type {
|
||||
RuleEngineRecord,
|
||||
@@ -239,6 +243,68 @@ export const useRuleEngineMutations = (resource: RuleEngineResourceSlug) => {
|
||||
return { create, update, remove };
|
||||
};
|
||||
|
||||
/**
|
||||
* Priority-rule approval workflow. Every create/update/delete of a priority
|
||||
* config is SUBMITTED as a change request; an approver applies or rejects it.
|
||||
* Error toasts surface the backend message so range-collision rejections
|
||||
* ("1–5 overlaps existing rule …") reach the user verbatim.
|
||||
*/
|
||||
export const usePriorityRuleWorkflow = (enabled: boolean) => {
|
||||
const qc = useQueryClient();
|
||||
|
||||
const backendMessage = (err: unknown, fallback: string) => {
|
||||
const msg = (err as { response?: { data?: { message?: string | string[] } } })
|
||||
?.response?.data?.message;
|
||||
if (Array.isArray(msg)) return msg.join(", ");
|
||||
return msg || fallback;
|
||||
};
|
||||
|
||||
const pending = useQuery({
|
||||
queryKey: QUERY_KEYS.RULE_ENGINE.priorityRuleChanges,
|
||||
queryFn: () => ruleEngineService.listPriorityRuleChanges("PENDING"),
|
||||
enabled,
|
||||
});
|
||||
|
||||
const invalidate = async () => {
|
||||
await qc.invalidateQueries({
|
||||
queryKey: QUERY_KEYS.RULE_ENGINE.priorityRuleChanges,
|
||||
});
|
||||
await invalidateRuleEngineList(qc, "priority-configs");
|
||||
};
|
||||
|
||||
const submit = useMutation({
|
||||
mutationFn: (payload: SubmitPriorityRuleChangePayload) =>
|
||||
ruleEngineService.submitPriorityRuleChange(payload),
|
||||
onSuccess: async () => {
|
||||
toast.success("Change submitted for approval — the team has been notified");
|
||||
await invalidate();
|
||||
},
|
||||
onError: (err) => toast.error(backendMessage(err, "Failed to submit change")),
|
||||
});
|
||||
|
||||
const approve = useMutation({
|
||||
mutationFn: ({ id, decisionNote }: { id: string; decisionNote?: string }) =>
|
||||
ruleEngineService.approvePriorityRuleChange(id, decisionNote),
|
||||
onSuccess: async () => {
|
||||
toast.success("Change approved and applied");
|
||||
await invalidate();
|
||||
},
|
||||
onError: (err) => toast.error(backendMessage(err, "Failed to approve change")),
|
||||
});
|
||||
|
||||
const reject = useMutation({
|
||||
mutationFn: ({ id, decisionNote }: { id: string; decisionNote?: string }) =>
|
||||
ruleEngineService.rejectPriorityRuleChange(id, decisionNote),
|
||||
onSuccess: async () => {
|
||||
toast.success("Change rejected");
|
||||
await invalidate();
|
||||
},
|
||||
onError: (err) => toast.error(backendMessage(err, "Failed to reject change")),
|
||||
});
|
||||
|
||||
return { pending, submit, approve, reject };
|
||||
};
|
||||
|
||||
export const useRateWorkflow = () => {
|
||||
const qc = useQueryClient();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user