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 queryClient = useQueryClient();
|
||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const { customer } = useAuth();
|
const { customer } = useAuth();
|
||||||
const { data: referenceData } = useQuery(
|
const { data: referenceData, isLoading: refDataLoading } = useQuery(
|
||||||
api.bookings.referenceData.queryOptions(),
|
api.bookings.referenceData.queryOptions(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -181,13 +181,14 @@ export default function NewBookingPage() {
|
|||||||
{step === 1 && <Step1ContractType form={form} />}
|
{step === 1 && <Step1ContractType form={form} />}
|
||||||
{step === 2 && <Step2ServiceType form={form} />}
|
{step === 2 && <Step2ServiceType form={form} />}
|
||||||
{step === 3 && (
|
{step === 3 && (
|
||||||
<Step4Route form={form} referenceData={referenceData} />
|
<Step4Route form={form} referenceData={referenceData} isLoading={refDataLoading} />
|
||||||
)}
|
)}
|
||||||
{step === 4 && (
|
{step === 4 && (
|
||||||
<Step5CargoDetails
|
<Step5CargoDetails
|
||||||
form={form}
|
form={form}
|
||||||
direction={direction}
|
direction={direction}
|
||||||
referenceData={referenceData}
|
referenceData={referenceData}
|
||||||
|
isLoading={refDataLoading}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{step === 5 && (
|
{step === 5 && (
|
||||||
|
|||||||
@@ -1,27 +1,28 @@
|
|||||||
|
import { useEffect, useMemo } from "react";
|
||||||
import { Controller, type UseFormReturn } from "react-hook-form";
|
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||||
import { Flame, MapPin, Snowflake } from "lucide-react";
|
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 { Freight } from "@edr/types";
|
||||||
import {
|
import {
|
||||||
type BookingFormValues,
|
type BookingFormValues,
|
||||||
getRouteDirection,
|
getRouteDirection,
|
||||||
} from "./schema";
|
} from "./schema";
|
||||||
import {
|
import {
|
||||||
AlertBox,
|
|
||||||
SelectField,
|
SelectField,
|
||||||
StepHeader,
|
StepHeader,
|
||||||
StepLabel,
|
StepLabel,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { useEffect, useMemo } from "react";
|
|
||||||
|
|
||||||
type BookingForm = UseFormReturn<BookingFormValues>;
|
type BookingForm = UseFormReturn<BookingFormValues>;
|
||||||
|
|
||||||
export function Step4Route({
|
export function Step4Route({
|
||||||
form,
|
form,
|
||||||
referenceData,
|
referenceData,
|
||||||
|
isLoading,
|
||||||
}: {
|
}: {
|
||||||
form: BookingForm;
|
form: BookingForm;
|
||||||
referenceData?: Freight.BookingReferenceData;
|
referenceData?: Freight.BookingReferenceData;
|
||||||
|
isLoading?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const originYard = form.watch("originYard");
|
const originYard = form.watch("originYard");
|
||||||
const destinationYard = form.watch("destinationYard");
|
const destinationYard = form.watch("destinationYard");
|
||||||
@@ -70,52 +71,50 @@ export function Step4Route({
|
|||||||
description="Select the origin and destination yards."
|
description="Select the origin and destination yards."
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="space-y-3">
|
{isLoading ? (
|
||||||
<StepLabel>Route</StepLabel>
|
<LoadingSkeleton />
|
||||||
<div className="grid gap-3 sm:grid-cols-2">
|
) : (
|
||||||
<Controller
|
<div className="space-y-3">
|
||||||
name="originYard"
|
<StepLabel>Route</StepLabel>
|
||||||
control={form.control}
|
<div className="grid gap-3 sm:grid-cols-2">
|
||||||
render={({ field, fieldState }) => (
|
<Controller
|
||||||
<SelectField
|
name="originYard"
|
||||||
field={field}
|
control={form.control}
|
||||||
error={fieldState.error}
|
render={({ field, fieldState }) => (
|
||||||
label="Origin Yard*"
|
<SelectField
|
||||||
placeholder="Select origin..."
|
field={field}
|
||||||
disabled={stationSelectDisabled}
|
error={fieldState.error}
|
||||||
>
|
label="Origin Yard*"
|
||||||
<YardSelectOptions
|
placeholder="Select origin..."
|
||||||
options={yardOptions}
|
disabled={stationSelectDisabled}
|
||||||
excludeValue={destinationYard}
|
>
|
||||||
/>
|
<YardSelectOptions
|
||||||
</SelectField>
|
options={yardOptions}
|
||||||
)}
|
excludeValue={destinationYard}
|
||||||
/>
|
/>
|
||||||
<Controller
|
</SelectField>
|
||||||
name="destinationYard"
|
)}
|
||||||
control={form.control}
|
/>
|
||||||
render={({ field, fieldState }) => (
|
<Controller
|
||||||
<SelectField
|
name="destinationYard"
|
||||||
field={field}
|
control={form.control}
|
||||||
error={fieldState.error}
|
render={({ field, fieldState }) => (
|
||||||
label="Destination Yard *"
|
<SelectField
|
||||||
placeholder="Select destination..."
|
field={field}
|
||||||
disabled={stationSelectDisabled}
|
error={fieldState.error}
|
||||||
>
|
label="Destination Yard *"
|
||||||
<YardSelectOptions
|
placeholder="Select destination..."
|
||||||
options={yardOptions}
|
disabled={stationSelectDisabled}
|
||||||
excludeValue={originYard}
|
>
|
||||||
/>
|
<YardSelectOptions
|
||||||
</SelectField>
|
options={yardOptions}
|
||||||
)}
|
excludeValue={originYard}
|
||||||
/>
|
/>
|
||||||
</div>
|
</SelectField>
|
||||||
{!referenceData && (
|
)}
|
||||||
<AlertBox tone="warning">
|
/>
|
||||||
Loading reference data...
|
</div>
|
||||||
</AlertBox>
|
{direction && (
|
||||||
)}
|
|
||||||
{direction && (
|
|
||||||
<div
|
<div
|
||||||
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium ${directionStyle[direction]}`}
|
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]}
|
{directionLabel[direction]}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{direction && direction != "domestic" && (
|
{direction && direction != "domestic" && (
|
||||||
<Controller
|
<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({
|
function YardSelectOptions({
|
||||||
options,
|
options,
|
||||||
excludeValue,
|
excludeValue,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { Controller, useFieldArray, type UseFormReturn } from "react-hook-form";
|
import { Controller, useFieldArray, type UseFormReturn } from "react-hook-form";
|
||||||
import { MapPin, Package, Plus, Trash2, Weight } from "lucide-react";
|
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 type { Freight } from "@edr/types";
|
||||||
import {
|
import {
|
||||||
calcWagons,
|
calcWagons,
|
||||||
@@ -23,10 +23,12 @@ export function Step5CargoDetails({
|
|||||||
form,
|
form,
|
||||||
direction,
|
direction,
|
||||||
referenceData,
|
referenceData,
|
||||||
|
isLoading,
|
||||||
}: {
|
}: {
|
||||||
form: BookingForm;
|
form: BookingForm;
|
||||||
direction: RouteDirection;
|
direction: RouteDirection;
|
||||||
referenceData?: Freight.BookingReferenceData;
|
referenceData?: Freight.BookingReferenceData;
|
||||||
|
isLoading?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const cargoType = form.watch("cargoType");
|
const cargoType = form.watch("cargoType");
|
||||||
const freightType = form.watch("freightType");
|
const freightType = form.watch("freightType");
|
||||||
@@ -69,6 +71,26 @@ export function Step5CargoDetails({
|
|||||||
return null;
|
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 (
|
return (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<StepHeader
|
<StepHeader
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export type {
|
|||||||
|
|
||||||
export * from "./components/button";
|
export * from "./components/button";
|
||||||
export * from "./components/input";
|
export * from "./components/input";
|
||||||
|
export * from "./components/skeleton";
|
||||||
export * from "./components/textarea";
|
export * from "./components/textarea";
|
||||||
export * from "./components/label";
|
export * from "./components/label";
|
||||||
export * from "./components/card";
|
export * from "./components/card";
|
||||||
|
|||||||
Reference in New Issue
Block a user