feat: enhance locomotive creation and management with optional code generation and default max pull weight

This commit is contained in:
Marshal
2026-06-28 23:02:40 +00:00
parent d6ae1ac18d
commit 47c91cad03
9 changed files with 66 additions and 80 deletions

View File

@@ -1,15 +1,10 @@
import { useState } from "react";
import { Download, Zap, FileText, Clock } from "lucide-react";
import { Stack, Text, Button } from "@mantine/core";
import { AllocateBookingWizard } from "@/components/trainScheduling/AllocateBookingWizard";
import type { BookingDetail } from "@/types/booking";
import { BookingActionsMenu } from "./BookingActionsMenu";
import { SectionCard } from "./detail/SectionCard";
import { toBookingListRow } from "@/features/bookings/mapBookingListRow";
import { canAllocateBooking } from "@/features/bookings/booking-actions.config";
import { useAuth } from "@/auth/useAuth";
import { canManageScheduling } from "@/lib/permissions";
import type { useBookingMutations } from "@/hooks/bookings/useBookings";
type Mutations = ReturnType<typeof useBookingMutations>;
@@ -21,11 +16,8 @@ interface BookingActionsToolbarProps {
/** Detail-page actions: primary toolbar + downloads. */
export function BookingActionsToolbar({ booking, mutations }: BookingActionsToolbarProps) {
const { user } = useAuth();
const row = toBookingListRow(booking);
const { status } = booking;
const [allocateOpen, setAllocateOpen] = useState(false);
const canAllocate = canManageScheduling(user);
const downloadBlob = async (fn: () => Promise<Blob>, filename: string) => {
const blob = await fn();
@@ -106,11 +98,7 @@ export function BookingActionsToolbar({ booking, mutations }: BookingActionsTool
<Text size="xs" c="dimmed">
Confirm each step before it is applied.
</Text>
<BookingActionsMenu
row={row}
variant="toolbar"
onAllocateBooking={() => setAllocateOpen(true)}
/>
<BookingActionsMenu row={row} variant="toolbar" />
</Stack>
</SectionCard>
@@ -130,14 +118,6 @@ export function BookingActionsToolbar({ booking, mutations }: BookingActionsTool
</Button>
</SectionCard>
)}
{canAllocate && canAllocateBooking(booking) ? (
<AllocateBookingWizard
booking={booking}
opened={allocateOpen}
onClose={() => setAllocateOpen(false)}
/>
) : null}
</Stack>
);
}

View File

@@ -66,12 +66,20 @@ const FleetFormDialog = ({
const [values, setValues] = useState<Record<string, unknown>>({});
const [errors, setErrors] = useState<Record<string, string>>({});
// Seed the form ONLY when the dialog opens or the edited record changes — NOT
// when `fields`/`emptyValues` get new object refs (they're rebuilt whenever the
// dynamic select options finish loading). Re-seeding on those would wipe the
// user's in-progress edits (e.g. a changed Current Yard / status) the moment
// the yard or wagon-type options resolve.
const recordId =
initialRecord && "id" in initialRecord ? String(initialRecord.id) : null;
useEffect(() => {
if (open) {
setValues(buildInitialValues(fields, emptyValues, initialRecord));
setErrors({});
}
}, [open, fields, emptyValues, initialRecord]);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [open, recordId]);
const shortFields = useMemo(
() => fields.filter((f) => f.type !== "textarea"),