mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 22:25:42 +00:00
implement batch board notification system for schedule changes and updates
This commit is contained in:
@@ -55,6 +55,7 @@ import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles";
|
||||
import { FREIGHT_BRAND, FREIGHT_BRAND_DARK } from "@/theme/freight-brand";
|
||||
import { keepPreviousData, useQuery } from "@tanstack/react-query";
|
||||
import { api } from "@/services/api";
|
||||
import { useBookingWindowSocket } from "@/features/bookingWindows/useBookingWindowSocket";
|
||||
import type {
|
||||
BatchBoardFilters,
|
||||
BatchBoardSchedule,
|
||||
@@ -424,6 +425,8 @@ function CardSkeleton() {
|
||||
|
||||
export default function BatchBoardPage() {
|
||||
const navigate = useNavigate();
|
||||
// Live board: phase + batch-changed pushes invalidate the list query below.
|
||||
useBookingWindowSocket();
|
||||
const { viewMode, setViewMode } = useFleetViewMode("batch-board");
|
||||
const { pagination, setPagination } = usePagination({ pageSize: 12 });
|
||||
const [search, setSearch] = useState("");
|
||||
@@ -484,7 +487,10 @@ export default function BatchBoardPage() {
|
||||
|
||||
const { data, isLoading, isError, isFetching, refetch } = useQuery({
|
||||
...api.trainScheduling.batchBoard.queryOptions({ input: { filters } }),
|
||||
refetchInterval: 30_000,
|
||||
// Real-time updates come from the booking-window socket (batch-board:changed
|
||||
// + PHASE pushes invalidate this query); 60s is only a self-heal safety net
|
||||
// for a missed emit.
|
||||
refetchInterval: 60_000,
|
||||
placeholderData: keepPreviousData,
|
||||
});
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { memo, useCallback, useMemo, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
Accordion,
|
||||
@@ -377,7 +377,15 @@ const BOOKING_COLUMNS: ColumnDef<BatchBoardBookingDetail>[] = [
|
||||
},
|
||||
];
|
||||
|
||||
function BookingTable({ bookings }: { bookings: BatchBoardBookingDetail[] }) {
|
||||
// Memoized: the page re-renders on unrelated state (tab switch, composition
|
||||
// booking selection, background-fetch flags) while the bookings arrays keep
|
||||
// their identity (useMemo + React Query structural sharing) — skip re-rendering
|
||||
// the whole table in those cases.
|
||||
const BookingTable = memo(function BookingTable({
|
||||
bookings,
|
||||
}: {
|
||||
bookings: BatchBoardBookingDetail[];
|
||||
}) {
|
||||
return (
|
||||
<DataTable
|
||||
columns={BOOKING_COLUMNS}
|
||||
@@ -387,7 +395,7 @@ function BookingTable({ bookings }: { bookings: BatchBoardBookingDetail[] }) {
|
||||
containerClassName="overflow-x-auto rounded-lg border border-edr-border"
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
function WindowCountChips({ counts }: { counts: BatchWindowGroup["counts"] }) {
|
||||
const chips: Array<{ value: number; color: string; label: string }> = [
|
||||
@@ -581,18 +589,10 @@ export default function BatchScheduleDetailPage() {
|
||||
api.trainScheduling.batchBoardDetail.queryOptions({
|
||||
input: { scheduleId: scheduleId ?? "" },
|
||||
enabled: Boolean(scheduleId),
|
||||
// Poll fast while a window cycle is actively moving (open / doc-review /
|
||||
// payment) so the priority ranking + pay countdowns stay live; back off to
|
||||
// 30s once the cycle is idle (pre-window / closed / done).
|
||||
refetchInterval: (query) => {
|
||||
const phase = (query.state.data as BatchBoardScheduleDetail | undefined)
|
||||
?.windowPhase;
|
||||
return phase === "OPEN" ||
|
||||
phase === "DOC_REVIEW" ||
|
||||
phase === "PAYMENT"
|
||||
? 5_000
|
||||
: 30_000;
|
||||
},
|
||||
// Real-time updates come from the booking-window socket (batch-board:changed
|
||||
// + PHASE pushes invalidate this query); 60s is only a self-heal safety net
|
||||
// for a missed emit.
|
||||
refetchInterval: 60_000,
|
||||
}),
|
||||
);
|
||||
// Keep the board in sync with server-pushed window-phase transitions too
|
||||
@@ -688,6 +688,10 @@ export default function BatchScheduleDetailPage() {
|
||||
null,
|
||||
);
|
||||
|
||||
// Stable identity so the memoized BookingsManager isn't re-rendered by
|
||||
// unrelated page state (tab switches, composition selection, fetch flags).
|
||||
const handleBookingsChanged = useCallback(() => void refetch(), [refetch]);
|
||||
|
||||
const handleCompleteDocReview = () => {
|
||||
completeDocReview
|
||||
.mutateAsync(scheduleId ?? "")
|
||||
@@ -1002,7 +1006,7 @@ export default function BatchScheduleDetailPage() {
|
||||
<BookingsManager
|
||||
scheduleId={scheduleId ?? ""}
|
||||
bookings={allBookings}
|
||||
onChanged={() => void refetch()}
|
||||
onChanged={handleBookingsChanged}
|
||||
readOnly={bookingsReadOnly}
|
||||
/>
|
||||
</Paper>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMemo, useState } from "react";
|
||||
import { memo, useMemo, useState } from "react";
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
@@ -118,8 +118,11 @@ export interface BookingsManagerProps {
|
||||
* allocation status, and remove or re-assign bookings individually or in bulk.
|
||||
* Wraps the shared DataTable; selection + actions are handled locally so the
|
||||
* surrounding accordion / tab layout stays untouched.
|
||||
*
|
||||
* Memoized: the detail page passes stable props (memoized bookings array +
|
||||
* useCallback onChanged), so its unrelated re-renders skip this subtree.
|
||||
*/
|
||||
export function BookingsManager({
|
||||
export const BookingsManager = memo(function BookingsManager({
|
||||
scheduleId,
|
||||
bookings,
|
||||
onChanged,
|
||||
@@ -620,6 +623,6 @@ export function BookingsManager({
|
||||
</Modal>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default BookingsManager;
|
||||
|
||||
@@ -49,7 +49,7 @@ import {
|
||||
RouteCorridor,
|
||||
StatusPill,
|
||||
} from "@/components/trainScheduling/scheduleVisuals";
|
||||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { keepPreviousData, useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { api } from "@/services/api";
|
||||
import { formatRouteLabel } from "@/services/routes.service";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@@ -169,7 +169,14 @@ export default function TrainScheduleV2ListPage() {
|
||||
);
|
||||
|
||||
const schedulesQuery = useQuery(
|
||||
api.trainScheduling.scheduleList.queryOptions({ input: { filters } }),
|
||||
api.trainScheduling.scheduleList.queryOptions({
|
||||
input: { filters },
|
||||
// Keep the previous page on screen while the next page/filter result
|
||||
// loads instead of flashing the empty state; 30s staleTime spares
|
||||
// back-and-forth navigation from refetching an unchanged list.
|
||||
placeholderData: keepPreviousData,
|
||||
staleTime: 30_000,
|
||||
}),
|
||||
);
|
||||
// Yard options for the origin/destination filters (shared routes reference
|
||||
// list, so the choices don't shrink to whatever the current page shows).
|
||||
|
||||
Reference in New Issue
Block a user