From 2700a3edec2eeb291654d843e13b9ae74bb61b72 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Mon, 22 Jun 2026 13:46:38 +0000 Subject: [PATCH] refactor: migrate from most of the custom hooks into the api.ts --- .../components/profile/MySignatureCard.tsx | 21 +- .../trainScheduling/AllocateBookingWizard.tsx | 35 +- .../trainScheduling/ScheduleBatchPanel.tsx | 30 +- .../AssignedBookingsPanel.tsx | 5 +- .../CompositionBookingTabs.tsx | 20 +- .../ContainerNumberInput.tsx | 8 +- .../compositionEditor/RemovalLogPanel.tsx | 10 +- .../compositionEditor/TrainConsistView.tsx | 13 +- .../UnassignedBookingsPanel.tsx | 17 +- .../components/wagons/AssignWagonDialog.tsx | 11 +- .../src/components/wagons/WagonsTable.tsx | 13 +- .../warehouses/CreateWarehouseModal.tsx | 7 +- .../warehouses/WarehouseCardView.tsx | 8 +- .../components/warehouses/WarehouseTable.tsx | 8 +- .../backoffice/src/constants/QUERY_KEYS.ts | 8 +- .../backoffice/src/hooks/fleet/useFleet.ts | 35 - .../trainScheduling/useTrainScheduling.ts | 325 ------- .../backoffice/src/hooks/use-cargo-types.ts | 12 - .../src/hooks/use-container-types.ts | 12 - .../backoffice/src/hooks/use-wagon-types.ts | 36 - .../backoffice/src/hooks/useContainers.ts | 59 -- .../src/hooks/useDropdownSettings.ts | 112 --- .../backoffice/src/hooks/useFacilities.ts | 16 - .../src/hooks/useFileUploadSettings.ts | 126 --- .../backoffice/src/hooks/useLocomotives.ts | 47 - .../backoffice/src/hooks/usePayments.ts | 23 - .../backoffice/src/hooks/useRoutes.ts | 55 -- .../backoffice/src/hooks/useSavedSignature.ts | 30 - .../backoffice/src/hooks/useStations.ts | 16 - .../backoffice/src/hooks/useTrains.ts | 39 - .../backoffice/src/hooks/useWagons.ts | 64 -- .../src/pages/bookings/NewBookingPage.tsx | 27 +- .../documents/EditFileUploadSettingDialog.tsx | 7 +- .../documents/FileUploadSettingsPage.tsx | 7 +- .../ManageFileUploadFieldsDialog.tsx | 9 +- .../DropdownSettingsPage.tsx | 7 +- .../EditDropdownSettingDialog.tsx | 11 +- .../ManageDropdownOptionsDialog.tsx | 9 +- .../src/pages/fleet/FleetCrudPages.tsx | 79 +- .../src/pages/fleet/FleetResourcePage.tsx | 48 +- .../backoffice/src/pages/fleet/RoutesPage.tsx | 20 +- .../src/pages/payments/PaymentsPage.tsx | 12 +- .../pages/trainScheduling/BatchBoardPage.tsx | 7 +- .../BatchScheduleDetailPage.tsx | 28 +- .../TrainScheduleTrackPage.tsx | 14 +- .../TrainScheduleV2DetailPage.tsx | 29 +- .../TrainScheduleV2ListPage.tsx | 23 +- .../src/pages/trains/TrainDetailPage.tsx | 8 +- .../backoffice/src/services/api.ts | 845 +++++++++++++++++- .../backoffice/tsconfig.app.json | 3 +- 50 files changed, 1165 insertions(+), 1249 deletions(-) delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/fleet/useFleet.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/trainScheduling/useTrainScheduling.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/use-cargo-types.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/use-container-types.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/use-wagon-types.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useContainers.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useDropdownSettings.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useFacilities.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useFileUploadSettings.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useLocomotives.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/usePayments.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useRoutes.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useSavedSignature.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useStations.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useTrains.ts delete mode 100644 apps/edr-freight-web/backoffice/src/hooks/useWagons.ts diff --git a/apps/edr-freight-web/backoffice/src/components/profile/MySignatureCard.tsx b/apps/edr-freight-web/backoffice/src/components/profile/MySignatureCard.tsx index 3ac661e1a..5f4f4d7a7 100644 --- a/apps/edr-freight-web/backoffice/src/components/profile/MySignatureCard.tsx +++ b/apps/edr-freight-web/backoffice/src/components/profile/MySignatureCard.tsx @@ -1,6 +1,9 @@ import { useState } from "react"; import { FileSignature, Loader2 } from "lucide-react"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import toast from "react-hot-toast"; +import { api } from "@/services/api"; import { ContractSignaturePad } from "@/components/bookings/ContractSignaturePad"; import { Card, @@ -10,10 +13,6 @@ import { CardTitle, } from "@/components/ui/card"; import { useAuth } from "@/auth/useAuth"; -import { - useMySignature, - useSaveSignature, -} from "@/hooks/useSavedSignature"; import { Button, Dialog, @@ -33,8 +32,10 @@ import { */ export function MySignatureCard() { const { user } = useAuth(); - const { data: saved, isLoading } = useMySignature(); - const saveMutation = useSaveSignature(); + const { data: saved, isLoading } = useQuery( + api.signatures.mySignature.queryOptions({ staleTime: 60_000 }), + ); + const saveMutation = useMutation(api.signatures.save.mutationOptions()); const [open, setOpen] = useState(false); const [signerName, setSignerName] = useState(""); @@ -56,7 +57,13 @@ export function MySignatureCard() { signerDisplayName: signerName.trim(), signatureImageBase64: signatureData, }, - { onSuccess: () => setOpen(false) }, + { + onSuccess: () => { + toast.success("Signature saved"); + setOpen(false); + }, + onError: () => toast.error("Failed to save signature"), + }, ); }; diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/AllocateBookingWizard.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/AllocateBookingWizard.tsx index 0b2e0a885..0c26c3392 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/AllocateBookingWizard.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/AllocateBookingWizard.tsx @@ -31,13 +31,8 @@ import { Weight, } from "lucide-react"; -import { - useAvailableLocomotives, - useEligibleBookings, - useScheduleList, - useScheduleMutations, -} from "@/hooks/trainScheduling/useTrainScheduling"; -import { useRoutes } from "@/hooks/useRoutes"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import { trainSchedulingService } from "@/services/trainScheduling.service"; import type { BookingDetail } from "@/types/booking"; @@ -133,13 +128,27 @@ export function AllocateBookingWizard({ [originId, destinationId], ); - const eligibleQuery = useEligibleBookings(eligibleFilters, opened); - const schedulesQuery = useScheduleList(); - const routesQuery = useRoutes(); - const locomotivesQuery = useAvailableLocomotives( - scheduleMode === "new" && routeId ? routeId : undefined, + const eligibleQuery = useQuery( + api.trainScheduling.eligibleBookings.queryOptions({ + input: { filters: eligibleFilters }, + enabled: opened, + }), ); - const { create, preview, assign, finalize } = useScheduleMutations(selectedScheduleId ?? undefined); + const schedulesQuery = useQuery( + api.trainScheduling.scheduleList.queryOptions({ input: {} }), + ); + const routesQuery = useQuery(api.routes.list.queryOptions()); + const locomotivesQuery = useQuery( + api.trainScheduling.availableLocomotives.queryOptions({ + input: { + routeId: scheduleMode === "new" && routeId ? routeId : undefined, + }, + }), + ); + const create = useMutation(api.trainScheduling.createSchedule.mutationOptions()); + const preview = useMutation(api.trainScheduling.preview.mutationOptions()); + const assign = useMutation(api.trainScheduling.assignBookings.mutationOptions()); + const finalize = useMutation(api.trainScheduling.finalizeSchedule.mutationOptions()); useEffect(() => { if (scheduleMode === "new") { diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBatchPanel.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBatchPanel.tsx index 515f1e361..d44ef4540 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBatchPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/ScheduleBatchPanel.tsx @@ -13,11 +13,10 @@ import { } from "@mantine/core"; import { CheckCircle2, Layers, Lock, LockOpen, PlayCircle, Repeat, XCircle } from "lucide-react"; +import { useMutation, useQuery } from "@tanstack/react-query"; + import { BookingStatusBadge } from "@/components/bookings/BookingStatusBadge"; -import { - useBatchActions, - useBookableSchedules, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { TrainScheduleDetail } from "@/types/trainScheduling"; @@ -33,16 +32,31 @@ const windowColor: Record = { export function ScheduleBatchPanel({ schedule }: ScheduleBatchPanelProps) { const { toast } = useToast(); - const actions = useBatchActions(schedule.id); + const actions = { + runBatch: useMutation(api.trainScheduling.runBatch.mutationOptions()), + setWindow: useMutation(api.trainScheduling.setBookingWindow.mutationOptions()), + markPaid: useMutation(api.trainScheduling.markBookingPaid.mutationOptions()), + expire: useMutation(api.trainScheduling.expireBooking.mutationOptions()), + moveSchedule: useMutation( + api.trainScheduling.moveBookingSchedule.mutationOptions(), + ), + }; const windowStatus = (schedule as { bookingWindowStatus?: string }).bookingWindowStatus ?? "OPEN"; const locked = schedule.status === "DISPATCHED" || schedule.status === "ARRIVED"; const [moveBookingId, setMoveBookingId] = useState(null); const [moveTarget, setMoveTarget] = useState(null); - const { data: targets } = useBookableSchedules( - schedule.originStation?.id, - schedule.destinationStation?.id, + const { data: targets } = useQuery( + api.trainScheduling.bookableSchedules.queryOptions({ + input: { + originYardId: schedule.originStation?.id, + destinationYardId: schedule.destinationStation?.id, + }, + enabled: Boolean( + schedule.originStation?.id && schedule.destinationStation?.id, + ), + }), ); const moveOptions = useMemo( () => diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/AssignedBookingsPanel.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/AssignedBookingsPanel.tsx index c1ce3a96d..914d5cb60 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/AssignedBookingsPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/AssignedBookingsPanel.tsx @@ -4,7 +4,8 @@ import { Building2, Package, TrainFront, Weight, X } from "lucide-react"; import type { TrainScheduleDetail } from "@/types/trainScheduling"; import type { BookingDetailData } from "./BookingDetailModal"; import { RemoveBookingConfirmModal, type RemovalTarget } from "./RemoveBookingConfirmModal"; -import { useScheduleMutations } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import { freightBrand } from "@/theme/freight-brand"; @@ -22,7 +23,7 @@ export const AssignedBookingsPanel = ({ onSelect, }: AssignedBookingsPanelProps) => { const { toast } = useToast(); - const unassign = useScheduleMutations(scheduleId).unassign; + const unassign = useMutation(api.trainScheduling.unassignBooking.mutationOptions()); const isDispatched = scheduleDetail.status === "DISPATCHED"; const [removalTarget, setRemovalTarget] = useState(null); diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/CompositionBookingTabs.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/CompositionBookingTabs.tsx index 5de67daa6..64cb6d5cb 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/CompositionBookingTabs.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/CompositionBookingTabs.tsx @@ -8,10 +8,8 @@ import { UnassignedBookingsPanel } from "./UnassignedBookingsPanel"; import { RemovalLogPanel } from "./RemovalLogPanel"; import { BatchBookingList } from "./BatchBookingList"; import { BookingDetailModal, type BookingDetailData } from "./BookingDetailModal"; -import { - useCompositionRemovals, - useUnassignedBookings, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { freightBrand } from "@/theme/freight-brand"; interface CompositionBookingTabsProps { @@ -47,8 +45,18 @@ export const CompositionBookingTabs = ({ const [detailBooking, setDetailBooking] = useState(null); const [tab, setTab] = useState("assigned"); - const unassignedQuery = useUnassignedBookings(scheduleId); - const removalsQuery = useCompositionRemovals(scheduleId); + const unassignedQuery = useQuery( + api.trainScheduling.unassignedBookings.queryOptions({ + input: { scheduleId }, + enabled: Boolean(scheduleId), + }), + ); + const removalsQuery = useQuery( + api.trainScheduling.compositionRemovals.queryOptions({ + input: { scheduleId }, + enabled: Boolean(scheduleId), + }), + ); const { assignedCount } = useMemo(() => { const wagons = scheduleDetail.trainSet?.wagons ?? []; diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/ContainerNumberInput.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/ContainerNumberInput.tsx index 5629ba698..bb25b51bb 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/ContainerNumberInput.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/ContainerNumberInput.tsx @@ -1,6 +1,7 @@ import { useState } from "react"; import { Group, TextInput, Text } from "@mantine/core"; -import { useUpdateContainerItem } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation } from "@tanstack/react-query"; +import { api } from "@/services/api"; interface ContainerNumberInputProps { value: string | null; @@ -19,13 +20,16 @@ export const ContainerNumberInput = ({ const [inputValue, setInputValue] = useState(value ?? ""); const [error, setError] = useState(null); - const updateMutation = useUpdateContainerItem(scheduleId); + const updateMutation = useMutation( + api.trainScheduling.updateContainerItem.mutationOptions(), + ); const isLoading = updateMutation.isPending; const handleSave = async () => { try { setError(null); await updateMutation.mutateAsync({ + scheduleId, itemId, containerNumber: inputValue || null, }); diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/RemovalLogPanel.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/RemovalLogPanel.tsx index 05f2a8328..6682cd795 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/RemovalLogPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/RemovalLogPanel.tsx @@ -1,13 +1,19 @@ import { Box, Card, Group, Stack, Text, ThemeIcon } from "@mantine/core"; import { History, PackageX } from "lucide-react"; -import { useCompositionRemovals } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; interface RemovalLogPanelProps { scheduleId: string; } export const RemovalLogPanel = ({ scheduleId }: RemovalLogPanelProps) => { - const removalQuery = useCompositionRemovals(scheduleId); + const removalQuery = useQuery( + api.trainScheduling.compositionRemovals.queryOptions({ + input: { scheduleId }, + enabled: Boolean(scheduleId), + }), + ); if (removalQuery.isLoading) { return ( diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/TrainConsistView.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/TrainConsistView.tsx index 43e8c58d2..2ebfbb8ff 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/TrainConsistView.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/TrainConsistView.tsx @@ -6,7 +6,8 @@ import { TrainStatsBar } from "./TrainStatsBar"; import { WagonCard } from "./WagonCard"; import { InteractiveTrainConsist } from "./InteractiveTrainConsist"; import { RemoveBookingModal } from "./RemoveBookingModal"; -import { useScheduleMutations, useRemoveWagonSlot } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { freightBrand } from "@/theme/freight-brand"; type Wagon = TrainScheduleDetail["trainSet"]["wagons"][number]; @@ -47,8 +48,12 @@ export const TrainConsistView = ({ const [selectedWagonId, setSelectedWagonId] = useState(null); const [removeModalOpen, setRemoveModalOpen] = useState(false); - const unassignMutation = useScheduleMutations(scheduleId).unassign; - const removeWagonMutation = useRemoveWagonSlot(scheduleId); + const unassignMutation = useMutation( + api.trainScheduling.unassignBooking.mutationOptions(), + ); + const removeWagonMutation = useMutation( + api.trainScheduling.removeWagonSlot.mutationOptions(), + ); const trainSet = scheduleDetail.trainSet; const wagons = trainSet?.wagons ?? []; @@ -83,7 +88,7 @@ export const TrainConsistView = ({ const handleRemoveWagon = async (wagonId: string) => { if (confirm("Are you sure you want to remove this wagon slot?")) { - await removeWagonMutation.mutateAsync(wagonId); + await removeWagonMutation.mutateAsync({ scheduleId, wagonId }); setSelectedWagonId(null); } }; diff --git a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/UnassignedBookingsPanel.tsx b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/UnassignedBookingsPanel.tsx index 48928be88..e5225f8dd 100644 --- a/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/UnassignedBookingsPanel.tsx +++ b/apps/edr-freight-web/backoffice/src/components/trainScheduling/compositionEditor/UnassignedBookingsPanel.tsx @@ -1,9 +1,7 @@ import { Badge, Box, Button, Card, Group, Stack, Text, ThemeIcon, Tooltip } from "@mantine/core"; import { AlertTriangle, Container as ContainerIcon, MapPin, Plus, TrainFront } from "lucide-react"; -import { - useUnassignedBookings, - useScheduleMutations, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { FleetAvailabilityRow } from "@/types/trainScheduling"; import type { BookingDetailData } from "./BookingDetailModal"; @@ -73,8 +71,15 @@ export const UnassignedBookingsPanel = ({ onSelect, }: UnassignedBookingsPanelProps) => { const { toast } = useToast(); - const unassignedQuery = useUnassignedBookings(scheduleId); - const assignMutation = useScheduleMutations(scheduleId).assignUnassigned; + const unassignedQuery = useQuery( + api.trainScheduling.unassignedBookings.queryOptions({ + input: { scheduleId }, + enabled: Boolean(scheduleId), + }), + ); + const assignMutation = useMutation( + api.trainScheduling.assignUnassignedBooking.mutationOptions(), + ); const handleAssign = async (bookingId: string, reference: string | null) => { try { diff --git a/apps/edr-freight-web/backoffice/src/components/wagons/AssignWagonDialog.tsx b/apps/edr-freight-web/backoffice/src/components/wagons/AssignWagonDialog.tsx index 07565163b..0c569e753 100644 --- a/apps/edr-freight-web/backoffice/src/components/wagons/AssignWagonDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/components/wagons/AssignWagonDialog.tsx @@ -4,17 +4,18 @@ import { Button, Group, Modal, NumberInput, Select, Stack, Text } from "@mantine import { Freight } from "@edr/types"; +import { useMutation, useQuery } from "@tanstack/react-query"; + +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; -import { useRouteYards } from "@/hooks/useRoutes"; -import { useAssignWagonToTrain, useWagons } from "@/hooks/useWagons"; export function AssignWagonDialog({ trainId }: { trainId: string }) { const [open, setOpen] = useState(false); const [wagonId, setWagonId] = useState(null); const [sequence, setSequence] = useState(""); - const { data: wagons } = useWagons(); - const { data: yards = [] } = useRouteYards(); - const assign = useAssignWagonToTrain(); + const { data: wagons } = useQuery(api.wagons.list.queryOptions({ input: {} })); + const { data: yards = [] } = useQuery(api.routes.yards.queryOptions()); + const assign = useMutation(api.wagons.assignToTrain.mutationOptions()); const { toast } = useToast(); const available = (wagons ?? []).filter( diff --git a/apps/edr-freight-web/backoffice/src/components/wagons/WagonsTable.tsx b/apps/edr-freight-web/backoffice/src/components/wagons/WagonsTable.tsx index d9d96c117..33a7438fe 100644 --- a/apps/edr-freight-web/backoffice/src/components/wagons/WagonsTable.tsx +++ b/apps/edr-freight-web/backoffice/src/components/wagons/WagonsTable.tsx @@ -3,15 +3,22 @@ import { Trash2 } from "lucide-react"; import type { ColumnDef } from "@edr/ui-common"; import { ActionIcon, Badge, Group, Text, Tooltip } from "@mantine/core"; +import { useMutation, useQuery } from "@tanstack/react-query"; + import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; -import { useUnassignWagon, useWagonsByTrain } from "@/hooks/useWagons"; import type { Wagon } from "@/services/wagon.service"; import { DataTable } from "@edr/ui-common"; export function WagonsTable({ trainId }: { trainId: string }) { - const { data: wagons = [], isLoading, refetch } = useWagonsByTrain(trainId); - const unassign = useUnassignWagon(); + const { data: wagons = [], isLoading, refetch } = useQuery( + api.wagons.listByTrain.queryOptions({ + input: { trainId }, + enabled: !!trainId, + }), + ); + const unassign = useMutation(api.wagons.unassign.mutationOptions()); const { toast } = useToast(); const columns = useMemo((): ColumnDef[] => { diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/CreateWarehouseModal.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/CreateWarehouseModal.tsx index cc8557be2..ad8b4109d 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/CreateWarehouseModal.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/CreateWarehouseModal.tsx @@ -9,11 +9,10 @@ import { TextInput, } from '@mantine/core'; -import { useMutation } from '@tanstack/react-query'; +import { useMutation, useQuery } from '@tanstack/react-query'; import { api } from '@/services/api'; import { useToast } from '@/hooks/use-toast'; -import { useStations } from '@/hooks/useStations'; import type { SaveWarehousePayload, Warehouse, WarehouseType } from '@/types/warehouse'; import { extractErrorMessage, statusOptions, warehouseTypeOptions } from './options'; @@ -52,7 +51,9 @@ export function CreateWarehouseModal({ opened, onClose, warehouse }: CreateWareh const { toast } = useToast(); const createMutation = useMutation(api.warehouses.create.mutationOptions()); const updateMutation = useMutation(api.warehouses.update.mutationOptions()); - const { data: stations } = useStations(); + const { data: stations } = useQuery( + api.stations.list.queryOptions({ staleTime: 5 * 60 * 1000 }), + ); const [form, setForm] = useState(emptyForm()); const stationOptions = (stations ?? []).map((s) => ({ value: s.id, label: `${s.name} (${s.code})` })); diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseCardView.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseCardView.tsx index 2d3dd2276..bd76b5e10 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseCardView.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseCardView.tsx @@ -2,7 +2,9 @@ import { useMemo } from 'react'; import { ActionIcon, Card, Group, SimpleGrid, Stack, Text } from '@mantine/core'; import { Building2, Eye, MapPin, Pencil } from 'lucide-react'; -import { useStations } from '@/hooks/useStations'; +import { useQuery } from '@tanstack/react-query'; + +import { api } from '@/services/api'; import type { Warehouse } from '@/types/warehouse'; import { WarehouseStatusBadge, WarehouseTypeBadge } from './badges'; import { formatCapacity } from './options'; @@ -14,7 +16,9 @@ interface WarehouseCardViewProps { } export function WarehouseCardView({ warehouses, onView, onEdit }: WarehouseCardViewProps) { - const { data: stations } = useStations(); + const { data: stations } = useQuery( + api.stations.list.queryOptions({ staleTime: 5 * 60 * 1000 }), + ); const stationNameById = useMemo( () => new Map((stations ?? []).map((s) => [s.id, s.name])), [stations], diff --git a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseTable.tsx b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseTable.tsx index 80b85f693..7f8dea0e5 100644 --- a/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseTable.tsx +++ b/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseTable.tsx @@ -3,7 +3,9 @@ import { ActionIcon, Group, Text } from '@mantine/core'; import { Eye, Pencil } from 'lucide-react'; import { DataTable, type ColumnDef } from '@edr/ui-common'; -import { useStations } from '@/hooks/useStations'; +import { useQuery } from '@tanstack/react-query'; + +import { api } from '@/services/api'; import type { Warehouse } from '@/types/warehouse'; import { WarehouseStatusBadge, WarehouseTypeBadge } from './badges'; import { formatCapacity } from './options'; @@ -15,7 +17,9 @@ interface WarehouseTableProps { } export function WarehouseTable({ warehouses, onView, onEdit }: WarehouseTableProps) { - const { data: stations } = useStations(); + const { data: stations } = useQuery( + api.stations.list.queryOptions({ staleTime: 5 * 60 * 1000 }), + ); const stationNameById = useMemo( () => new Map((stations ?? []).map((s) => [s.id, s.name])), [stations], diff --git a/apps/edr-freight-web/backoffice/src/constants/QUERY_KEYS.ts b/apps/edr-freight-web/backoffice/src/constants/QUERY_KEYS.ts index 65667fe19..8d91b6f0d 100644 --- a/apps/edr-freight-web/backoffice/src/constants/QUERY_KEYS.ts +++ b/apps/edr-freight-web/backoffice/src/constants/QUERY_KEYS.ts @@ -1,9 +1,9 @@ -import type { BookingListFilter } from "@/services/bookings.service"; -import type { CompanyListFilter } from "@/types/customer"; -import type { RuleEngineListParams } from "@/services/ruleEngine/ruleEngine.service"; -import type { TrainScheduleFilters } from "@/types/trainScheduling"; import type { FleetResourceSlug } from "@/pages/fleet/config/resources"; +import type { BookingListFilter } from "@/services/bookings.service"; +import type { RuleEngineListParams } from "@/services/ruleEngine/ruleEngine.service"; +import type { CompanyListFilter } from "@/types/customer"; import type { RuleEngineResourceSlug } from "@/types/rule-engine"; +import type { TrainScheduleFilters } from "@/types/trainScheduling"; export const QUERY_KEYS = { USERS: { diff --git a/apps/edr-freight-web/backoffice/src/hooks/fleet/useFleet.ts b/apps/edr-freight-web/backoffice/src/hooks/fleet/useFleet.ts deleted file mode 100644 index 29e7bf62b..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/fleet/useFleet.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; - -import { QUERY_KEYS } from "@/constants/QUERY_KEYS"; -import type { FleetListFilters, FleetResourceSlug } from "@/services/fleet/fleet.service"; -import { fleetService } from "@/services/fleet/fleet.service"; - -export function useFleetList(slug: FleetResourceSlug, filters?: FleetListFilters) { - return useQuery({ - queryKey: [...QUERY_KEYS.FLEET.list(slug), filters ?? {}], - queryFn: () => fleetService.list(slug, filters), - }); -} - -export function useFleetMutations(slug: FleetResourceSlug) { - const qc = useQueryClient(); - const invalidate = () => qc.invalidateQueries({ queryKey: QUERY_KEYS.FLEET.list(slug) }); - - const create = useMutation({ - mutationFn: (data: Record) => fleetService.create(slug, data), - onSuccess: invalidate, - }); - - const update = useMutation({ - mutationFn: ({ id, data }: { id: string; data: Record }) => - fleetService.update(slug, id, data), - onSuccess: invalidate, - }); - - const remove = useMutation({ - mutationFn: (id: string) => fleetService.remove(slug, id), - onSuccess: invalidate, - }); - - return { create, update, remove }; -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/trainScheduling/useTrainScheduling.ts b/apps/edr-freight-web/backoffice/src/hooks/trainScheduling/useTrainScheduling.ts deleted file mode 100644 index 823d13324..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/trainScheduling/useTrainScheduling.ts +++ /dev/null @@ -1,325 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; - -import { QUERY_KEYS } from "@/constants/QUERY_KEYS"; -import { trainSchedulingService } from "@/services/trainScheduling.service"; -import type { - AssignBookingsPayload, - CreateTrainSchedulePayload, - FreightType, - PinWagonsPayload, - RecordCheckpointPayload, - TrainScheduleFilters, - TrainSchedulePreviewPayload, -} from "@/types/trainScheduling"; - -export const useScheduleList = (freightType?: FreightType) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.schedules(), - queryFn: () => trainSchedulingService.listSchedules(freightType), - }); - -export const useBatchBoard = () => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoard(), - queryFn: () => trainSchedulingService.getBatchBoard(), - refetchInterval: 30_000, - }); - -export const useBatchBoardDetail = (scheduleId: string | undefined) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoardDetail(scheduleId ?? ""), - queryFn: () => trainSchedulingService.getBatchBoardDetail(scheduleId!), - enabled: Boolean(scheduleId), - refetchInterval: 30_000, - }); - -export const useRunAllocation = (scheduleId: string) => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: () => trainSchedulingService.runAllocation(scheduleId), - onSuccess: () => { - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoardDetail(scheduleId), - }); - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoard() }); - }, - }); -}; - -export const useScheduleDetail = (id: string | undefined, freightType?: FreightType) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(id ?? ""), - queryFn: () => trainSchedulingService.getScheduleById(id!, freightType), - enabled: Boolean(id), - }); - -export const useEligibleBookings = ( - filters?: TrainScheduleFilters, - enabled = true, - freightType?: FreightType, -) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.eligible(freightType, filters), - queryFn: () => trainSchedulingService.getEligibleBookings(filters, freightType), - enabled, - }); - -export const useAvailableLocomotives = (routeId?: string) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.locomotives(routeId), - queryFn: () => trainSchedulingService.getAvailableLocomotives(routeId), - enabled: routeId ? Boolean(routeId) : true, - }); - -export const useBatchActions = (scheduleId?: string) => { - const qc = useQueryClient(); - const invalidate = () => { - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.ROOT }); - void qc.invalidateQueries({ queryKey: QUERY_KEYS.BOOKINGS.ROOT }); - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoard() }); - if (scheduleId) { - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(scheduleId), - }); - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoardDetail(scheduleId), - }); - } - }; - - const runBatch = useMutation({ - mutationFn: (id: string) => trainSchedulingService.runBatch(id), - onSuccess: invalidate, - }); - const setWindow = useMutation({ - mutationFn: ({ id, status }: { id: string; status: "OPEN" | "CLOSED" }) => - trainSchedulingService.setBookingWindow(id, status), - onSuccess: invalidate, - }); - const markPaid = useMutation({ - mutationFn: (bookingId: string) => trainSchedulingService.markBookingPaid(bookingId), - onSuccess: invalidate, - }); - const expire = useMutation({ - mutationFn: (bookingId: string) => trainSchedulingService.expireBooking(bookingId), - onSuccess: invalidate, - }); - const moveSchedule = useMutation({ - mutationFn: ({ bookingId, trainScheduleId }: { bookingId: string; trainScheduleId: string }) => - trainSchedulingService.moveBookingSchedule(bookingId, trainScheduleId), - onSuccess: invalidate, - }); - - return { runBatch, setWindow, markPaid, expire, moveSchedule, invalidate }; -}; - -export const useBookableSchedules = ( - originYardId?: string | null, - destinationYardId?: string | null, -) => - useQuery({ - queryKey: [ - ...QUERY_KEYS.TRAIN_SCHEDULING.ROOT, - "bookable", - originYardId ?? "", - destinationYardId ?? "", - ], - queryFn: () => - trainSchedulingService.getBookableSchedules( - originYardId ?? undefined, - destinationYardId ?? undefined, - ), - enabled: Boolean(originYardId && destinationYardId), - }); - -/** - * Day-level pool: which days have an OPEN departure on the route. Staff pick a - * day (not a train) when creating a booking; the engine assigns the train. - */ -export const useAvailableDays = ( - originYardId?: string | null, - destinationYardId?: string | null, -) => - useQuery({ - queryKey: [ - ...QUERY_KEYS.TRAIN_SCHEDULING.ROOT, - "available-days", - originYardId ?? "", - destinationYardId ?? "", - ], - queryFn: () => - trainSchedulingService.getAvailableDays( - originYardId ?? undefined, - destinationYardId ?? undefined, - ), - enabled: Boolean(originYardId && destinationYardId), - }); - -export const useTrainTrack = (id: string | undefined) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.track(id ?? ""), - queryFn: () => trainSchedulingService.getTrack(id!), - enabled: Boolean(id), - }); - -export const useScheduleMutations = (scheduleId?: string) => { - const qc = useQueryClient(); - - const invalidate = () => { - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.ROOT }); - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.schedules() }); - void qc.invalidateQueries({ queryKey: QUERY_KEYS.TRAIN_SCHEDULING.locomotives() }); - if (scheduleId) { - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(scheduleId), - }); - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.track(scheduleId), - }); - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.unassignedBookings(scheduleId), - }); - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.compositionRemovals(scheduleId), - }); - } - void qc.invalidateQueries({ queryKey: QUERY_KEYS.BOOKINGS.ROOT }); - }; - - const create = useMutation({ - mutationFn: ({ - freightType, - payload, - }: { - freightType?: FreightType; - payload: CreateTrainSchedulePayload; - }) => trainSchedulingService.createSchedule(payload, freightType), - onSuccess: invalidate, - }); - - const preview = useMutation({ - mutationFn: ({ - freightType, - payload, - }: { - freightType?: FreightType; - payload: TrainSchedulePreviewPayload; - }) => trainSchedulingService.preview(payload, freightType), - }); - - const assign = useMutation({ - mutationFn: ({ - id, - freightType, - payload, - }: { - id: string; - freightType?: FreightType; - payload: AssignBookingsPayload; - }) => trainSchedulingService.assignBookings(id, payload, freightType), - onSuccess: invalidate, - }); - - const assignUnassigned = useMutation({ - mutationFn: ({ id, bookingId }: { id: string; bookingId: string }) => - trainSchedulingService.assignUnassignedBooking(id, bookingId), - onSuccess: invalidate, - }); - - const unassign = useMutation({ - mutationFn: ({ id, bookingId }: { id: string; bookingId: string }) => - trainSchedulingService.unassignBooking(id, bookingId), - onSuccess: invalidate, - }); - - const pin = useMutation({ - mutationFn: ({ id, payload }: { id: string; payload: PinWagonsPayload }) => - trainSchedulingService.pinWagons(id, payload), - onSuccess: invalidate, - }); - - const finalize = useMutation({ - mutationFn: (id: string) => trainSchedulingService.finalizeSchedule(id), - onSuccess: invalidate, - }); - - const dispatch = useMutation({ - mutationFn: (id: string) => trainSchedulingService.dispatchSchedule(id), - onSuccess: invalidate, - }); - - const cancel = useMutation({ - mutationFn: ({ id, freightType }: { id: string; freightType?: FreightType }) => - trainSchedulingService.cancelSchedule(id, freightType ?? "CONTAINER"), - onSuccess: invalidate, - }); - - const recordCheckpoint = useMutation({ - mutationFn: ({ id, payload }: { id: string; payload: RecordCheckpointPayload }) => - trainSchedulingService.recordCheckpoint(id, payload), - onSuccess: invalidate, - }); - - const arrive = useMutation({ - mutationFn: (id: string) => trainSchedulingService.arriveSchedule(id), - onSuccess: invalidate, - }); - - return { - create, - preview, - assign, - assignUnassigned, - unassign, - pin, - finalize, - dispatch, - cancel, - recordCheckpoint, - arrive, - invalidate, - }; -}; - -export const useUnassignedBookings = (scheduleId: string | undefined) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.unassignedBookings(scheduleId ?? ""), - queryFn: () => trainSchedulingService.getUnassignedBookings(scheduleId!), - enabled: Boolean(scheduleId), - }); - -export const useCompositionRemovals = (scheduleId: string | undefined) => - useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.compositionRemovals(scheduleId ?? ""), - queryFn: () => trainSchedulingService.getCompositionRemovals(scheduleId!), - enabled: Boolean(scheduleId), - }); - -export const useRemoveWagonSlot = (scheduleId: string) => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (wagonId: string) => - trainSchedulingService.removeWagonSlot(scheduleId, wagonId), - onSuccess: () => { - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(scheduleId), - }); - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.batchBoardDetail(scheduleId), - }); - }, - }); -}; - -export const useUpdateContainerItem = (scheduleId: string) => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ itemId, containerNumber }: { itemId: string; containerNumber: string | null }) => - trainSchedulingService.updateContainerItem(scheduleId, itemId, { containerNumber }), - onSuccess: () => { - void qc.invalidateQueries({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(scheduleId), - }); - }, - }); -}; diff --git a/apps/edr-freight-web/backoffice/src/hooks/use-cargo-types.ts b/apps/edr-freight-web/backoffice/src/hooks/use-cargo-types.ts deleted file mode 100644 index 864e9732c..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/use-cargo-types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { cargoTypesService } from '@/services/cargo-types.service'; - -export const CARGO_TYPES_QUERY_KEY = ['cargo-types']; - -export function useCargoTypes() { - return useQuery({ - queryKey: CARGO_TYPES_QUERY_KEY, - queryFn: () => cargoTypesService.getCargoTypes(), - staleTime: Infinity, - }); -} \ No newline at end of file diff --git a/apps/edr-freight-web/backoffice/src/hooks/use-container-types.ts b/apps/edr-freight-web/backoffice/src/hooks/use-container-types.ts deleted file mode 100644 index c216c2adf..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/use-container-types.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; -import { containerTypesService } from '@/services/container-types.service'; - -export const CONTAINER_TYPES_QUERY_KEY = ['container-types']; - -export function useContainerTypes() { - return useQuery({ - queryKey: CONTAINER_TYPES_QUERY_KEY, - queryFn: () => containerTypesService.getContainerTypes(), - staleTime: Infinity, - }); -} \ No newline at end of file diff --git a/apps/edr-freight-web/backoffice/src/hooks/use-wagon-types.ts b/apps/edr-freight-web/backoffice/src/hooks/use-wagon-types.ts deleted file mode 100644 index 88566c776..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/use-wagon-types.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { wagonTypesService } from '@/services/wagon-types.service'; - -export const WAGON_TYPES_QUERY_KEY = ['wagon-types']; - -export function useWagonTypes() { - return useQuery({ - queryKey: WAGON_TYPES_QUERY_KEY, - queryFn: () => wagonTypesService.getWagonTypes(), - }); -} - -export function useCreateWagonType() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: wagonTypesService.create, - onSuccess: () => qc.invalidateQueries({ queryKey: WAGON_TYPES_QUERY_KEY }), - }); -} - -export function useUpdateWagonType() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ id, data }: { id: string; data: Record }) => - wagonTypesService.update(id, data), - onSuccess: () => qc.invalidateQueries({ queryKey: WAGON_TYPES_QUERY_KEY }), - }); -} - -export function useDeleteWagonType() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: wagonTypesService.delete, - onSuccess: () => qc.invalidateQueries({ queryKey: WAGON_TYPES_QUERY_KEY }), - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useContainers.ts b/apps/edr-freight-web/backoffice/src/hooks/useContainers.ts deleted file mode 100644 index b14d9b5c5..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useContainers.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { containerService } from '@/services/containerService'; - -export const containerKeys = { - all: ['containers'] as const, - byWagon: (wagonId: string) => [...containerKeys.all, 'wagon', wagonId] as const, - details: () => [...containerKeys.all, 'detail'] as const, - detail: (id: string) => [...containerKeys.details(), id] as const, -}; - -export function useContainers() { - return useQuery({ queryKey: containerKeys.all, queryFn: () => containerService.getAll().then(res => res.data) }); -} - -export const useGetContainers = useContainers; - -export function useContainersByWagon(wagonId: string) { - return useQuery({ queryKey: containerKeys.byWagon(wagonId), queryFn: () => containerService.getByWagon(wagonId).then(res => res.data), enabled: !!wagonId }); -} - -export function useContainer(id: string) { - return useQuery({ queryKey: containerKeys.detail(id), queryFn: () => containerService.getById(id).then(res => res.data), enabled: !!id }); -} - -export const useGetContainer = useContainer; - -export function useCreateContainer() { - const qc = useQueryClient(); - return useMutation({ mutationFn: containerService.create, onSuccess: () => qc.invalidateQueries({ queryKey: containerKeys.all }) }); -} - -export function useUpdateContainer() { - const qc = useQueryClient(); - return useMutation({ mutationFn: ({ id, data }: any) => containerService.update(id, data), onSuccess: (_, { id }) => { - qc.invalidateQueries({ queryKey: containerKeys.all }); - qc.invalidateQueries({ queryKey: containerKeys.detail(id) }); - } }); -} - -export function useDeleteContainer() { - const qc = useQueryClient(); - return useMutation({ mutationFn: containerService.delete, onSuccess: () => qc.invalidateQueries({ queryKey: containerKeys.all }) }); -} - -export function useAssignContainerToWagon() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ containerId, wagonId, position }: any) => containerService.assignToWagon(containerId, wagonId, position), - onSuccess: (_, { wagonId }) => qc.invalidateQueries({ queryKey: containerKeys.byWagon(wagonId) }) - }); -} - -export function useUnassignContainer() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: containerService.unassign, - onSuccess: () => qc.invalidateQueries({ queryKey: containerKeys.all }) - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useDropdownSettings.ts b/apps/edr-freight-web/backoffice/src/hooks/useDropdownSettings.ts deleted file mode 100644 index 256fc1702..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useDropdownSettings.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import { api } from "@/services/api"; -import type { - CreateDropdownOptionDto, - CreateDropdownSettingDto, - UpdateDropdownOptionDto, - UpdateDropdownSettingDto, -} from "@/types/dropdownSettings"; - -/* ----------------------------- Mutations ----------------------------- */ - -export const useCreateDropdownSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (dto: CreateDropdownSettingDto) => - api.dropdownSettings.create.call(dto), - onSuccess: () => - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }), - }); -}; - -export const useUpdateDropdownSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - id, - dto, - }: { - id: string; - dto: UpdateDropdownSettingDto; - }) => api.dropdownSettings.update.call({ id, dto }), - onSuccess: (_data, { id }) => { - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }); - qc.invalidateQueries({ - queryKey: api.dropdownSettings.getById.queryKey({ id }), - }); - }, - }); -}; - -export const useDeleteDropdownSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (id: string) => api.dropdownSettings.remove.call({ id }), - onSuccess: () => - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }), - }); -}; - -export const useReplaceDropdownOptions = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - settingId, - options, - }: { - settingId: string; - options: CreateDropdownOptionDto[]; - }) => api.dropdownSettings.replaceOptions.call({ id: settingId, options }), - onSuccess: (_data, { settingId }) => { - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }); - qc.invalidateQueries({ - queryKey: api.dropdownSettings.getById.queryKey({ id: settingId }), - }); - }, - }); -}; - -export const useAddDropdownOption = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - settingId, - dto, - }: { - settingId: string; - dto: CreateDropdownOptionDto; - }) => api.dropdownSettings.addOption.call({ id: settingId, dto }), - onSuccess: (_data, { settingId }) => { - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }); - qc.invalidateQueries({ - queryKey: api.dropdownSettings.getById.queryKey({ id: settingId }), - }); - }, - }); -}; - -export const useUpdateDropdownOption = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - optionId, - dto, - }: { - optionId: string; - dto: UpdateDropdownOptionDto; - }) => api.dropdownSettings.updateOption.call({ optionId, dto }), - onSuccess: () => - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }), - }); -}; - -export const useRemoveDropdownOption = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (optionId: string) => - api.dropdownSettings.removeOption.call({ optionId }), - onSuccess: () => - qc.invalidateQueries({ queryKey: api.dropdownSettings.list.queryKey() }), - }); -}; diff --git a/apps/edr-freight-web/backoffice/src/hooks/useFacilities.ts b/apps/edr-freight-web/backoffice/src/hooks/useFacilities.ts deleted file mode 100644 index 481378ba7..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useFacilities.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { facilityService } from '@/services/facility.service'; - -export const facilityKeys = { - all: ['facilities'] as const, - list: () => ['facilities', 'list'] as const, - detail: (id: string) => ['facilities', 'detail', id] as const, -}; - -export function useFacilities() { - return useQuery({ - queryKey: facilityKeys.list(), - queryFn: () => facilityService.list().then((r) => r.data), - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useFileUploadSettings.ts b/apps/edr-freight-web/backoffice/src/hooks/useFileUploadSettings.ts deleted file mode 100644 index 748de4f88..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useFileUploadSettings.ts +++ /dev/null @@ -1,126 +0,0 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; - -import { api } from "@/services/api"; -import type { - CreateFileUploadFieldDto, - CreateFileUploadSettingDto, - UpdateFileUploadFieldDto, - UpdateFileUploadSettingDto, -} from "@/types/fileUploadSettings"; - -/* ----------------------------- Mutations ----------------------------- */ - -export const useCreateFileUploadSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (dto: CreateFileUploadSettingDto) => - api.fileUploadSettings.create.call(dto), - onSuccess: () => - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }), - }); -}; - -export const useUpdateFileUploadSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - id, - dto, - }: { - id: string; - dto: UpdateFileUploadSettingDto; - }) => api.fileUploadSettings.update.call({ id, dto }), - onSuccess: (_data, { id }) => { - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }); - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.getById.queryKey({ id }), - }); - }, - }); -}; - -export const useDeleteFileUploadSetting = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (id: string) => api.fileUploadSettings.remove.call({ id }), - onSuccess: () => - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }), - }); -}; - -export const useReplaceFileUploadFields = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - settingId, - fields, - }: { - settingId: string; - fields: CreateFileUploadFieldDto[]; - }) => api.fileUploadSettings.replaceFields.call({ id: settingId, fields }), - onSuccess: (_data, { settingId }) => { - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }); - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.getById.queryKey({ id: settingId }), - }); - }, - }); -}; - -export const useAddFileUploadField = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - settingId, - dto, - }: { - settingId: string; - dto: CreateFileUploadFieldDto; - }) => api.fileUploadSettings.addField.call({ settingId, dto }), - onSuccess: (_data, { settingId }) => { - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }); - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.getById.queryKey({ id: settingId }), - }); - }, - }); -}; - -export const useUpdateFileUploadField = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ - fieldId, - dto, - }: { - fieldId: string; - dto: UpdateFileUploadFieldDto; - }) => api.fileUploadSettings.updateField.call({ fieldId, dto }), - onSuccess: () => - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }), - }); -}; - -export const useRemoveFileUploadField = () => { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (fieldId: string) => - api.fileUploadSettings.removeField.call({ fieldId }), - onSuccess: () => - qc.invalidateQueries({ - queryKey: api.fileUploadSettings.list.queryKey(), - }), - }); -}; diff --git a/apps/edr-freight-web/backoffice/src/hooks/useLocomotives.ts b/apps/edr-freight-web/backoffice/src/hooks/useLocomotives.ts deleted file mode 100644 index ee50bd59e..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useLocomotives.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; - -import { locomotivesService } from '@/services/locomotives.service'; - -export const locomotiveKeys = { - all: ['locomotives'] as const, - details: () => [...locomotiveKeys.all, 'detail'] as const, - detail: (id: string) => [...locomotiveKeys.details(), id] as const, -}; - -export function useLocomotives() { - return useQuery({ - queryKey: locomotiveKeys.all, - queryFn: () => locomotivesService.getAll().then((response) => response.data), - }); -} - -export function useCreateLocomotive() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: locomotivesService.create, - onSuccess: () => qc.invalidateQueries({ queryKey: locomotiveKeys.all }), - }); -} - -export function useUpdateLocomotive() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ id, data }: { id: string; data: Record }) => - locomotivesService.update(id, data), - onSuccess: (_, { id }) => { - qc.invalidateQueries({ queryKey: locomotiveKeys.all }); - qc.invalidateQueries({ queryKey: locomotiveKeys.detail(id) }); - }, - }); -} - -export function useDecommissionLocomotive() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: locomotivesService.decommission, - onSuccess: (_, id) => { - qc.invalidateQueries({ queryKey: locomotiveKeys.all }); - qc.invalidateQueries({ queryKey: locomotiveKeys.detail(id) }); - }, - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/usePayments.ts b/apps/edr-freight-web/backoffice/src/hooks/usePayments.ts deleted file mode 100644 index e9a09760b..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/usePayments.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useQuery } from "@tanstack/react-query"; - -import { - paymentsService, - type PaymentListFilter, -} from "@/services/payments.service"; - -export function usePaymentList(filter?: PaymentListFilter, enabled = true) { - return useQuery({ - queryKey: ["payments", "list", filter ?? {}], - queryFn: () => paymentsService.list(filter), - enabled, - }); -} - -export function usePaymentSummary(enabled = true) { - return useQuery({ - queryKey: ["payments", "summary"], - queryFn: () => paymentsService.getSummary(), - staleTime: 30_000, - enabled, - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useRoutes.ts b/apps/edr-freight-web/backoffice/src/hooks/useRoutes.ts deleted file mode 100644 index 3ae4ba924..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useRoutes.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; - -import { routesService } from '@/services/routes.service'; - -export const routeKeys = { - all: ['routes'] as const, - yards: ['routes', 'yards'] as const, - details: () => [...routeKeys.all, 'detail'] as const, - detail: (id: string) => [...routeKeys.details(), id] as const, -}; - -export function useRoutes() { - return useQuery({ - queryKey: routeKeys.all, - queryFn: () => routesService.getAll().then((response) => response.data), - }); -} - -export function useRouteYards() { - return useQuery({ - queryKey: routeKeys.yards, - queryFn: () => routesService.getYards().then((response) => response.data.data), - }); -} - -export function useCreateRoute() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: routesService.create, - onSuccess: () => qc.invalidateQueries({ queryKey: routeKeys.all }), - }); -} - -export function useUpdateRoute() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: ({ id, data }: { id: string; data: Record }) => - routesService.update(id, data), - onSuccess: (_, { id }) => { - qc.invalidateQueries({ queryKey: routeKeys.all }); - qc.invalidateQueries({ queryKey: routeKeys.detail(id) }); - }, - }); -} - -export function useDeactivateRoute() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: routesService.deactivate, - onSuccess: (_, id) => { - qc.invalidateQueries({ queryKey: routeKeys.all }); - qc.invalidateQueries({ queryKey: routeKeys.detail(id) }); - }, - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useSavedSignature.ts b/apps/edr-freight-web/backoffice/src/hooks/useSavedSignature.ts deleted file mode 100644 index b8c9480a7..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useSavedSignature.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import toast from "react-hot-toast"; - -import { - signaturesService, - type SaveSignaturePayload, -} from "@/services/signatures.service"; - -const SAVED_SIGNATURE_KEY = ["me", "signature"] as const; - -export function useMySignature() { - return useQuery({ - queryKey: SAVED_SIGNATURE_KEY, - queryFn: () => signaturesService.getMySignature(), - staleTime: 60_000, - }); -} - -export function useSaveSignature() { - const qc = useQueryClient(); - return useMutation({ - mutationFn: (payload: SaveSignaturePayload) => - signaturesService.saveMySignature(payload), - onSuccess: () => { - toast.success("Signature saved"); - void qc.invalidateQueries({ queryKey: SAVED_SIGNATURE_KEY }); - }, - onError: () => toast.error("Failed to save signature"), - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useStations.ts b/apps/edr-freight-web/backoffice/src/hooks/useStations.ts deleted file mode 100644 index b7ab95d0a..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useStations.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { useQuery } from '@tanstack/react-query'; - -import { trainSchedulingService } from '@/services/trainScheduling.service'; -import { QUERY_KEYS } from '@/constants/QUERY_KEYS'; - -/** - * The 21 network stations / yards, sourced from the existing booking - * reference-data API. Reused as the parent "Facility / Port" for warehouses. - */ -export function useStations() { - return useQuery({ - queryKey: QUERY_KEYS.TRAIN_SCHEDULING.stations(), - queryFn: () => trainSchedulingService.getStations(), - staleTime: 5 * 60 * 1000, - }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useTrains.ts b/apps/edr-freight-web/backoffice/src/hooks/useTrains.ts deleted file mode 100644 index c2b24d294..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useTrains.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { trainService } from '@/services/trains.service'; - -export const trainKeys = { - all: ['trains'] as const, - lists: () => [...trainKeys.all, 'list'] as const, - details: () => [...trainKeys.all, 'detail'] as const, - detail: (id: string) => [...trainKeys.details(), id] as const, -}; - -export function useTrains() { - return useQuery({ queryKey: trainKeys.lists(), queryFn: () => trainService.getAll().then(res => res.data) }); -} - -export const useGetTrains = useTrains; - -export function useTrain(id: string) { - return useQuery({ queryKey: trainKeys.detail(id), queryFn: () => trainService.getById(id).then(res => res.data), enabled: !!id }); -} - -export const useGetTrain = useTrain; - -export function useCreateTrain() { - const qc = useQueryClient(); - return useMutation({ mutationFn: trainService.create, onSuccess: () => qc.invalidateQueries({ queryKey: trainKeys.lists() }) }); -} - -export function useUpdateTrain() { - const qc = useQueryClient(); - return useMutation({ mutationFn: ({ id, data }: any) => trainService.update(id, data), onSuccess: (_, { id }) => { - qc.invalidateQueries({ queryKey: trainKeys.lists() }); - qc.invalidateQueries({ queryKey: trainKeys.detail(id) }); - } }); -} - -export function useDeleteTrain() { - const qc = useQueryClient(); - return useMutation({ mutationFn: trainService.delete, onSuccess: () => qc.invalidateQueries({ queryKey: trainKeys.lists() }) }); -} diff --git a/apps/edr-freight-web/backoffice/src/hooks/useWagons.ts b/apps/edr-freight-web/backoffice/src/hooks/useWagons.ts deleted file mode 100644 index b672a2a84..000000000 --- a/apps/edr-freight-web/backoffice/src/hooks/useWagons.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; -import { wagonService } from '@/services/wagon.service'; - -export type WagonListFilters = import('@/services/wagon.service').WagonListFilters; - -export const wagonKeys = { - all: ['wagons'] as const, - list: (filters?: WagonListFilters) => [...wagonKeys.all, 'list', filters ?? {}] as const, - byTrain: (trainId: string) => [...wagonKeys.all, 'train', trainId] as const, - details: () => [...wagonKeys.all, 'detail'] as const, - detail: (id: string) => [...wagonKeys.details(), id] as const, -}; - -export function useWagons(filters?: WagonListFilters) { - return useQuery({ - queryKey: wagonKeys.list(filters), - queryFn: () => wagonService.getAll(filters ?? {}).then((res) => res.data), - }); -} - -export const useGetWagons = useWagons; - -export function useWagonsByTrain(trainId: string) { - return useQuery({ queryKey: wagonKeys.byTrain(trainId), queryFn: () => wagonService.getByTrain(trainId).then(res => res.data), enabled: !!trainId }); -} - -export function useWagon(id: string) { - return useQuery({ queryKey: wagonKeys.detail(id), queryFn: () => wagonService.getById(id).then(res => res.data), enabled: !!id }); -} - -export const useGetWagon = useWagon; - -export function useAssignWagonToTrain() { - const qc = useQueryClient(); - return useMutation({ mutationFn: ({ wagonId, trainId, sequenceNumber }: any) => wagonService.assignToTrain(wagonId, trainId, sequenceNumber), onSuccess: (_, { trainId }) => qc.invalidateQueries({ queryKey: wagonKeys.byTrain(trainId) }) }); -} - -export function useUnassignWagon() { - const qc = useQueryClient(); - return useMutation({ mutationFn: wagonService.unassign, onSuccess: () => qc.invalidateQueries({ queryKey: wagonKeys.all }) }); -} - -export function useReorderWagons() { - const qc = useQueryClient(); - return useMutation({ mutationFn: ({ trainId, wagonIds }: any) => wagonService.reorder(trainId, wagonIds), onSuccess: (_, { trainId }) => qc.invalidateQueries({ queryKey: wagonKeys.byTrain(trainId) }) }); -} - -export function useCreateWagon() { - const qc = useQueryClient(); - return useMutation({ mutationFn: wagonService.create, onSuccess: () => qc.invalidateQueries({ queryKey: wagonKeys.all }) }); -} - -export function useUpdateWagon() { - const qc = useQueryClient(); - return useMutation({ mutationFn: ({ id, data }: any) => wagonService.update(id, data), onSuccess: (_, { id }) => { - qc.invalidateQueries({ queryKey: wagonKeys.all }); - qc.invalidateQueries({ queryKey: wagonKeys.detail(id) }); - } }); -} - -export function useDeleteWagon() { - const qc = useQueryClient(); - return useMutation({ mutationFn: wagonService.delete, onSuccess: () => qc.invalidateQueries({ queryKey: wagonKeys.all }) }); -} diff --git a/apps/edr-freight-web/backoffice/src/pages/bookings/NewBookingPage.tsx b/apps/edr-freight-web/backoffice/src/pages/bookings/NewBookingPage.tsx index 3f5323c7e..b3c9db35d 100644 --- a/apps/edr-freight-web/backoffice/src/pages/bookings/NewBookingPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/bookings/NewBookingPage.tsx @@ -1,7 +1,3 @@ -import { useEffect, useMemo, useState } from "react"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; -import { useNavigate } from "react-router-dom"; -import { isAxiosError } from "axios"; import { ActionIcon, Badge, @@ -23,6 +19,8 @@ import { ThemeIcon, Tooltip, } from "@mantine/core"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { isAxiosError } from "axios"; import { AlertTriangle, ArrowLeft, @@ -40,14 +38,16 @@ import { Trash2, Weight, } from "lucide-react"; +import { useEffect, useMemo, useState } from "react"; import toast from "react-hot-toast"; +import { useNavigate } from "react-router-dom"; -import Breadcrumbs from "@/components/ui/Breadcrumbs"; -import { bookingsService } from "@/services/bookings.service"; -import { useAvailableDays } from "@/hooks/trainScheduling/useTrainScheduling"; import { api } from "@/auth/http"; -import { unwrap } from "@/utils/endpoint"; +import Breadcrumbs from "@/components/ui/Breadcrumbs"; import { URL_CONSTANTS } from "@/constants/URLS"; +import { api as appApi } from "@/services/api"; +import { bookingsService } from "@/services/bookings.service"; +import { unwrap } from "@/utils/endpoint"; interface CompanyOption { id: string; @@ -234,9 +234,11 @@ export default function NewBookingPage() { // Day-level pool: fetch only the days that have a departure on the route (no // train, no capacity). The batch engine assigns the train after booking. - const { data: availableDays, isLoading: daysLoading } = useAvailableDays( - originYardId, - destinationYardId, + const { data: availableDays, isLoading: daysLoading } = useQuery( + appApi.trainScheduling.availableDays.queryOptions({ + input: { originYardId, destinationYardId }, + enabled: Boolean(originYardId && destinationYardId), + }), ); const dayOptions = (availableDays ?? []).map((day) => ({ value: day, @@ -392,7 +394,7 @@ export default function NewBookingPage() { - + {/* LEFT — form */} @@ -453,7 +455,6 @@ export default function NewBookingPage() { value={originYardId} onChange={(v) => { setOriginYardId(v); - setTrainScheduleId(null); }} searchable disabled={isLoading} diff --git a/apps/edr-freight-web/backoffice/src/pages/documents/EditFileUploadSettingDialog.tsx b/apps/edr-freight-web/backoffice/src/pages/documents/EditFileUploadSettingDialog.tsx index 1938443ab..4370adac1 100644 --- a/apps/edr-freight-web/backoffice/src/pages/documents/EditFileUploadSettingDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/documents/EditFileUploadSettingDialog.tsx @@ -15,7 +15,8 @@ import { Label } from "@/components/ui/label"; import { Button } from "@/components/ui/button"; import { Textarea } from "@/components/ui/textarea"; import { FileUploadEntity } from "@edr/types/freight"; -import { useCreateFileUploadSetting, useUpdateFileUploadSetting } from "@/hooks/useFileUploadSettings"; +import { useMutation } from "@tanstack/react-query"; +import { api } from "@/services/api"; // import type { // FileUploadEntity, @@ -61,8 +62,8 @@ export default function EditFileUploadSettingDialog({ const [description, setDescription] = useState(setting?.description ?? ""); const [error, setError] = useState(null); - const createMutation = useCreateFileUploadSetting(); - const updateMutation = useUpdateFileUploadSetting(); + const createMutation = useMutation(api.fileUploadSettings.create.mutationOptions()); + const updateMutation = useMutation(api.fileUploadSettings.update.mutationOptions()); const pending = createMutation.isPending || updateMutation.isPending; const reset = () => { diff --git a/apps/edr-freight-web/backoffice/src/pages/documents/FileUploadSettingsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/documents/FileUploadSettingsPage.tsx index acc9b91c0..24c2772aa 100644 --- a/apps/edr-freight-web/backoffice/src/pages/documents/FileUploadSettingsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/documents/FileUploadSettingsPage.tsx @@ -25,12 +25,11 @@ import { Trash2, X, } from "lucide-react"; -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; import { api } from "@/services/api"; -import { useDeleteFileUploadSetting } from "@/hooks/useFileUploadSettings"; import { getMinFiles, type FileUploadSetting } from "@/types/fileUploadSettings"; import { DataTable, type ColumnDef } from "@edr/ui-common"; @@ -44,7 +43,7 @@ export default function FileUploadSettingsPage() { const { data, isLoading, isError, error, refetch } = useQuery( api.fileUploadSettings.list.queryOptions(), ); - const deleteMutation = useDeleteFileUploadSetting(); + const deleteMutation = useMutation(api.fileUploadSettings.remove.mutationOptions()); const fileUploadSettings = useMemo( () => (Array.isArray(data) ? data : []), @@ -202,7 +201,7 @@ export default function FileUploadSettingsPage() { deleteMutation.mutate(setting.id)} + onConfirm={() => deleteMutation.mutate({ id: setting.id })} > (seed); - const replaceMutation = useReplaceFileUploadFields(); + const replaceMutation = useMutation( + api.fileUploadSettings.replaceFields.mutationOptions(), + ); const update = (i: number, patch: Partial) => setFields((prev) => @@ -145,7 +148,7 @@ export default function ManageFileUploadFieldsDialog({ })); replaceMutation.mutate( - { settingId: setting.id, fields: payload }, + { id: setting.id, fields: payload }, { onSuccess: () => setOpen(false), onError: (err) => diff --git a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/DropdownSettingsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/DropdownSettingsPage.tsx index d2689c750..751b94886 100644 --- a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/DropdownSettingsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/DropdownSettingsPage.tsx @@ -31,9 +31,8 @@ import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import EditDropdownSettingDialog from "./EditDropdownSettingDialog"; import ManageDropdownOptionsDialog from "./ManageDropdownOptionsDialog"; import DeleteDropdownSettingDialog from "./DeleteDropdownSettingDialog"; -import { useQuery } from "@tanstack/react-query"; +import { useMutation, useQuery } from "@tanstack/react-query"; import { api } from "@/services/api"; -import { useDeleteDropdownSetting } from "@/hooks/useDropdownSettings"; import type { DropdownSetting } from "@/types/dropdownSettings"; import { DataTable, @@ -63,7 +62,7 @@ export default function DropdownSettingsPage() { const { data, isLoading, isError, error } = useQuery( api.dropdownSettings.list.queryOptions(), ); - const deleteMutation = useDeleteDropdownSetting(); + const deleteMutation = useMutation(api.dropdownSettings.remove.mutationOptions()); const dropdownSettings = useMemo( () => (Array.isArray(data) ? data : []), @@ -353,7 +352,7 @@ export default function DropdownSettingsPage() { key={`delete-${activeSetting.id}`} settingLabel={activeSetting.label} settingCode={activeSetting.code} - onConfirm={() => deleteMutation.mutate(activeSetting.id)} + onConfirm={() => deleteMutation.mutate({ id: activeSetting.id })} open={activeDialog === "delete"} onOpenChange={(next) => (next ? null : closeDialog())} /> diff --git a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/EditDropdownSettingDialog.tsx b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/EditDropdownSettingDialog.tsx index 14922446c..053a74867 100644 --- a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/EditDropdownSettingDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/EditDropdownSettingDialog.tsx @@ -20,10 +20,9 @@ import type { DropdownSetting, UpdateDropdownSettingDto, } from "@/types/dropdownSettings"; -import { - useCreateDropdownSetting, - useUpdateDropdownSetting, -} from "@/hooks/useDropdownSettings"; +import { useMutation } from "@tanstack/react-query"; + +import { api } from "@/services/api"; export interface EditDropdownSettingDialogProps { mode?: "create" | "edit"; @@ -76,8 +75,8 @@ export default function EditDropdownSettingDialog({ ); const [error, setError] = useState(null); - const createMutation = useCreateDropdownSetting(); - const updateMutation = useUpdateDropdownSetting(); + const createMutation = useMutation(api.dropdownSettings.create.mutationOptions()); + const updateMutation = useMutation(api.dropdownSettings.update.mutationOptions()); const pending = createMutation.isPending || updateMutation.isPending; const reset = () => { diff --git a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/ManageDropdownOptionsDialog.tsx b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/ManageDropdownOptionsDialog.tsx index 0528145ff..9f9a27057 100644 --- a/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/ManageDropdownOptionsDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/dropdown_settings/ManageDropdownOptionsDialog.tsx @@ -18,7 +18,8 @@ import type { CreateDropdownOptionDto, DropdownSetting, } from "@/types/dropdownSettings"; -import { useReplaceDropdownOptions } from "@/hooks/useDropdownSettings"; +import { useMutation } from "@tanstack/react-query"; +import { api } from "@/services/api"; export interface ManageDropdownOptionsDialogProps { setting: DropdownSetting; @@ -84,7 +85,9 @@ export default function ManageDropdownOptionsDialog({ const [options, setOptions] = useState(seed); - const replaceMutation = useReplaceDropdownOptions(); + const replaceMutation = useMutation( + api.dropdownSettings.replaceOptions.mutationOptions(), + ); const update = (i: number, patch: Partial) => setOptions((prev) => @@ -147,7 +150,7 @@ export default function ManageDropdownOptionsDialog({ }); replaceMutation.mutate( - { settingId: setting.id, options: payload }, + { id: setting.id, options: payload }, { onSuccess: () => setOpen(false), onError: (err) => diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetCrudPages.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetCrudPages.tsx index 538b8ff6d..cd846ada7 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetCrudPages.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetCrudPages.tsx @@ -36,30 +36,7 @@ import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@edr/ui-common'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table'; -import { useCargoTypes } from '@/hooks/use-cargo-types'; -import { useContainerTypes } from '@/hooks/use-container-types'; -import { - useCreateWagonType, - useDeleteWagonType, - useUpdateWagonType, - useWagonTypes, -} from '@/hooks/use-wagon-types'; import { useToast } from '@/hooks/use-toast'; -import { - useContainers, - useCreateContainer, - useDeleteContainer, - useUpdateContainer, -} from '@/hooks/useContainers'; -import { useCreateTrain, useDeleteTrain, useTrains, useUpdateTrain } from '@/hooks/useTrains'; -import { useRouteYards } from '@/hooks/useRoutes'; -import { useCreateWagon, useDeleteWagon, useUpdateWagon, useWagons } from '@/hooks/useWagons'; -import { - useCreateLocomotive, - useDecommissionLocomotive, - useLocomotives, - useUpdateLocomotive, -} from '@/hooks/useLocomotives'; import type { Cargo } from '@/services/cargoService'; import { DeliverCargoDialog } from '@/components/cargoes/DeliverCargoDialog'; import type { Container } from '@/services/containerService'; @@ -513,7 +490,7 @@ const optionLabel = (options: { value: string; label: string }[], value?: string options.find((option) => option.value === value)?.label ?? value ?? '-'; export function TrainMasterDataPage() { - const query = useTrains(); + const query = useQuery(api.trains.list.queryOptions()); return ( title="Trains" @@ -521,9 +498,9 @@ export function TrainMasterDataPage() { addLabel="Add Train" data={query.data} isLoading={query.isLoading} - create={useCreateTrain()} - update={useUpdateTrain()} - remove={useDeleteTrain()} + create={useMutation(api.trains.create.mutationOptions())} + update={useMutation(api.trains.update.mutationOptions())} + remove={useMutation(api.trains.remove.mutationOptions())} searchText={(train) => [train.code, train.trainNumber, train.trainName, train.status].join(' ')} columns={[ { key: 'code', label: 'Code' }, @@ -548,10 +525,10 @@ export function TrainMasterDataPage() { } export function WagonTypesCrudPage() { - const query = useWagonTypes(); - const create = useCreateWagonType(); - const update = useUpdateWagonType(); - const remove = useDeleteWagonType(); + const query = useQuery(api.wagonTypes.list.queryOptions()); + const create = useMutation(api.wagonTypes.create.mutationOptions()); + const update = useMutation(api.wagonTypes.update.mutationOptions()); + const remove = useMutation(api.wagonTypes.remove.mutationOptions()); const { toast } = useToast(); const [search, setSearch] = useState(''); const [page, setPage] = useState(1); @@ -898,9 +875,9 @@ export function WagonTypesCrudPage() { } export function WagonsCrudPage() { - const query = useWagons(); - const { data: wagonTypes = [] } = useWagonTypes(); - const { data: yards = [] } = useRouteYards(); + const query = useQuery(api.wagons.list.queryOptions({ input: {} })); + const { data: wagonTypes = [] } = useQuery(api.wagonTypes.list.queryOptions()); + const { data: yards = [] } = useQuery(api.routes.yards.queryOptions()); const wagonTypeOptions = wagonTypes.map((type: any) => ({ value: type.id, label: `${type.code} - ${type.name}`, @@ -916,9 +893,9 @@ export function WagonsCrudPage() { addLabel="Add Wagon" data={query.data} isLoading={query.isLoading} - create={useCreateWagon()} - update={useUpdateWagon()} - remove={useDeleteWagon()} + create={useMutation(api.wagons.create.mutationOptions())} + update={useMutation(api.wagons.update.mutationOptions())} + remove={useMutation(api.wagons.remove.mutationOptions())} searchText={(wagon) => [ wagon.wagonNumber, wagon.wagonTypeId, @@ -986,9 +963,11 @@ export function WagonsCrudPage() { } export function ContainersCrudPage() { - const query = useContainers(); - const { data: containerTypes = [] } = useContainerTypes(); - const { data: wagons = [] } = useWagons(); + const query = useQuery(api.containers.list.queryOptions()); + const { data: containerTypes = [] } = useQuery( + api.containerTypes.list.queryOptions({ staleTime: Infinity }), + ); + const { data: wagons = [] } = useQuery(api.wagons.list.queryOptions({ input: {} })); const containerTypeOptions = containerTypes.map((type: any) => ({ value: type.id, label: type.label ?? type.name ?? type.code, @@ -1004,9 +983,9 @@ export function ContainersCrudPage() { addLabel="Add Container" data={query.data} isLoading={query.isLoading} - create={useCreateContainer()} - update={useUpdateContainer()} - remove={useDeleteContainer()} + create={useMutation(api.containers.create.mutationOptions())} + update={useMutation(api.containers.update.mutationOptions())} + remove={useMutation(api.containers.remove.mutationOptions())} searchText={(container) => [container.containerNumber, container.containerTypeId, container.wagonId, container.status].join(' ')} columns={[ { key: 'containerNumber', label: 'Number' }, @@ -1044,8 +1023,10 @@ export function ContainersCrudPage() { export function CargoesCrudPage() { const query = useQuery(api.cargoes.list.queryOptions()); - const { data: cargoTypes = [] } = useCargoTypes(); - const { data: containers = [] } = useContainers(); + const { data: cargoTypes = [] } = useQuery( + api.cargoTypes.list.queryOptions({ staleTime: Infinity }), + ); + const { data: containers = [] } = useQuery(api.containers.list.queryOptions()); const cargoTypeOptions = cargoTypes.map((type: any) => ({ value: type.id, label: type.cargoTypeName ?? type.cargo_type_name ?? type.name ?? type.code, @@ -1112,7 +1093,7 @@ export function CargoesCrudPage() { } export function LocomotivesCrudPage() { - const query = useLocomotives(); + const query = useQuery(api.locomotives.list.queryOptions()); return ( @@ -1122,9 +1103,9 @@ export function LocomotivesCrudPage() { addLabel="Add Locomotive" data={query.data} isLoading={query.isLoading} - create={useCreateLocomotive()} - update={useUpdateLocomotive()} - remove={useDecommissionLocomotive()} + create={useMutation(api.locomotives.create.mutationOptions())} + update={useMutation(api.locomotives.update.mutationOptions())} + remove={useMutation(api.locomotives.decommission.mutationOptions())} removeActionLabel="Decommission" removeConfirmMessage="Decommission this locomotive?" removeSuccessMessage="Locomotive decommissioned" diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx index 6292a1764..194df8561 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/FleetResourcePage.tsx @@ -1,5 +1,8 @@ import type { ColumnDef } from "@edr/ui-common"; import { Box, Button, Card, Group, Modal, Select, Stack, Text } from "@mantine/core"; +import { useMutation, useQuery } from "@tanstack/react-query"; + +import { api } from "@/services/api"; import { Archive, Circle, @@ -22,14 +25,7 @@ import { formatFleetCell, registerFleetOptionLabels } from "@/components/fleet/f import { useFleetViewMode } from "@/components/fleet/useFleetViewMode"; import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; -import { useFleetList, useFleetMutations } from "@/hooks/fleet/useFleet"; -import { useCargoTypes } from "@/hooks/use-cargo-types"; -import { useContainerTypes } from "@/hooks/use-container-types"; import { useToast } from "@/hooks/use-toast"; -import { useWagonTypes } from "@/hooks/use-wagon-types"; -import { useContainers } from "@/hooks/useContainers"; -import { useRouteYards } from "@/hooks/useRoutes"; -import { useWagons } from "@/hooks/useWagons"; import { FLEET_SELECT_NONE, getFleetResource, @@ -90,15 +86,31 @@ const FleetResourcePage = () => { return filters; }, [slug, listFilterValues, search]); - const { data: allRows = [], isLoading, isError, error } = useFleetList(slug, serverListFilters); - const { create, update, remove } = useFleetMutations(slug); + const { data: allRows = [], isLoading, isError, error } = useQuery( + api.fleet.list.queryOptions({ input: { slug, filters: serverListFilters } }), + ); + const create = useMutation(api.fleet.create.mutationOptions()); + const update = useMutation(api.fleet.update.mutationOptions()); + const remove = useMutation(api.fleet.remove.mutationOptions()); - const { data: wagonTypes = [], isLoading: wagonTypesLoading } = useWagonTypes(); - const { data: containerTypes = [], isLoading: containerTypesLoading } = useContainerTypes(); - const { data: cargoTypes = [], isLoading: cargoTypesLoading } = useCargoTypes(); - const { data: wagons = [], isLoading: wagonsLoading } = useWagons(); - const { data: containers = [], isLoading: containersLoading } = useContainers(); - const { data: yards = [], isLoading: yardsLoading } = useRouteYards(); + const { data: wagonTypes = [], isLoading: wagonTypesLoading } = useQuery( + api.wagonTypes.list.queryOptions(), + ); + const { data: containerTypes = [], isLoading: containerTypesLoading } = useQuery( + api.containerTypes.list.queryOptions({ staleTime: Infinity }), + ); + const { data: cargoTypes = [], isLoading: cargoTypesLoading } = useQuery( + api.cargoTypes.list.queryOptions({ staleTime: Infinity }), + ); + const { data: wagons = [], isLoading: wagonsLoading } = useQuery( + api.wagons.list.queryOptions({ input: {} }), + ); + const { data: containers = [], isLoading: containersLoading } = useQuery( + api.containers.list.queryOptions(), + ); + const { data: yards = [], isLoading: yardsLoading } = useQuery( + api.routes.yards.queryOptions(), + ); useEffect(() => { setPagination((prev) => ({ pageIndex: 0, pageSize: prev.pageSize })); @@ -316,10 +328,10 @@ const FleetResourcePage = () => { const handleFormSubmit = async (values: Record) => { try { if (editing && "id" in editing) { - await update.mutateAsync({ id: String(editing.id), data: values }); + await update.mutateAsync({ slug, id: String(editing.id), data: values }); toast({ title: `${config.entityLabel} updated` }); } else { - await create.mutateAsync(values); + await create.mutateAsync({ slug, data: values }); toast({ title: `${config.entityLabel} created` }); } setFormOpen(false); @@ -335,7 +347,7 @@ const FleetResourcePage = () => { const handleRemove = async () => { if (!removeTarget || !("id" in removeTarget)) return; try { - await remove.mutateAsync(String(removeTarget.id)); + await remove.mutateAsync({ slug, id: String(removeTarget.id) }); toast({ title: config.removeSuccessMessage ?? `${config.entityLabel} removed`, }); diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx b/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx index 543ae26db..7d530697f 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/RoutesPage.tsx @@ -17,18 +17,14 @@ import { Tooltip, } from "@mantine/core"; +import { useMutation, useQuery } from "@tanstack/react-query"; + import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; import FleetToolbar from "@/components/fleet/FleetToolbar"; import { useFleetViewMode } from "@/components/fleet/useFleetViewMode"; import RuleEngineListFooter from "@/components/ruleEngine/RuleEngineListFooter"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; -import { - useCreateRoute, - useDeactivateRoute, - useRouteYards, - useRoutes, - useUpdateRoute, -} from "@/hooks/useRoutes"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { RouteRecord, YardRef } from "@/services/routes.service"; import { DataTable, DataTableFooter, usePagination } from "@edr/ui-common"; @@ -71,11 +67,11 @@ export default function RoutesPage() { const { pagination, setPagination } = usePagination({ pageSize: 10 }); const { toast } = useToast(); - const routesQuery = useRoutes(); - const yardsQuery = useRouteYards(); - const createMutation = useCreateRoute(); - const updateMutation = useUpdateRoute(); - const deactivateMutation = useDeactivateRoute(); + const routesQuery = useQuery(api.routes.list.queryOptions()); + const yardsQuery = useQuery(api.routes.yards.queryOptions()); + const createMutation = useMutation(api.routes.create.mutationOptions()); + const updateMutation = useMutation(api.routes.update.mutationOptions()); + const deactivateMutation = useMutation(api.routes.deactivate.mutationOptions()); const filteredRoutes = useMemo(() => { const query = search.trim().toLowerCase(); diff --git a/apps/edr-freight-web/backoffice/src/pages/payments/PaymentsPage.tsx b/apps/edr-freight-web/backoffice/src/pages/payments/PaymentsPage.tsx index e5a490927..f68ac32f4 100644 --- a/apps/edr-freight-web/backoffice/src/pages/payments/PaymentsPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/payments/PaymentsPage.tsx @@ -22,8 +22,10 @@ import { } from "lucide-react"; import { useMemo, useState } from "react"; +import { useQuery } from "@tanstack/react-query"; + import { KpiStrip, PageContainer, PageHeader } from "@/components/page"; -import { usePaymentList, usePaymentSummary } from "@/hooks/usePayments"; +import { api } from "@/services/api"; import type { PaymentMethod, PaymentRow } from "@/services/payments.service"; import { Badge, @@ -106,8 +108,12 @@ export default function PaymentsPage() { [query, statuses, method, pagination.pageIndex, pagination.pageSize], ); - const { data, isLoading, isError } = usePaymentList(filter); - const { data: summary, isLoading: summaryLoading } = usePaymentSummary(); + const { data, isLoading, isError } = useQuery( + api.payments.list.queryOptions({ input: { filter } }), + ); + const { data: summary, isLoading: summaryLoading } = useQuery( + api.payments.summary.queryOptions({ staleTime: 30_000 }), + ); const rows = data?.items ?? []; const total = data?.total ?? 0; diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx index bc6e5bb15..9085f2868 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchBoardPage.tsx @@ -46,7 +46,8 @@ import { import { RouteCorridor } from "@/components/trainScheduling/scheduleVisuals"; import { ruleEngineTable } from "@/components/ruleEngine/ruleEngineStyles"; import { FREIGHT_BRAND, FREIGHT_BRAND_DARK } from "@/theme/freight-brand"; -import { useBatchBoard } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import type { BatchBoardSchedule } from "@/types/trainScheduling"; const fmtTons = (n: number) => @@ -368,7 +369,9 @@ function CardSkeleton() { export default function BatchBoardPage() { const navigate = useNavigate(); - const { data, isLoading, isError, isFetching, refetch } = useBatchBoard(); + const { data, isLoading, isError, isFetching, refetch } = useQuery( + api.trainScheduling.batchBoard.queryOptions({ refetchInterval: 30_000 }), + ); const { viewMode, setViewMode } = useFleetViewMode("batch-board"); const { pagination, setPagination } = usePagination({ pageSize: 10 }); const [search, setSearch] = useState(""); diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx index f78639359..1417d738c 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/BatchScheduleDetailPage.tsx @@ -52,11 +52,8 @@ import { WindowStatusPill, } from "@/components/trainScheduling/batchVisuals"; import { RouteCorridor } from "@/components/trainScheduling/scheduleVisuals"; -import { - useBatchBoardDetail, - useRunAllocation, - useScheduleDetail, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { BatchBoardBookingDetail, @@ -429,8 +426,16 @@ export default function BatchScheduleDetailPage() { const { scheduleId } = useParams<{ scheduleId: string }>(); const navigate = useNavigate(); const { toast } = useToast(); - const { data, isLoading, isFetching, refetch } = useBatchBoardDetail(scheduleId); - const runAllocation = useRunAllocation(scheduleId ?? ""); + const { data, isLoading, isFetching, refetch } = useQuery( + api.trainScheduling.batchBoardDetail.queryOptions({ + input: { scheduleId: scheduleId ?? "" }, + enabled: Boolean(scheduleId), + refetchInterval: 30_000, + }), + ); + const runAllocation = useMutation( + api.trainScheduling.runAllocation.mutationOptions(), + ); const hasAssignedWagons = useMemo( () => @@ -443,7 +448,12 @@ export default function BatchScheduleDetailPage() { [data], ); - const scheduleDetailQuery = useScheduleDetail(scheduleId, "CONTAINER"); + const scheduleDetailQuery = useQuery( + api.trainScheduling.scheduleDetail.queryOptions({ + input: { id: scheduleId ?? "", freightType: "CONTAINER" }, + enabled: Boolean(scheduleId), + }), + ); // Batch bookings by state for the composition side panel (payment / expired lists). const batchBookings = useMemo(() => { @@ -550,7 +560,7 @@ export default function BatchScheduleDetailPage() { const handleRunAllocation = () => { runAllocation - .mutateAsync() + .mutateAsync({ scheduleId: scheduleId ?? "" }) .then((result) => { const failed = result.issues.filter((i) => i.status === "FAILED").length; const deferred = result.deferred.length; diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleTrackPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleTrackPage.tsx index caed00435..7eb995ac2 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleTrackPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleTrackPage.tsx @@ -28,7 +28,8 @@ import { PageContainer } from "@/components/page"; import { RouteCorridorTrack } from "@/components/trainScheduling/RouteCorridorTrack"; import { RouteCorridor, StatusPill } from "@/components/trainScheduling/scheduleVisuals"; import { freightBrand } from "@/theme/freight-brand"; -import { useTrainTrack, useScheduleMutations } from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; const parseError = (error: unknown, fallback: string) => { @@ -81,8 +82,15 @@ function MetaStat({ export default function TrainScheduleTrackPage() { const { scheduleId } = useParams<{ scheduleId: string }>(); const { toast } = useToast(); - const trackQuery = useTrainTrack(scheduleId); - const { recordCheckpoint } = useScheduleMutations(scheduleId); + const trackQuery = useQuery( + api.trainScheduling.trainTrack.queryOptions({ + input: { id: scheduleId ?? "" }, + enabled: Boolean(scheduleId), + }), + ); + const recordCheckpoint = useMutation( + api.trainScheduling.recordCheckpoint.mutationOptions(), + ); if (trackQuery.isLoading) { return ( diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx index e2618510a..8c16b8d14 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2DetailPage.tsx @@ -56,11 +56,8 @@ import { shouldShowContainerPlacementStep } from "@/components/trainScheduling/s import { TrainCompositionDiagram } from "@/components/trainScheduling/TrainCompositionDiagram"; import { WagonPlanGrid } from "@/components/trainScheduling/WagonPlanGrid"; import { WorkflowRail, WorkflowStep } from "@/components/trainScheduling/WorkflowStep"; -import { - useEligibleBookings, - useScheduleDetail, - useScheduleMutations, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; import type { ContainerPlacement, @@ -91,7 +88,12 @@ export default function TrainScheduleV2DetailPage() { const [maintenanceOpen, setMaintenanceOpen] = useState(false); const autoPreviewedRef = useRef(false); - const detailQuery = useScheduleDetail(scheduleId); + const detailQuery = useQuery( + api.trainScheduling.scheduleDetail.queryOptions({ + input: { id: scheduleId ?? "" }, + enabled: Boolean(scheduleId), + }), + ); const schedule = detailQuery.data; const freightType: FreightType | undefined = schedule?.freightType as FreightType | undefined; @@ -111,12 +113,17 @@ export default function TrainScheduleV2DetailPage() { const eligibleFreightType = freightType === "CONTAINER" || freightType === "BULK" ? freightType : undefined; - const eligibleQuery = useEligibleBookings( - eligibleFilters, - Boolean(schedule), - eligibleFreightType, + const eligibleQuery = useQuery( + api.trainScheduling.eligibleBookings.queryOptions({ + input: { filters: eligibleFilters, freightType: eligibleFreightType }, + enabled: Boolean(schedule), + }), ); - const { preview, assign, unassign, finalize, dispatch } = useScheduleMutations(scheduleId); + const preview = useMutation(api.trainScheduling.preview.mutationOptions()); + const assign = useMutation(api.trainScheduling.assignBookings.mutationOptions()); + const unassign = useMutation(api.trainScheduling.unassignBooking.mutationOptions()); + const finalize = useMutation(api.trainScheduling.finalizeSchedule.mutationOptions()); + const dispatch = useMutation(api.trainScheduling.dispatchSchedule.mutationOptions()); const assignedIds = useMemo( () => (schedule?.bookings ?? []).map((b) => b.id), diff --git a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx index d389009aa..193caa0a5 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trainScheduling/TrainScheduleV2ListPage.tsx @@ -39,13 +39,9 @@ import { RouteCorridor, StatusPill, } from "@/components/trainScheduling/scheduleVisuals"; -import { - useAvailableLocomotives, - useScheduleList, - useScheduleMutations, -} from "@/hooks/trainScheduling/useTrainScheduling"; +import { useMutation, useQuery } from "@tanstack/react-query"; +import { api } from "@/services/api"; import { useToast } from "@/hooks/use-toast"; -import { useRoutes } from "@/hooks/useRoutes"; import type { TrainScheduleListItem } from "@/types/trainScheduling"; import { DataTable, DataTableFooter, usePagination } from "@edr/ui-common"; @@ -88,10 +84,17 @@ export default function TrainScheduleV2ListPage() { const [scheduleDate, setScheduleDate] = useState(""); const [locomotiveId, setLocomotiveId] = useState(""); - const schedulesQuery = useScheduleList(); - const routesQuery = useRoutes(); - const locomotivesQuery = useAvailableLocomotives(routeId || undefined); - const { create, cancel } = useScheduleMutations(); + const schedulesQuery = useQuery( + api.trainScheduling.scheduleList.queryOptions({ input: {} }), + ); + const routesQuery = useQuery(api.routes.list.queryOptions()); + const locomotivesQuery = useQuery( + api.trainScheduling.availableLocomotives.queryOptions({ + input: { routeId: routeId || undefined }, + }), + ); + const create = useMutation(api.trainScheduling.createSchedule.mutationOptions()); + const cancel = useMutation(api.trainScheduling.cancelSchedule.mutationOptions()); const activeRoutes = useMemo( () => (routesQuery.data ?? []).filter((r) => r.isActive), diff --git a/apps/edr-freight-web/backoffice/src/pages/trains/TrainDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/trains/TrainDetailPage.tsx index e6c2534db..9e336fa19 100644 --- a/apps/edr-freight-web/backoffice/src/pages/trains/TrainDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/trains/TrainDetailPage.tsx @@ -2,13 +2,17 @@ import { useParams, Link } from "react-router-dom"; import { ArrowLeft } from "lucide-react"; import { Badge, Button, Card, Group, Loader, SimpleGrid, Stack, Text } from "@mantine/core"; +import { useQuery } from "@tanstack/react-query"; + import { AssignWagonDialog } from "@/components/wagons/AssignWagonDialog"; import { WagonsTable } from "@/components/wagons/WagonsTable"; -import { useTrain } from "@/hooks/useTrains"; +import { api } from "@/services/api"; export default function TrainDetailPage() { const { id } = useParams<{ id: string }>(); - const { data: train, isLoading } = useTrain(id!); + const { data: train, isLoading } = useQuery( + api.trains.getById.queryOptions({ input: { id: id ?? "" }, enabled: !!id }), + ); if (isLoading) { return ( diff --git a/apps/edr-freight-web/backoffice/src/services/api.ts b/apps/edr-freight-web/backoffice/src/services/api.ts index 410f68b0c..e801b856c 100644 --- a/apps/edr-freight-web/backoffice/src/services/api.ts +++ b/apps/edr-freight-web/backoffice/src/services/api.ts @@ -42,7 +42,68 @@ import { type Cargo, type DeliverCargoPayload, } from "./cargoService"; +import { containerService, type Container } from "./containerService"; +import { containerTypesService } from "./container-types.service"; +import { + wagonService, + type Wagon, + type WagonListFilters, +} from "./wagon.service"; +import { wagonTypesService, type WagonType } from "./wagon-types.service"; +import { trainService, type Train } from "./trains.service"; +import { + locomotivesService, + type Locomotive, + type SaveLocomotivePayload, +} from "./locomotives.service"; +import { cargoTypesService } from "./cargo-types.service"; +import { + fleetService, + type FleetListFilters, + type FleetRecord, +} from "./fleet/fleet.service"; +import type { FleetResourceSlug } from "@/pages/fleet/config/resources"; +import { + paymentsService, + type PaginatedPayments, + type PaymentListFilter, + type PaymentSummary, +} from "./payments.service"; +import { + signaturesService, + type SavedSignature, + type SaveSignaturePayload, +} from "./signatures.service"; import { warehouseService } from "./warehouse.service"; +import { trainSchedulingService } from "./trainScheduling.service"; +import { + routesService, + type RouteRecord, + type SaveRoutePayload, + type YardRef, +} from "./routes.service"; +import type { + AssignBookingsPayload, + BatchBoardSchedule, + BatchBoardScheduleDetail, + BookableSchedule, + CompositionRemovalEntry, + CreateTrainSchedulePayload, + EligibleContainerBookingsResponse, + FreightType, + LocomotiveRecord, + PinWagonsPayload, + RecordCheckpointPayload, + TrainScheduleDetail, + TrainScheduleFilters, + TrainScheduleListItem, + TrainSchedulePreviewPayload, + TrainSchedulePreviewResponse, + TrainTrackResponse, + UnassignedBookingsResponse, + WagonAllocationAttemptResult, + YardOption, +} from "@/types/trainScheduling"; import type { AllocationCriteria, AllocationPreviewResult, @@ -104,7 +165,333 @@ const INVENTORY_INVALIDATIONS: ReadonlyArray = [ ["warehouses"], ]; +/** + * Train-scheduling mutations broadly affect the schedule board and bookings. + * The grouped hooks invalidated TRAIN_SCHEDULING.ROOT + BOOKINGS.ROOT; since + * every train-scheduling key is prefixed with `"train-scheduling"`, the two + * roots below cover all of them via React Query's prefix matching. + */ +const TRAIN_SCHEDULING_INVALIDATIONS: ReadonlyArray = [ + QUERY_KEYS.TRAIN_SCHEDULING.ROOT, + QUERY_KEYS.BOOKINGS.ROOT, +]; + export const api = { + trainScheduling: { + // ── Queries ──────────────────────────────────────────────────────────── + scheduleList: endpoint<{ freightType?: FreightType }, TrainScheduleListItem[]>( + "train-scheduling", + "schedules", + ({ freightType }) => trainSchedulingService.listSchedules(freightType), + () => QUERY_KEYS.TRAIN_SCHEDULING.schedules(), + ), + + batchBoard: endpoint( + "train-scheduling", + "batch-board", + () => trainSchedulingService.getBatchBoard(), + () => QUERY_KEYS.TRAIN_SCHEDULING.batchBoard(), + ), + + batchBoardDetail: endpoint<{ scheduleId: string }, BatchBoardScheduleDetail>( + "train-scheduling", + "batch-board-detail", + ({ scheduleId }) => trainSchedulingService.getBatchBoardDetail(scheduleId), + ({ scheduleId }) => QUERY_KEYS.TRAIN_SCHEDULING.batchBoardDetail(scheduleId), + ), + + scheduleDetail: endpoint< + { id: string; freightType?: FreightType }, + TrainScheduleDetail + >( + "train-scheduling", + "schedule-detail", + ({ id, freightType }) => + trainSchedulingService.getScheduleById(id, freightType), + ({ id }) => QUERY_KEYS.TRAIN_SCHEDULING.scheduleById(id), + ), + + eligibleBookings: endpoint< + { filters?: TrainScheduleFilters; freightType?: FreightType }, + EligibleContainerBookingsResponse + >( + "train-scheduling", + "eligible-bookings", + ({ filters, freightType }) => + trainSchedulingService.getEligibleBookings(filters, freightType), + ({ filters, freightType }) => + QUERY_KEYS.TRAIN_SCHEDULING.eligible(freightType, filters), + ), + + availableLocomotives: endpoint<{ routeId?: string }, LocomotiveRecord[]>( + "train-scheduling", + "locomotives", + ({ routeId }) => trainSchedulingService.getAvailableLocomotives(routeId), + ({ routeId }) => QUERY_KEYS.TRAIN_SCHEDULING.locomotives(routeId), + ), + + bookableSchedules: endpoint< + { originYardId?: string | null; destinationYardId?: string | null }, + BookableSchedule[] + >( + "train-scheduling", + "bookable", + ({ originYardId, destinationYardId }) => + trainSchedulingService.getBookableSchedules( + originYardId ?? undefined, + destinationYardId ?? undefined, + ), + ({ originYardId, destinationYardId }) => [ + ...QUERY_KEYS.TRAIN_SCHEDULING.ROOT, + "bookable", + originYardId ?? "", + destinationYardId ?? "", + ], + ), + + availableDays: endpoint< + { originYardId?: string | null; destinationYardId?: string | null }, + string[] + >( + "train-scheduling", + "available-days", + ({ originYardId, destinationYardId }) => + trainSchedulingService.getAvailableDays( + originYardId ?? undefined, + destinationYardId ?? undefined, + ), + ({ originYardId, destinationYardId }) => [ + ...QUERY_KEYS.TRAIN_SCHEDULING.ROOT, + "available-days", + originYardId ?? "", + destinationYardId ?? "", + ], + ), + + trainTrack: endpoint<{ id: string }, TrainTrackResponse>( + "train-scheduling", + "track", + ({ id }) => trainSchedulingService.getTrack(id), + ({ id }) => QUERY_KEYS.TRAIN_SCHEDULING.track(id), + ), + + unassignedBookings: endpoint< + { scheduleId: string }, + UnassignedBookingsResponse + >( + "train-scheduling", + "unassigned-bookings", + ({ scheduleId }) => + trainSchedulingService.getUnassignedBookings(scheduleId), + ({ scheduleId }) => + QUERY_KEYS.TRAIN_SCHEDULING.unassignedBookings(scheduleId), + ), + + compositionRemovals: endpoint< + { scheduleId: string }, + CompositionRemovalEntry[] + >( + "train-scheduling", + "composition-removals", + ({ scheduleId }) => + trainSchedulingService.getCompositionRemovals(scheduleId), + ({ scheduleId }) => + QUERY_KEYS.TRAIN_SCHEDULING.compositionRemovals(scheduleId), + ), + + // ── Mutations ────────────────────────────────────────────────────────── + runAllocation: endpoint<{ scheduleId: string }, WagonAllocationAttemptResult>( + "train-scheduling", + "run-allocation", + ({ scheduleId }) => trainSchedulingService.runAllocation(scheduleId), + undefined, + () => [QUERY_KEYS.TRAIN_SCHEDULING.ROOT], + ), + + runBatch: endpoint( + "train-scheduling", + "run-batch", + (id) => trainSchedulingService.runBatch(id), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + setBookingWindow: endpoint< + { id: string; status: "OPEN" | "CLOSED" }, + TrainScheduleDetail + >( + "train-scheduling", + "set-booking-window", + ({ id, status }) => trainSchedulingService.setBookingWindow(id, status), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + markBookingPaid: endpoint( + "train-scheduling", + "mark-booking-paid", + (bookingId) => trainSchedulingService.markBookingPaid(bookingId), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + expireBooking: endpoint( + "train-scheduling", + "expire-booking", + (bookingId) => trainSchedulingService.expireBooking(bookingId), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + moveBookingSchedule: endpoint< + { bookingId: string; trainScheduleId: string }, + void + >( + "train-scheduling", + "move-booking-schedule", + ({ bookingId, trainScheduleId }) => + trainSchedulingService.moveBookingSchedule(bookingId, trainScheduleId), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + createSchedule: endpoint< + { freightType?: FreightType; payload: CreateTrainSchedulePayload }, + TrainScheduleDetail + >( + "train-scheduling", + "create-schedule", + ({ freightType, payload }) => + trainSchedulingService.createSchedule(payload, freightType), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + preview: endpoint< + { freightType?: FreightType; payload: TrainSchedulePreviewPayload }, + TrainSchedulePreviewResponse + >("train-scheduling", "preview", ({ freightType, payload }) => + trainSchedulingService.preview(payload, freightType), + ), + + assignBookings: endpoint< + { id: string; freightType?: FreightType; payload: AssignBookingsPayload }, + TrainScheduleDetail + >( + "train-scheduling", + "assign-bookings", + ({ id, freightType, payload }) => + trainSchedulingService.assignBookings(id, payload, freightType), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + assignUnassignedBooking: endpoint< + { id: string; bookingId: string }, + TrainScheduleDetail + >( + "train-scheduling", + "assign-unassigned-booking", + ({ id, bookingId }) => + trainSchedulingService.assignUnassignedBooking(id, bookingId), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + unassignBooking: endpoint< + { id: string; bookingId: string }, + TrainScheduleDetail + >( + "train-scheduling", + "unassign-booking", + ({ id, bookingId }) => + trainSchedulingService.unassignBooking(id, bookingId), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + pinWagons: endpoint<{ id: string; payload: PinWagonsPayload }, TrainScheduleDetail>( + "train-scheduling", + "pin-wagons", + ({ id, payload }) => trainSchedulingService.pinWagons(id, payload), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + finalizeSchedule: endpoint( + "train-scheduling", + "finalize-schedule", + (id) => trainSchedulingService.finalizeSchedule(id), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + dispatchSchedule: endpoint( + "train-scheduling", + "dispatch-schedule", + (id) => trainSchedulingService.dispatchSchedule(id), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + cancelSchedule: endpoint< + { id: string; freightType?: FreightType }, + TrainScheduleDetail + >( + "train-scheduling", + "cancel-schedule", + ({ id, freightType }) => + trainSchedulingService.cancelSchedule(id, freightType ?? "CONTAINER"), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + recordCheckpoint: endpoint< + { id: string; payload: RecordCheckpointPayload }, + TrainTrackResponse + >( + "train-scheduling", + "record-checkpoint", + ({ id, payload }) => trainSchedulingService.recordCheckpoint(id, payload), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + arriveSchedule: endpoint( + "train-scheduling", + "arrive-schedule", + (id) => trainSchedulingService.arriveSchedule(id), + undefined, + () => TRAIN_SCHEDULING_INVALIDATIONS, + ), + + removeWagonSlot: endpoint< + { scheduleId: string; wagonId: string }, + TrainScheduleDetail + >( + "train-scheduling", + "remove-wagon-slot", + ({ scheduleId, wagonId }) => + trainSchedulingService.removeWagonSlot(scheduleId, wagonId), + undefined, + () => [QUERY_KEYS.TRAIN_SCHEDULING.ROOT], + ), + + updateContainerItem: endpoint< + { scheduleId: string; itemId: string; containerNumber: string | null }, + { id: string; containerNumber: string | null } + >( + "train-scheduling", + "update-container-item", + ({ scheduleId, itemId, containerNumber }) => + trainSchedulingService.updateContainerItem(scheduleId, itemId, { + containerNumber, + }), + undefined, + () => [QUERY_KEYS.TRAIN_SCHEDULING.ROOT], + ), + }, + warehouses: { // ── Warehouses ───────────────────────────────────────────────────────── list: endpoint<{ filter?: WarehouseFilter }, Warehouse[]>( @@ -685,6 +1072,388 @@ export const api = { ), }, + routes: { + list: endpoint("routes", "list", () => + routesService.getAll().then((r) => r.data), + ), + + yards: endpoint( + "routes", + "yards", + () => routesService.getYards().then((r) => r.data.data), + () => ["routes", "yards"], + ), + + create: endpoint( + "routes", + "create", + (payload) => routesService.create(payload).then((r) => r.data), + undefined, + () => [["routes"]], + ), + + update: endpoint<{ id: string; data: Partial }, RouteRecord>( + "routes", + "update", + ({ id, data }) => routesService.update(id, data).then((r) => r.data), + undefined, + () => [["routes"]], + ), + + deactivate: endpoint( + "routes", + "deactivate", + (id) => routesService.deactivate(id).then(() => undefined), + undefined, + () => [["routes"]], + ), + }, + + stations: { + list: endpoint( + "train-scheduling", + "stations", + () => trainSchedulingService.getStations(), + () => QUERY_KEYS.TRAIN_SCHEDULING.stations(), + ), + }, + + containers: { + list: endpoint("containers", "list", () => + containerService.getAll().then((r) => r.data), + ), + + listByWagon: endpoint<{ wagonId: string }, Container[]>( + "containers", + "listByWagon", + ({ wagonId }) => containerService.getByWagon(wagonId).then((r) => r.data), + ({ wagonId }) => ["containers", "wagon", wagonId], + ), + + getById: endpoint<{ id: string }, Container>( + "containers", + "getById", + ({ id }) => containerService.getById(id).then((r) => r.data), + ), + + create: endpoint, Container>( + "containers", + "create", + (payload) => containerService.create(payload).then((r) => r.data), + undefined, + () => [["containers"]], + ), + + update: endpoint<{ id: string; data: Partial }, Container>( + "containers", + "update", + ({ id, data }) => containerService.update(id, data).then((r) => r.data), + undefined, + () => [["containers"]], + ), + + remove: endpoint( + "containers", + "remove", + (id) => containerService.delete(id).then(() => undefined), + undefined, + () => [["containers"]], + ), + + assignToWagon: endpoint< + { containerId: string; wagonId: string; position?: number }, + Container + >( + "containers", + "assignToWagon", + ({ containerId, wagonId, position }) => + containerService + .assignToWagon(containerId, wagonId, position) + .then((r) => r.data), + undefined, + () => [["containers"]], + ), + + unassign: endpoint( + "containers", + "unassign", + (containerId) => + containerService.unassign(containerId).then(() => undefined), + undefined, + () => [["containers"]], + ), + }, + + containerTypes: { + list: endpoint("container-types", "list", () => + containerTypesService.getContainerTypes(), + ), + }, + + wagons: { + list: endpoint<{ filters?: WagonListFilters }, Wagon[]>( + "wagons", + "list", + ({ filters }) => wagonService.getAll(filters ?? {}).then((r) => r.data), + ({ filters }) => ["wagons", "list", filters ?? {}], + ), + + listByTrain: endpoint<{ trainId: string }, Wagon[]>( + "wagons", + "listByTrain", + ({ trainId }) => wagonService.getByTrain(trainId).then((r) => r.data), + ({ trainId }) => ["wagons", "train", trainId], + ), + + getById: endpoint<{ id: string }, Wagon>( + "wagons", + "getById", + ({ id }) => wagonService.getById(id).then((r) => r.data), + ), + + assignToTrain: endpoint< + { wagonId: string; trainId: string; sequenceNumber?: number }, + Wagon + >( + "wagons", + "assignToTrain", + ({ wagonId, trainId, sequenceNumber }) => + wagonService + .assignToTrain(wagonId, trainId, sequenceNumber) + .then((r) => r.data), + undefined, + () => [["wagons"]], + ), + + unassign: endpoint( + "wagons", + "unassign", + (wagonId) => wagonService.unassign(wagonId).then(() => undefined), + undefined, + () => [["wagons"]], + ), + + reorder: endpoint<{ trainId: string; wagonIds: string[] }, Wagon[]>( + "wagons", + "reorder", + ({ trainId, wagonIds }) => + wagonService.reorder(trainId, wagonIds).then((r) => r.data), + undefined, + () => [["wagons"]], + ), + + create: endpoint, Wagon>( + "wagons", + "create", + (payload) => wagonService.create(payload).then((r) => r.data), + undefined, + () => [["wagons"]], + ), + + update: endpoint<{ id: string; data: Partial }, Wagon>( + "wagons", + "update", + ({ id, data }) => wagonService.update(id, data).then((r) => r.data), + undefined, + () => [["wagons"]], + ), + + remove: endpoint( + "wagons", + "remove", + (id) => wagonService.delete(id).then(() => undefined), + undefined, + () => [["wagons"]], + ), + }, + + trains: { + list: endpoint( + "trains", + "list", + () => trainService.getAll().then((r) => r.data), + () => ["trains", "list"], + ), + + getById: endpoint<{ id: string }, Train>( + "trains", + "getById", + ({ id }) => trainService.getById(id).then((r) => r.data), + ({ id }) => ["trains", "detail", id], + ), + + create: endpoint, Train>( + "trains", + "create", + (payload) => trainService.create(payload).then((r) => r.data), + undefined, + () => [["trains"]], + ), + + update: endpoint<{ id: string; data: Partial }, Train>( + "trains", + "update", + ({ id, data }) => trainService.update(id, data).then((r) => r.data), + undefined, + () => [["trains"]], + ), + + remove: endpoint( + "trains", + "remove", + (id) => trainService.delete(id).then(() => undefined), + undefined, + () => [["trains"]], + ), + }, + + locomotives: { + list: endpoint( + "locomotives", + "list", + () => locomotivesService.getAll().then((r) => r.data), + () => ["locomotives"], + ), + + create: endpoint, Locomotive>( + "locomotives", + "create", + (payload) => locomotivesService.create(payload).then((r) => r.data), + undefined, + () => [["locomotives"]], + ), + + update: endpoint< + { id: string; data: Partial }, + Locomotive + >( + "locomotives", + "update", + ({ id, data }) => locomotivesService.update(id, data).then((r) => r.data), + undefined, + () => [["locomotives"]], + ), + + decommission: endpoint( + "locomotives", + "decommission", + (id) => locomotivesService.decommission(id).then(() => undefined), + undefined, + () => [["locomotives"]], + ), + }, + + cargoTypes: { + list: endpoint("cargo-types", "list", () => + cargoTypesService.getCargoTypes(), + ), + }, + + payments: { + list: endpoint<{ filter?: PaymentListFilter }, PaginatedPayments>( + "payments", + "list", + ({ filter }) => paymentsService.list(filter), + ({ filter }) => ["payments", "list", filter ?? {}], + ), + + summary: endpoint( + "payments", + "summary", + () => paymentsService.getSummary(), + () => ["payments", "summary"], + ), + }, + + signatures: { + mySignature: endpoint( + "me", + "signature", + () => signaturesService.getMySignature(), + () => ["me", "signature"], + ), + + save: endpoint( + "me", + "save-signature", + (payload) => signaturesService.saveMySignature(payload), + undefined, + () => [["me", "signature"]], + ), + }, + + fleet: { + list: endpoint< + { slug: FleetResourceSlug; filters?: FleetListFilters }, + FleetRecord[] + >( + "fleet", + "list", + ({ slug, filters }) => fleetService.list(slug, filters), + ({ slug, filters }) => [...QUERY_KEYS.FLEET.list(slug), filters ?? {}], + ), + + create: endpoint< + { slug: FleetResourceSlug; data: Record }, + unknown + >( + "fleet", + "create", + ({ slug, data }) => fleetService.create(slug, data), + undefined, + ({ slug }) => [QUERY_KEYS.FLEET.list(slug)], + ), + + update: endpoint< + { slug: FleetResourceSlug; id: string; data: Record }, + unknown + >( + "fleet", + "update", + ({ slug, id, data }) => fleetService.update(slug, id, data), + undefined, + ({ slug }) => [QUERY_KEYS.FLEET.list(slug)], + ), + + remove: endpoint<{ slug: FleetResourceSlug; id: string }, unknown>( + "fleet", + "remove", + ({ slug, id }) => fleetService.remove(slug, id), + undefined, + ({ slug }) => [QUERY_KEYS.FLEET.list(slug)], + ), + }, + + wagonTypes: { + list: endpoint("wagon-types", "list", () => + wagonTypesService.getWagonTypes(), + ), + + create: endpoint, WagonType>( + "wagon-types", + "create", + (payload) => wagonTypesService.create(payload).then((r) => r.data), + undefined, + () => [["wagon-types"]], + ), + + update: endpoint<{ id: string; data: Partial }, WagonType>( + "wagon-types", + "update", + ({ id, data }) => wagonTypesService.update(id, data).then((r) => r.data), + undefined, + () => [["wagon-types"]], + ), + + remove: endpoint( + "wagon-types", + "remove", + (id) => wagonTypesService.delete(id).then(() => undefined), + undefined, + () => [["wagon-types"]], + ), + }, + cargoes: { list: endpoint("cargoes", "list", () => cargoService.getAll().then((r) => r.data), @@ -778,46 +1547,68 @@ export const api = { "file-upload-settings", "create", (payload) => fileUploadSettingsService.create(payload), + undefined, + () => [["file-upload-settings"]], ), update: endpoint< { id: string; dto: UpdateFileUploadSettingDto }, FileUploadSetting - >("file-upload-settings", "update", ({ id, dto }) => - fileUploadSettingsService.update(id, dto), + >( + "file-upload-settings", + "update", + ({ id, dto }) => fileUploadSettingsService.update(id, dto), + undefined, + () => [["file-upload-settings"]], ), remove: endpoint<{ id: string }, void>( "file-upload-settings", "remove", ({ id }) => fileUploadSettingsService.remove(id), + undefined, + () => [["file-upload-settings"]], ), replaceFields: endpoint< { id: string; fields: CreateFileUploadFieldDto[] }, FileUploadField[] - >("file-upload-settings", "replaceFields", ({ id, fields }) => - fileUploadSettingsService.replaceFields(id, fields), + >( + "file-upload-settings", + "replaceFields", + ({ id, fields }) => fileUploadSettingsService.replaceFields(id, fields), + undefined, + () => [["file-upload-settings"]], ), addField: endpoint< { settingId: string; dto: CreateFileUploadFieldDto }, FileUploadField - >("file-upload-settings", "addField", ({ settingId, dto }) => - fileUploadSettingsService.addField(settingId, dto), + >( + "file-upload-settings", + "addField", + ({ settingId, dto }) => fileUploadSettingsService.addField(settingId, dto), + undefined, + () => [["file-upload-settings"]], ), updateField: endpoint< { fieldId: string; dto: UpdateFileUploadFieldDto }, FileUploadField - >("file-upload-settings", "updateField", ({ fieldId, dto }) => - fileUploadSettingsService.updateField(fieldId, dto), + >( + "file-upload-settings", + "updateField", + ({ fieldId, dto }) => fileUploadSettingsService.updateField(fieldId, dto), + undefined, + () => [["file-upload-settings"]], ), removeField: endpoint<{ fieldId: string }, void>( "file-upload-settings", "removeField", ({ fieldId }) => fileUploadSettingsService.removeField(fieldId), + undefined, + () => [["file-upload-settings"]], ), }, @@ -844,46 +1635,68 @@ export const api = { "dropdown-settings", "create", (payload) => dropdownSettingsService.create(payload), + undefined, + () => [["dropdown-settings"]], ), update: endpoint< { id: string; dto: UpdateDropdownSettingDto }, DropdownSetting - >("dropdown-settings", "update", ({ id, dto }) => - dropdownSettingsService.update(id, dto), + >( + "dropdown-settings", + "update", + ({ id, dto }) => dropdownSettingsService.update(id, dto), + undefined, + () => [["dropdown-settings"]], ), remove: endpoint<{ id: string }, void>( "dropdown-settings", "remove", ({ id }) => dropdownSettingsService.remove(id), + undefined, + () => [["dropdown-settings"]], ), replaceOptions: endpoint< { id: string; options: CreateDropdownOptionDto[] }, DropdownOption[] - >("dropdown-settings", "replaceOptions", ({ id, options }) => - dropdownSettingsService.replaceOptions(id, options), + >( + "dropdown-settings", + "replaceOptions", + ({ id, options }) => dropdownSettingsService.replaceOptions(id, options), + undefined, + () => [["dropdown-settings"]], ), addOption: endpoint< { id: string; dto: CreateDropdownOptionDto }, DropdownOption - >("dropdown-settings", "addOption", ({ id, dto }) => - dropdownSettingsService.addOption(id, dto), + >( + "dropdown-settings", + "addOption", + ({ id, dto }) => dropdownSettingsService.addOption(id, dto), + undefined, + () => [["dropdown-settings"]], ), updateOption: endpoint< { optionId: string; dto: UpdateDropdownOptionDto }, DropdownOption - >("dropdown-settings", "updateOption", ({ optionId, dto }) => - dropdownSettingsService.updateOption(optionId, dto), + >( + "dropdown-settings", + "updateOption", + ({ optionId, dto }) => dropdownSettingsService.updateOption(optionId, dto), + undefined, + () => [["dropdown-settings"]], ), removeOption: endpoint<{ optionId: string }, void>( "dropdown-settings", "removeOption", ({ optionId }) => dropdownSettingsService.removeOption(optionId), + undefined, + () => [["dropdown-settings"]], ), }, diff --git a/apps/edr-freight-web/backoffice/tsconfig.app.json b/apps/edr-freight-web/backoffice/tsconfig.app.json index ff909c216..9c7064567 100644 --- a/apps/edr-freight-web/backoffice/tsconfig.app.json +++ b/apps/edr-freight-web/backoffice/tsconfig.app.json @@ -4,9 +4,8 @@ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", "useDefineForClassFields": true, "skipLibCheck": true, - "baseUrl": ".", "paths": { - "@/*": ["src/*"] + "@/*": ["./src/*"] } }, "include": ["src"]