mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 23:28:11 +00:00
feat: add pagination to schedule history and consolidation approvals
- Implemented pagination in ScheduleHistoryPanel to manage large history entries. - Updated API to support pagination parameters for schedule history. - Enhanced ConsolidationApprovalsPage with tabbed navigation and pagination for approval rows. - Introduced new types for paginated responses in bookings and train scheduling services. - Added a database migration to create an index on wagon_booking_allocations for performance improvements.
This commit is contained in:
@@ -15,6 +15,8 @@ import {
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { AlertCircle, ArrowRight, PackageCheck, PackageOpen, TrainFront } from "lucide-react";
|
||||
|
||||
import { useAuth } from "@/auth/useAuth";
|
||||
import { FREIGHT_PERMS, hasPermission as hasFreightPermission } from "@/lib/permissions";
|
||||
import { api } from "@/services/api";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import type {
|
||||
@@ -130,6 +132,9 @@ export function IntercityRideAlongPanel({
|
||||
direction: string | null | undefined;
|
||||
}) {
|
||||
const { toast } = useToast();
|
||||
const { user } = useAuth();
|
||||
const canLoad = hasFreightPermission(user, FREIGHT_PERMS.trainScheduling.load);
|
||||
const canUnload = hasFreightPermission(user, FREIGHT_PERMS.trainScheduling.unload);
|
||||
const queryClient = useQueryClient();
|
||||
const [selected, setSelected] = useState<string[]>([]);
|
||||
|
||||
@@ -378,12 +383,19 @@ export function IntercityRideAlongPanel({
|
||||
<Table.Td>
|
||||
<Group gap="xs" justify="flex-end">
|
||||
{row.status === "PAID" && (
|
||||
<Tooltip label="Train must be at the booking's origin yard">
|
||||
<Tooltip
|
||||
label={
|
||||
canLoad
|
||||
? "Train must be at the booking's origin yard"
|
||||
: "You don't have permission to load cargo"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="light"
|
||||
leftSection={<PackageCheck size={13} />}
|
||||
loading={load.isPending}
|
||||
disabled={!canLoad}
|
||||
onClick={() =>
|
||||
load.mutate({ scheduleId, bookingId: row.id })
|
||||
}
|
||||
@@ -393,13 +405,20 @@ export function IntercityRideAlongPanel({
|
||||
</Tooltip>
|
||||
)}
|
||||
{row.status === "IN_TRANSIT" && (
|
||||
<Tooltip label="Train must be at the booking's destination yard">
|
||||
<Tooltip
|
||||
label={
|
||||
canUnload
|
||||
? "Train must be at the booking's destination yard"
|
||||
: "You don't have permission to unload cargo"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="light"
|
||||
color="orange"
|
||||
leftSection={<PackageOpen size={13} />}
|
||||
loading={unload.isPending}
|
||||
disabled={!canUnload}
|
||||
onClick={() =>
|
||||
unload.mutate({ scheduleId, bookingId: row.id })
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import { useEffect, useState } from "react";
|
||||
import { Freight } from "@edr/types";
|
||||
|
||||
import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge";
|
||||
import { useAuth } from "@/auth/useAuth";
|
||||
import { FREIGHT_PERMS, hasPermission as hasFreightPermission } from "@/lib/permissions";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { api } from "@/services/api";
|
||||
import type { TrackStation, YardWorkBookingRow } from "@/types/trainScheduling";
|
||||
@@ -111,6 +113,8 @@ export function LogPassYardWorkModal({
|
||||
alreadyLogged: boolean;
|
||||
}) {
|
||||
const { toast } = useToast();
|
||||
const { user } = useAuth();
|
||||
const canLoad = hasFreightPermission(user, FREIGHT_PERMS.trainScheduling.load);
|
||||
const [justLogged, setJustLogged] = useState(false);
|
||||
// When the train was here — defaults to now, past allowed (recorded after the fact).
|
||||
const [passAt, setPassAt] = useState<Date | null>(null);
|
||||
@@ -353,18 +357,20 @@ export function LogPassYardWorkModal({
|
||||
{!row.loadedAt ? (
|
||||
<Tooltip
|
||||
label={
|
||||
!logged
|
||||
? "Log the pass first — the train must be at this yard"
|
||||
: !row.canLoad
|
||||
? "Booking is not ready to load (payment pending)"
|
||||
: "Confirm cargo loaded onto the train"
|
||||
!canLoad
|
||||
? "You don't have permission to load cargo"
|
||||
: !logged
|
||||
? "Log the pass first — the train must be at this yard"
|
||||
: !row.canLoad
|
||||
? "Booking is not ready to load (payment pending)"
|
||||
: "Confirm cargo loaded onto the train"
|
||||
}
|
||||
>
|
||||
<Button
|
||||
size="compact-xs"
|
||||
variant="light"
|
||||
leftSection={<PackageCheck size={13} />}
|
||||
disabled={!logged || !row.canLoad}
|
||||
disabled={!canLoad || !logged || !row.canLoad}
|
||||
loading={
|
||||
load.isPending && load.variables?.bookingId === row.id
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ import { CountdownTimer } from "@edr/ui-common";
|
||||
|
||||
import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge";
|
||||
import { EntityLink } from "@/components/detail";
|
||||
import { useAuth } from "@/auth/useAuth";
|
||||
import { FREIGHT_PERMS, hasPermission as hasFreightPermission } from "@/lib/permissions";
|
||||
import { api } from "@/services/api";
|
||||
import { bookingsService } from "@/services/bookings.service";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
@@ -170,6 +172,9 @@ export function ScheduleWorkspacePanel({
|
||||
onChanged,
|
||||
}: ScheduleWorkspacePanelProps) {
|
||||
const { toast } = useToast();
|
||||
const { user } = useAuth();
|
||||
const canLoad = hasFreightPermission(user, FREIGHT_PERMS.trainScheduling.load);
|
||||
const canUnload = hasFreightPermission(user, FREIGHT_PERMS.trainScheduling.unload);
|
||||
|
||||
const freightType: FreightType | undefined =
|
||||
schedule.freightType === "CONTAINER" || schedule.freightType === "BULK"
|
||||
@@ -838,13 +843,15 @@ export function ScheduleWorkspacePanel({
|
||||
{showLoad ? (
|
||||
<Tooltip
|
||||
label={
|
||||
boardHere
|
||||
? `Load cargo onto the train at ${group.label}`
|
||||
: passed
|
||||
? `Train already passed ${group.label} — this cargo missed its stop`
|
||||
: `Loads at ${group.label} — train is ${
|
||||
trainAtLabel ? `at ${trainAtLabel}` : "not there yet"
|
||||
}`
|
||||
!canLoad
|
||||
? "You don't have permission to load cargo"
|
||||
: boardHere
|
||||
? `Load cargo onto the train at ${group.label}`
|
||||
: passed
|
||||
? `Train already passed ${group.label} — this cargo missed its stop`
|
||||
: `Loads at ${group.label} — train is ${
|
||||
trainAtLabel ? `at ${trainAtLabel}` : "not there yet"
|
||||
}`
|
||||
}
|
||||
withArrow
|
||||
>
|
||||
@@ -853,7 +860,7 @@ export function ScheduleWorkspacePanel({
|
||||
variant="filled"
|
||||
color="edr-green"
|
||||
radius="md"
|
||||
disabled={!boardHere}
|
||||
disabled={!boardHere || !canLoad}
|
||||
leftSection={<PackageCheck size={13} />}
|
||||
loading={
|
||||
loadJourney.isPending &&
|
||||
@@ -869,7 +876,11 @@ export function ScheduleWorkspacePanel({
|
||||
) : null}
|
||||
{showTruckToTrain ? (
|
||||
<Tooltip
|
||||
label="Customer truck loaded straight onto the wagon — no warehouse receipt, no GRN. Sets direct truck-to-train handover and loads."
|
||||
label={
|
||||
canLoad
|
||||
? "Customer truck loaded straight onto the wagon — no warehouse receipt, no GRN. Sets direct truck-to-train handover and loads."
|
||||
: "You don't have permission to load cargo"
|
||||
}
|
||||
withArrow
|
||||
>
|
||||
<Button
|
||||
@@ -877,6 +888,7 @@ export function ScheduleWorkspacePanel({
|
||||
variant="light"
|
||||
color="blue"
|
||||
radius="md"
|
||||
disabled={!canLoad}
|
||||
leftSection={<Truck size={13} />}
|
||||
loading={truckToTrainPending === b.id}
|
||||
onClick={() =>
|
||||
@@ -894,9 +906,11 @@ export function ScheduleWorkspacePanel({
|
||||
{showUnload ? (
|
||||
<Tooltip
|
||||
label={
|
||||
alightHere
|
||||
? "Unload at this yard — stamps the booking's arrival"
|
||||
: "Unloads when the train reaches its destination yard"
|
||||
!canUnload
|
||||
? "You don't have permission to unload cargo"
|
||||
: alightHere
|
||||
? "Unload at this yard — stamps the booking's arrival"
|
||||
: "Unloads when the train reaches its destination yard"
|
||||
}
|
||||
withArrow
|
||||
>
|
||||
@@ -905,7 +919,7 @@ export function ScheduleWorkspacePanel({
|
||||
variant="light"
|
||||
color="orange"
|
||||
radius="md"
|
||||
disabled={!alightHere}
|
||||
disabled={!alightHere || !canUnload}
|
||||
leftSection={<PackageOpen size={13} />}
|
||||
loading={
|
||||
unloadJourney.isPending &&
|
||||
|
||||
Reference in New Issue
Block a user