Add train deactivation feature and import train number management

- Introduced DEACTIVATED status for trains, allowing staff to park trains indefinitely.
- Implemented methods to deactivate and reactivate trains in the TrainBuilderService.
- Added UI components for train deactivation and reactivation in TrainBuilderDetailPage.
- Created a dropdown setting for admin-managed import train numbers, with corresponding migrations.
- Updated yard code length to accommodate soft-delete suffix.
- Enhanced train status handling to include DEACTIVATED state.
This commit is contained in:
Marshal
2026-07-20 07:04:23 +00:00
parent ab355d0e16
commit 771aa2a605
20 changed files with 467 additions and 36 deletions

View File

@@ -16,7 +16,8 @@ import { useEffect, useState } from "react";
import { api } from "@/services/api";
import type { TrainComposition } from "@/services/trainBuilder.service";
import { useToast } from "@/hooks/use-toast";
import { IMPORT_TRAIN_OPTIONS, exportRunFor } from "@/constants/trainRuns";
import { useImportTrainNumberOptions } from "@/hooks/useImportTrainNumberOptions";
import { exportRunFor } from "@/constants/trainRuns";
const parseError = (error: unknown, fallback: string) => {
if (isAxiosError(error)) {
@@ -42,6 +43,9 @@ export default function BuildTrainModal({ opened, onClose, onBuilt }: BuildTrain
const [notes, setNotes] = useState("");
const yardsQuery = useQuery(api.routes.yards.queryOptions({ staleTime: 5 * 60_000 }));
// Admin-managed run list (dropdown settings); numbers already on a train
// come back disabled so they cannot be picked twice.
const importNumbers = useImportTrainNumberOptions();
// Only serviceable locomotives standing in the selected yard can be coupled.
const locomotivesQuery = useQuery(
api.locomotives.listFiltered.queryOptions({
@@ -150,12 +154,13 @@ export default function BuildTrainModal({ opened, onClose, onBuilt }: BuildTrain
<Select
label="Import train number"
description="Even — Djibouti → Ethiopia runs"
placeholder="e.g. 8002"
data={IMPORT_TRAIN_OPTIONS}
placeholder={importNumbers.isLoading ? "Loading…" : "e.g. 8002"}
data={importNumbers.options}
value={importTrainNumber || null}
onChange={(value) => setImportTrainNumber(value ?? "")}
searchable
clearable
nothingFoundMessage="No free run numbers — add more in Dropdown Settings"
/>
</Group>
<Select