mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
feat(bookings): Implement skeleton loaders for new booking form steps
This commit is contained in:
@@ -33,7 +33,7 @@ export default function NewBookingPage() {
|
||||
const queryClient = useQueryClient();
|
||||
const [step, setStep] = useState(1);
|
||||
const { customer } = useAuth();
|
||||
const { data: referenceData } = useQuery(
|
||||
const { data: referenceData, isLoading: refDataLoading } = useQuery(
|
||||
api.bookings.referenceData.queryOptions(),
|
||||
);
|
||||
|
||||
@@ -181,13 +181,14 @@ export default function NewBookingPage() {
|
||||
{step === 1 && <Step1ContractType form={form} />}
|
||||
{step === 2 && <Step2ServiceType form={form} />}
|
||||
{step === 3 && (
|
||||
<Step4Route form={form} referenceData={referenceData} />
|
||||
<Step4Route form={form} referenceData={referenceData} isLoading={refDataLoading} />
|
||||
)}
|
||||
{step === 4 && (
|
||||
<Step5CargoDetails
|
||||
form={form}
|
||||
direction={direction}
|
||||
referenceData={referenceData}
|
||||
isLoading={refDataLoading}
|
||||
/>
|
||||
)}
|
||||
{step === 5 && (
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||
import { Flame, MapPin, Snowflake } from "lucide-react";
|
||||
import { Field, SelectItem, Separator, Switch } from "@edr/ui-common";
|
||||
import { Field, SelectItem, Separator, Skeleton, Switch } from "@edr/ui-common";
|
||||
import type { Freight } from "@edr/types";
|
||||
import {
|
||||
type BookingFormValues,
|
||||
getRouteDirection,
|
||||
} from "./schema";
|
||||
import {
|
||||
AlertBox,
|
||||
SelectField,
|
||||
StepHeader,
|
||||
StepLabel,
|
||||
} from "./shared";
|
||||
import { useEffect, useMemo } from "react";
|
||||
|
||||
type BookingForm = UseFormReturn<BookingFormValues>;
|
||||
|
||||
export function Step4Route({
|
||||
form,
|
||||
referenceData,
|
||||
isLoading,
|
||||
}: {
|
||||
form: BookingForm;
|
||||
referenceData?: Freight.BookingReferenceData;
|
||||
isLoading?: boolean;
|
||||
}) {
|
||||
const originYard = form.watch("originYard");
|
||||
const destinationYard = form.watch("destinationYard");
|
||||
@@ -70,52 +71,50 @@ export function Step4Route({
|
||||
description="Select the origin and destination yards."
|
||||
/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<StepLabel>Route</StepLabel>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Controller
|
||||
name="originYard"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Origin Yard*"
|
||||
placeholder="Select origin..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<YardSelectOptions
|
||||
options={yardOptions}
|
||||
excludeValue={destinationYard}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="destinationYard"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Destination Yard *"
|
||||
placeholder="Select destination..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<YardSelectOptions
|
||||
options={yardOptions}
|
||||
excludeValue={originYard}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{!referenceData && (
|
||||
<AlertBox tone="warning">
|
||||
Loading reference data...
|
||||
</AlertBox>
|
||||
)}
|
||||
{direction && (
|
||||
{isLoading ? (
|
||||
<LoadingSkeleton />
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
<StepLabel>Route</StepLabel>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Controller
|
||||
name="originYard"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Origin Yard*"
|
||||
placeholder="Select origin..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<YardSelectOptions
|
||||
options={yardOptions}
|
||||
excludeValue={destinationYard}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
name="destinationYard"
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Destination Yard *"
|
||||
placeholder="Select destination..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<YardSelectOptions
|
||||
options={yardOptions}
|
||||
excludeValue={originYard}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{direction && (
|
||||
<div
|
||||
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium ${directionStyle[direction]}`}
|
||||
>
|
||||
@@ -123,7 +122,8 @@ export function Step4Route({
|
||||
{directionLabel[direction]}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{direction && direction != "domestic" && (
|
||||
<Controller
|
||||
@@ -190,6 +190,24 @@ export function Step4Route({
|
||||
);
|
||||
}
|
||||
|
||||
function LoadingSkeleton() {
|
||||
return (
|
||||
<div className="space-y-4 rounded-xl border border-border p-4">
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-3 w-20" />
|
||||
<Skeleton className="h-10 w-full" />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<Skeleton className="h-10 w-full" />
|
||||
</div>
|
||||
</div>
|
||||
<Skeleton className="h-8 w-full" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function YardSelectOptions({
|
||||
options,
|
||||
excludeValue,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { Controller, useFieldArray, type UseFormReturn } from "react-hook-form";
|
||||
import { MapPin, Package, Plus, Trash2, Weight } from "lucide-react";
|
||||
import { Button, Field, FieldError, FieldLabel, Input } from "@edr/ui-common";
|
||||
import { Button, Field, FieldError, FieldLabel, Input, Skeleton } from "@edr/ui-common";
|
||||
import type { Freight } from "@edr/types";
|
||||
import {
|
||||
calcWagons,
|
||||
@@ -23,10 +23,12 @@ export function Step5CargoDetails({
|
||||
form,
|
||||
direction,
|
||||
referenceData,
|
||||
isLoading,
|
||||
}: {
|
||||
form: BookingForm;
|
||||
direction: RouteDirection;
|
||||
referenceData?: Freight.BookingReferenceData;
|
||||
isLoading?: boolean;
|
||||
}) {
|
||||
const cargoType = form.watch("cargoType");
|
||||
const freightType = form.watch("freightType");
|
||||
@@ -69,6 +71,26 @@ export function Step5CargoDetails({
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<StepHeader
|
||||
title="Cargo Details"
|
||||
description="Define your cargo type, weight, and container configuration."
|
||||
/>
|
||||
<div className="space-y-4 rounded-xl border border-border p-4">
|
||||
<Skeleton className="h-4 w-24" />
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Skeleton className="h-24 w-full" />
|
||||
<Skeleton className="h-24 w-full" />
|
||||
</div>
|
||||
<Skeleton className="h-10 w-full" />
|
||||
<Skeleton className="h-10 w-1/3" />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<StepHeader
|
||||
|
||||
@@ -23,6 +23,7 @@ export type {
|
||||
|
||||
export * from "./components/button";
|
||||
export * from "./components/input";
|
||||
export * from "./components/skeleton";
|
||||
export * from "./components/textarea";
|
||||
export * from "./components/label";
|
||||
export * from "./components/card";
|
||||
|
||||
Reference in New Issue
Block a user