automation of loading and unloading

This commit is contained in:
Hagernesh
2026-06-17 22:33:06 +00:00
1637 changed files with 375027 additions and 20437 deletions

View File

@@ -0,0 +1,35 @@
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<string, unknown>) => fleetService.create(slug, data),
onSuccess: invalidate,
});
const update = useMutation({
mutationFn: ({ id, data }: { id: string; data: Record<string, unknown> }) =>
fleetService.update(slug, id, data),
onSuccess: invalidate,
});
const remove = useMutation({
mutationFn: (id: string) => fleetService.remove(slug, id),
onSuccess: invalidate,
});
return { create, update, remove };
}

View File

@@ -26,6 +26,52 @@ export const useRuleEngineList = (
queryFn: () => ruleEngineService.list(resource, params),
});
const ORDER_LIST_PAGE_SIZE = 500;
export const useRuleEngineOrderList = (
resource: RuleEngineResourceSlug,
enabled: boolean,
sortBy?: string,
) =>
useQuery({
queryKey: QUERY_KEYS.RULE_ENGINE.orderList(resource),
queryFn: () =>
ruleEngineService.list(resource, {
page: 1,
pageSize: ORDER_LIST_PAGE_SIZE,
sortBy,
sortOrder: "ASC",
}),
enabled,
});
export const useRuleEngineOrderMutations = (resource: RuleEngineResourceSlug) => {
const qc = useQueryClient();
const reorder = useMutation({
mutationFn: (payload: { ids: string[]; requiresDirectorApproval?: boolean }) =>
ruleEngineService.reorder(resource, payload),
onSuccess: async () => {
toast.success("Order updated");
await invalidateRuleEngineList(qc, resource);
await qc.invalidateQueries({ queryKey: QUERY_KEYS.RULE_ENGINE.orderList(resource) });
},
onError: () => toast.error("Failed to update order"),
});
const moveOrder = useMutation({
mutationFn: ({ id, direction }: { id: string; direction: "up" | "down" }) =>
ruleEngineService.moveOrder(resource, id, direction),
onSuccess: async () => {
await invalidateRuleEngineList(qc, resource);
await qc.invalidateQueries({ queryKey: QUERY_KEYS.RULE_ENGINE.orderList(resource) });
},
onError: () => toast.error("Cannot move item further in that direction"),
});
return { reorder, moveOrder };
};
export const useCargoTypeParentOptions = (excludeId?: string, enabled = true) =>
useQuery({
queryKey: QUERY_KEYS.RULE_ENGINE.selectOptions("cargo-types"),

View File

@@ -0,0 +1,302 @@
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),
});
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),
});
},
});
};

View File

@@ -1 +0,0 @@
export * from '@/components/container_management/use-cargoes';

View File

@@ -1 +0,0 @@
export * from '@/components/container_management/use-containers';

View File

@@ -0,0 +1,52 @@
import { useQuery } from "@tanstack/react-query";
import { QUERY_KEYS } from "@/constants/QUERY_KEYS";
import { overviewService } from "@/services/overview.service";
import type { OverviewRange } from "@/types/overview";
export function useOverview(range: OverviewRange = "30d") {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.dashboard(range),
queryFn: () => overviewService.getDashboard(range),
});
}
export function useOverviewBookingsTab(range: OverviewRange, enabled: boolean) {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.bookingsTab(range),
queryFn: () => overviewService.getBookingsTab(range),
enabled,
});
}
export function useOverviewBillingTab(range: OverviewRange, enabled: boolean) {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.billingTab(range),
queryFn: () => overviewService.getBillingTab(range),
enabled,
});
}
export function useOverviewOperationsTab(enabled: boolean) {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.operationsTab(),
queryFn: () => overviewService.getOperationsTab(),
enabled,
});
}
export function useOverviewCustomersTab(range: OverviewRange, enabled: boolean) {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.customersTab(range),
queryFn: () => overviewService.getCustomersTab(range),
enabled,
});
}
export function useOverviewStaffTab(range: OverviewRange, enabled: boolean) {
return useQuery({
queryKey: QUERY_KEYS.OVERVIEW.staffTab(range),
queryFn: () => overviewService.getStaffTab(range),
enabled,
});
}

View File

@@ -0,0 +1,23 @@
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,
});
}

View File

@@ -0,0 +1,30 @@
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"),
});
}

View File

@@ -1,15 +1,21 @@
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() {
return useQuery({ queryKey: wagonKeys.all, queryFn: () => wagonService.getAll().then(res => res.data) });
export function useWagons(filters?: WagonListFilters) {
return useQuery({
queryKey: wagonKeys.list(filters),
queryFn: () => wagonService.getAll(filters ?? {}).then((res) => res.data),
});
}
export const useGetWagons = useWagons;