shoc-frontend-new/src/app/v/[token]/dispatch/[id].tsx
2026-08-11 08:57:38 -03:00

129 lines
4.3 KiB
TypeScript

import { useCallback, useState } from "react";
import { useParams } from "react-router";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { Text } from "@/components/ui/text";
import { DispatchDetailSections } from "@/app/v/[token]/dispatch/_components/dispatch-detail-sections";
import { DispatchSummaryCard } from "@/app/v/[token]/dispatch/_components/dispatch-summary-card";
import { RefuseDispatchDialog } from "@/app/v/[token]/dispatch/_components/refuse-dispatch-dialog";
import { useVendorPortal } from "@/app/v/_components/vendor-portal-context";
import { vendorPortalApi } from "@/domain/vendor-portal/api/vendor-portal-api";
import { isVendorPortalLockedStatus } from "@/domain/vendor-portal/lib/status-helpers";
import type { VendorPortalDispatchDetail } from "@/domain/vendor-portal/types/vendor-portal";
import { queryKeys } from "@/infra/query-key/query-key";
export default function VendorPortalDispatchPage() {
const { token } = useVendorPortal();
const { id = "" } = useParams();
const dispatchId = Number(id);
const queryClient = useQueryClient();
const [busy, setBusy] = useState(false);
const [actionError, setActionError] = useState("");
const [refuseOpen, setRefuseOpen] = useState(false);
const { data, isLoading, error, refetch } = useQuery({
queryKey: queryKeys.vendorPortal.dispatch(token, dispatchId),
queryFn: () => vendorPortalApi.getDispatch(token, dispatchId),
enabled: Boolean(token && dispatchId),
});
const refresh = useCallback(async () => {
await refetch();
await queryClient.invalidateQueries({ queryKey: queryKeys.vendorPortal.dispatches(token) });
}, [refetch, queryClient, token]);
const pollEvidenceDocument = useCallback(
(documentId: number) => vendorPortalApi.getDocumentStatus(token, dispatchId, documentId),
[token, dispatchId],
);
const runAction = async (action: () => Promise<void>, confirmMsg?: string) => {
if (confirmMsg && !window.confirm(confirmMsg)) return;
setActionError("");
setBusy(true);
try {
await action();
await refresh();
} catch {
setActionError("Unable to update this dispatch. Please try again.");
} finally {
setBusy(false);
}
};
const updateLocal = (
updater: (current: VendorPortalDispatchDetail) => VendorPortalDispatchDetail,
) => {
queryClient.setQueryData(
queryKeys.vendorPortal.dispatch(token, dispatchId),
(current: unknown) => {
if (!current) return current;
return updater(current as VendorPortalDispatchDetail);
},
);
};
const requestCancel = () => {
const reason = window.prompt("Reason for cancel request?");
if (reason == null) return;
void runAction(() => vendorPortalApi.requestCancel(token, dispatchId, reason));
};
if (isLoading && !data) {
return (
<Text variant="feedback" className="vp-card vp-muted">
Loading dispatch…
</Text>
);
}
if (error && !data) {
return <Text variant="error">Unable to load this dispatch. Please try again.</Text>;
}
if (!data) return null;
const locked = isVendorPortalLockedStatus(data.status);
return (
<>
<DispatchSummaryCard
token={token}
data={data}
locked={locked}
busy={busy}
onAccept={() => void runAction(() => vendorPortalApi.accept(token, dispatchId))}
onRefuse={() => setRefuseOpen(true)}
onStart={() =>
void runAction(() => vendorPortalApi.changeStatus(token, dispatchId, "In Progress"))
}
onComplete={() =>
void runAction(
() => vendorPortalApi.changeStatus(token, dispatchId, "Completed"),
"Mark this dispatch as completed?",
)
}
onRequestCancel={requestCancel}
/>
<Text variant="error" when={Boolean(actionError)}>
{actionError}
</Text>
<DispatchDetailSections
token={token}
dispatchId={dispatchId}
data={data}
locked={locked}
refresh={refresh}
pollEvidenceDocument={pollEvidenceDocument}
updateLocal={updateLocal}
/>
<RefuseDispatchDialog
open={refuseOpen}
dispatchId={dispatchId}
onClose={() => setRefuseOpen(false)}
onRefused={() => setActionError("")}
/>
</>
);
}