mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 15:25:45 +00:00
add CUSTOMS type to priority configs and update related logic
This commit is contained in:
@@ -15,11 +15,56 @@ import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
|
||||
// prefix — strip a trailing `/api` if the base URL carries one.
|
||||
const SOCKET_ORIGIN = String(API_BASE_URL ?? "").replace(/\/api\/?$/, "");
|
||||
|
||||
// The two carousel window lists share the MyBookingWindow-shaped row and can be
|
||||
// patched in place. The batch board is a richer, differently-shaped view, so it
|
||||
// stays on a (debounced) invalidate.
|
||||
const WINDOW_ACTIONS = new Set(["all-booking-windows", "contractBookingWindows"]);
|
||||
|
||||
/** Shape shared by both carousel window lists (all-lanes + contract-scoped). */
|
||||
interface WindowRow {
|
||||
scheduleId: string;
|
||||
windowPhase: string | null;
|
||||
isOpenNow: boolean;
|
||||
windowOpensAt: string | null;
|
||||
windowClosesAt: string | null;
|
||||
docReviewEndsAt: string | null;
|
||||
paymentPhaseEndsAt: string | null;
|
||||
bookingWindowStatus: string;
|
||||
bookingCycleNo: number;
|
||||
departureDate: string;
|
||||
}
|
||||
|
||||
function isWindowKey(key: readonly unknown[]): boolean {
|
||||
return key[0] === "train-scheduling" && WINDOW_ACTIONS.has(String(key[1]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to live booking-window pushes for staff. Every phase transition
|
||||
* the window engine applies invalidates the GL windows carousel and the batch
|
||||
* board, so both flip the moment the backend does — polling stays only as a
|
||||
* fallback.
|
||||
* Fold a server phase push onto a cached window row, recomputing isOpenNow the
|
||||
* same way the server does (phase OPEN + status OPEN) so live-patched state can
|
||||
* never disagree with a fresh REST fetch on refresh.
|
||||
*/
|
||||
function applyEvent<T extends WindowRow>(row: T, event: BookingWindowPhaseEvent): T {
|
||||
return {
|
||||
...row,
|
||||
windowPhase: event.phase,
|
||||
bookingWindowStatus: event.bookingWindowStatus ?? row.bookingWindowStatus,
|
||||
bookingCycleNo: event.bookingCycleNo,
|
||||
isOpenNow: event.phase === "OPEN" && event.bookingWindowStatus === "OPEN",
|
||||
windowOpensAt: event.windowOpensAt,
|
||||
windowClosesAt: event.windowClosesAt,
|
||||
docReviewEndsAt: event.docReviewEndsAt,
|
||||
paymentPhaseEndsAt: event.paymentPhaseEndsAt,
|
||||
departureDate: event.scheduledDepartureDate ?? row.departureDate,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribes to live booking-window pushes for staff. A phase transition carries
|
||||
* the schedule's full new state; we fold it straight into the carousel window
|
||||
* lists with setQueriesData rather than invalidating — same rationale as the
|
||||
* portal hook (no per-push refetch storm; live + refreshed state agree, killing
|
||||
* the refresh-jump). The batch board is a different-shaped view, so it keeps a
|
||||
* debounced invalidate, as do pushes for schedules not present in any list.
|
||||
*/
|
||||
export function useBookingWindowSocket(enabled: boolean = true) {
|
||||
const qc = useQueryClient();
|
||||
@@ -47,19 +92,53 @@ export function useBookingWindowSocket(enabled: boolean = true) {
|
||||
console.debug("[booking-windows] socket disconnected:", reason),
|
||||
);
|
||||
|
||||
socket.on(
|
||||
BOOKING_WINDOW_WS_EVENTS.PHASE,
|
||||
(_event: BookingWindowPhaseEvent) => {
|
||||
qc.invalidateQueries({
|
||||
queryKey: ["train-scheduling", "all-booking-windows"],
|
||||
});
|
||||
qc.invalidateQueries({
|
||||
// Coalesce the batch-board refresh (and the unknown-schedule fallback) so a
|
||||
// burst of pushes triggers at most one invalidation per window.
|
||||
let refetchTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
const scheduleRefetch = (includeWindowLists: boolean) => {
|
||||
if (refetchTimer) return;
|
||||
refetchTimer = setTimeout(() => {
|
||||
refetchTimer = null;
|
||||
void qc.invalidateQueries({
|
||||
queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoard(),
|
||||
});
|
||||
if (includeWindowLists) {
|
||||
void qc.invalidateQueries({
|
||||
predicate: (q) => isWindowKey(q.queryKey),
|
||||
});
|
||||
}
|
||||
}, 800);
|
||||
};
|
||||
|
||||
socket.on(
|
||||
BOOKING_WINDOW_WS_EVENTS.PHASE,
|
||||
(event: BookingWindowPhaseEvent) => {
|
||||
let patchedSomewhere = false;
|
||||
|
||||
qc.setQueriesData<WindowRow[]>(
|
||||
{ predicate: (q) => isWindowKey(q.queryKey) },
|
||||
(rows) => {
|
||||
if (!rows) return rows;
|
||||
let changed = false;
|
||||
const next = rows.map((row) => {
|
||||
if (row.scheduleId !== event.scheduleId) return row;
|
||||
changed = true;
|
||||
patchedSomewhere = true;
|
||||
return applyEvent(row, event);
|
||||
});
|
||||
return changed ? next : rows;
|
||||
},
|
||||
);
|
||||
|
||||
// Always refresh the batch board (different shape, not patched). When the
|
||||
// schedule wasn't in any window list either, refresh those too so a newly
|
||||
// announced window surfaces. Both debounced — no per-push stampede.
|
||||
scheduleRefetch(!patchedSomewhere);
|
||||
},
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (refetchTimer) clearTimeout(refetchTimer);
|
||||
socket.off();
|
||||
socket.disconnect();
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user