refactor: migrate from most of the custom hooks into the api.ts

This commit is contained in:
Nathnael
2026-06-22 13:46:38 +00:00
parent c6bc636495
commit 2700a3edec
50 changed files with 1165 additions and 1249 deletions

View File

@@ -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() {
</Button>
</Group>
<Grid gutter="lg" mt="lg">
<Grid gap="lg" mt="lg">
{/* LEFT — form */}
<Grid.Col span={{ base: 12, lg: 8 }}>
<Stack gap="lg">
@@ -453,7 +455,6 @@ export default function NewBookingPage() {
value={originYardId}
onChange={(v) => {
setOriginYardId(v);
setTrainScheduleId(null);
}}
searchable
disabled={isLoading}

View File

@@ -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<string | null>(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 = () => {

View File

@@ -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() {
<DeleteFileUploadSettingDialog
settingLabel={setting.label}
settingCode={setting.code}
onConfirm={() => deleteMutation.mutate(setting.id)}
onConfirm={() => deleteMutation.mutate({ id: setting.id })}
>
<ActionIcon
variant="default"

View File

@@ -19,7 +19,8 @@ import type {
FileUploadSetting,
} from "@/types/fileUploadSettings";
import { getMinFiles } from "@/types/fileUploadSettings";
import { useReplaceFileUploadFields } from "@/hooks/useFileUploadSettings";
import { useMutation } from "@tanstack/react-query";
import { api } from "@/services/api";
export interface ManageFileUploadFieldsDialogProps {
setting: FileUploadSetting;
@@ -77,7 +78,9 @@ export default function ManageFileUploadFieldsDialog({
const [fields, setFields] = useState<DraftField[]>(seed);
const replaceMutation = useReplaceFileUploadFields();
const replaceMutation = useMutation(
api.fileUploadSettings.replaceFields.mutationOptions(),
);
const update = (i: number, patch: Partial<DraftField>) =>
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) =>

View File

@@ -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<DropdownSetting[]>(
() => (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())}
/>

View File

@@ -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<string | null>(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 = () => {

View File

@@ -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<DraftOption[]>(seed);
const replaceMutation = useReplaceDropdownOptions();
const replaceMutation = useMutation(
api.dropdownSettings.replaceOptions.mutationOptions(),
);
const update = (i: number, patch: Partial<DraftOption>) =>
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) =>

View File

@@ -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 (
<FleetCrudPage<Train>
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 (
<FleetCrudPage<Locomotive>
@@ -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"

View File

@@ -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<string, unknown>) => {
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`,
});

View File

@@ -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();

View File

@@ -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;

View File

@@ -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("");

View File

@@ -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;

View File

@@ -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 (

View File

@@ -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),

View File

@@ -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),

View File

@@ -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 (