feat(bookings): Revamp Edit Booking page to a single-form experience with enhanced details

This commit is contained in:
ghost2023
2026-06-06 11:43:59 +03:00
parent efdcb9a7c8
commit c76c6dbf90

View File

@@ -1,40 +1,51 @@
import { useMemo, useState } from "react";
import { useMemo } from "react";
import { useFieldArray, Controller, useForm } from "react-hook-form";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { useNavigate, useParams } from "react-router-dom";
import {
AlertCircle,
Check,
ChevronLeft,
ChevronRight,
LoaderCircle,
Loader2,
Package,
Weight,
Plus,
Trash2,
MapPin,
Flame,
Snowflake,
Truck,
FileText,
} from "lucide-react";
import { Button } from "@edr/ui-common";
import {
Button,
Field,
FieldLabel,
FieldError,
Input,
Badge,
Switch,
Textarea,
Separator,
Skeleton,
} from "@edr/ui-common";
import type { Freight } from "@edr/types";
import { api } from "@/services/api";
import type {
CreateBookingPayload,
} from "@/services/bookings.service";
import type { CreateBookingPayload } from "@/services/bookings.service";
import {
BookingFormInputValues,
STEPS,
bookingFormSchema,
getRouteDirection,
initialBookingFormValues,
stepFields,
type BookingFormValues,
type RouteDirection,
} from "./new-booking-form/schema";
import { StepIndicator } from "./new-booking-form/StepIndicator";
import {
Step1ContractType,
Step2ServiceType,
Step4Route,
Step5CargoDetails,
Step8Review,
} from "./new-booking-form/steps";
import type { Freight } from "@edr/types";
SelectField,
SelectItem,
AlertBox,
} from "./new-booking-form/shared";
function yardNameFromBooking(yard: { label?: string; code?: string; name?: string } | undefined | null): string {
return yard?.label ?? yard?.name ?? yard?.code ?? "";
@@ -103,7 +114,6 @@ export default function EditBookingPage() {
const { id } = useParams<{ id: string }>();
const navigate = useNavigate();
const queryClient = useQueryClient();
const [step, setStep] = useState(1);
const bookingQuery = useQuery(
api.bookings.get.queryOptions({
@@ -112,7 +122,7 @@ export default function EditBookingPage() {
}),
);
const { data: referenceData, isLoading: refDataLoading } = useQuery(
const { data: referenceData } = useQuery(
api.bookings.referenceData.queryOptions({
enabled: !!bookingQuery.data,
}),
@@ -144,17 +154,72 @@ export default function EditBookingPage() {
const originYard = form.watch("originYard");
const destinationYard = form.watch("destinationYard");
const serviceType = form.watch("serviceType");
const firstMileEnabled = form.watch("firstMile.enabled");
const lastMileEnabled = form.watch("lastMile.enabled");
const cargoType = form.watch("cargoType");
const freightType = form.watch("freightType");
const containers = form.watch("containers");
const direction: RouteDirection = useMemo(
() => getRouteDirection(originYard, destinationYard),
[originYard, destinationYard],
);
async function handleContinue() {
const valid = await form.trigger(stepFields[step], { shouldFocus: true });
if (!valid) return;
setStep((currentStep) => Math.min(STEPS.length, currentStep + 1));
}
const { fields, append, remove } = useFieldArray({
control: form.control,
name: "containers",
});
const yardOptions = useMemo(() => {
if (!referenceData?.yard) return [];
return referenceData.yard.map((y) => ({
value: y.name,
label: y.name,
country: y.country,
}));
}, [referenceData]);
const shippingLineOptions = useMemo(() => {
if (!referenceData?.shipping_line) return [];
return referenceData.shipping_line.map((sl) => ({
value: sl.name,
label: sl.name,
}));
}, [referenceData]);
const freightTypeGroups = useMemo(() => {
if (!referenceData?.cargo_type) return [];
return referenceData.cargo_type.filter(
(g) => g.code !== "CONTAINER",
);
}, [referenceData]);
const commodityOptions = useMemo(() => {
if (!referenceData?.cargo_type || !freightType) return [];
const group = referenceData.cargo_type.find(
(g) => g.code.toLowerCase() === freightType,
);
return group?.children?.map((c) => c.name) ?? [];
}, [referenceData, freightType]);
const containerTypeOptions = useMemo(() => {
if (!referenceData?.containers) return [];
return referenceData.containers.flatMap((group) =>
group.types.map((t) => t.name),
);
}, [referenceData]);
const directionStyle: Record<string, string> = {
export: "bg-sky-50 text-sky-800 border-sky-200",
import: "bg-amber-50 text-amber-800 border-amber-200",
domestic: "bg-muted text-muted-foreground border-border",
};
const directionLabel: Record<string, string> = {
export: "Export workflow (inside country to outside country)",
import: "Import workflow (outside country to inside country)",
domestic: "Domestic corridor",
};
const handleSubmit = form.handleSubmit((data) => {
const yards = referenceData?.yard ?? [];
@@ -290,83 +355,744 @@ export default function EditBookingPage() {
return (
<form
id="edit-booking-form"
className="flex flex-col"
className="mx-auto max-w-4xl"
onSubmit={handleSubmit}
>
<div className="sticky top-0 z-20">
<div className="mx-auto max-w-4xl space-y-3 px-6 pt-4">
<StepIndicator step={step} />
</div>
</div>
<div className="px-6 py-8">
<h1 className="text-2xl font-bold tracking-tight">
Edit Booking {booking.reference ?? ""}
</h1>
<p className="mt-1 text-sm text-muted-foreground">
Update the booking details below. All changes are saved together.
</p>
<div className="flex-1">
<div className="mx-auto max-w-4xl px-6 py-8">
{updateMutation.isError && (
<div className="mb-6 flex items-start gap-3 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-500" />
<div>
<p className="font-semibold">Failed to save changes</p>
<p className="mt-1 text-red-600">
{updateMutation.error instanceof Error
? updateMutation.error.message
: "An unexpected error occurred. Please try again."}
</p>
{updateMutation.isError && (
<div className="mt-6 flex items-start gap-3 rounded-xl border border-red-200 bg-red-50 p-4 text-sm text-red-800">
<AlertCircle className="mt-0.5 h-4 w-4 shrink-0 text-red-500" />
<div>
<p className="font-semibold">Failed to save changes</p>
<p className="mt-1 text-red-600">
{updateMutation.error instanceof Error
? updateMutation.error.message
: "An unexpected error occurred. Please try again."}
</p>
</div>
</div>
)}
<div className="mt-8 space-y-10">
{/* ── Section 1: Contract ── */}
<section className="space-y-4">
<div>
<h2 className="text-lg font-bold">Contract</h2>
<p className="text-sm text-muted-foreground">
New contract or renewal of an existing one.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
name="contractType"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Contract Type *"
placeholder="Select contract type..."
>
<SelectItem value="new">New Contract</SelectItem>
<SelectItem value="renewal">Contract Renewal</SelectItem>
</SelectField>
)}
/>
</div>
</section>
<Separator />
{/* ── Section 2: Service ── */}
<section className="space-y-4">
<div>
<h2 className="text-lg font-bold">Service</h2>
<p className="text-sm text-muted-foreground">
Select the service combination and configure trucking options.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
name="serviceType"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Service Type *"
placeholder="Select service type..."
>
<SelectItem value="rail">Rail Transport Only</SelectItem>
<SelectItem value="rail_forwarding">Logistics (Rail + Forwarding)</SelectItem>
</SelectField>
)}
/>
<Controller
name="equipmentReturn"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Equipment Return"
placeholder="Select..."
>
<SelectItem value="with_return">With Return</SelectItem>
<SelectItem value="without_return">Without Return</SelectItem>
</SelectField>
)}
/>
</div>
{serviceType === "rail_forwarding" && (
<div className="divide-y divide-border rounded-xl border border-border">
<div className="p-4">
<Controller
name="firstMile.enabled"
control={form.control}
render={({ field }) => (
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3">
<Truck className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
<div>
<p className="text-sm font-medium">First Mile - Pick-up</p>
<p className="mt-0.5 text-xs text-muted-foreground">
Truck pick-up from your premises to the origin rail yard.
</p>
</div>
</div>
<Switch
checked={field.value}
onCheckedChange={(value) => {
field.onChange(value);
if (!value) {
form.setValue("firstMile.pickUpAddress", "", {
shouldDirty: true,
shouldValidate: true,
});
}
}}
/>
</div>
)}
/>
{firstMileEnabled && (
<Controller
name="firstMile.pickUpAddress"
control={form.control}
render={({ field, fieldState }) => (
<Field className="mt-3" data-invalid={fieldState.invalid}>
<Input
{...field}
aria-invalid={fieldState.invalid}
placeholder="Pick-up address *"
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
)}
</div>
<div className="p-4">
<Controller
name="lastMile.enabled"
control={form.control}
render={({ field }) => (
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3">
<Truck className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
<div>
<p className="text-sm font-medium">Last Mile - Delivery</p>
<p className="mt-0.5 text-xs text-muted-foreground">
Truck delivery from the destination rail yard to the final address.
</p>
</div>
</div>
<Switch
checked={field.value}
onCheckedChange={(value) => {
field.onChange(value);
if (!value) {
form.setValue("lastMile.deliveryAddress", "", {
shouldDirty: true,
shouldValidate: true,
});
}
}}
/>
</div>
)}
/>
{lastMileEnabled && (
<Controller
name="lastMile.deliveryAddress"
control={form.control}
render={({ field, fieldState }) => (
<Field className="mt-3" data-invalid={fieldState.invalid}>
<Input
{...field}
aria-invalid={fieldState.invalid}
placeholder="Delivery address *"
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
)}
</div>
<div className="p-4">
<Controller
name="customsClearingEnabled"
control={form.control}
render={({ field }) => (
<div className="flex items-start justify-between gap-4">
<div className="flex items-start gap-3">
<FileText className="mt-0.5 h-4 w-4 shrink-0 text-muted-foreground" />
<div>
<p className="text-sm font-medium">Customs Clearing Service</p>
<p className="mt-0.5 text-xs text-muted-foreground">
EDR handles customs documentation and clearance on your behalf.
</p>
</div>
</div>
<Switch
checked={field.value}
onCheckedChange={field.onChange}
/>
</div>
)}
/>
</div>
</div>
)}
</section>
<Separator />
{/* ── Section 3: Route ── */}
<section className="space-y-4">
<div>
<h2 className="text-lg font-bold">Route</h2>
<p className="text-sm text-muted-foreground">
Select the origin and destination yards.
</p>
</div>
<div className="grid gap-4 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={yardOptions.length === 0}
>
{yardOptions.length === 0 ? (
<SelectItem value="__empty" disabled>No yards available</SelectItem>
) : (
yardOptions
.filter((y) => y.value !== destinationYard)
.map((y) => (
<SelectItem key={y.value} value={y.value}>
{y.label}
</SelectItem>
))
)}
</SelectField>
)}
/>
<Controller
name="destinationYard"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Destination Yard *"
placeholder="Select destination..."
disabled={yardOptions.length === 0}
>
{yardOptions.length === 0 ? (
<SelectItem value="__empty" disabled>No yards available</SelectItem>
) : (
yardOptions
.filter((y) => y.value !== originYard)
.map((y) => (
<SelectItem key={y.value} value={y.value}>
{y.label}
</SelectItem>
))
)}
</SelectField>
)}
/>
</div>
{direction && (
<div
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium ${directionStyle[direction]}`}
>
<MapPin className="h-3.5 w-3.5 shrink-0" />
{directionLabel[direction]}
</div>
)}
{direction && direction !== "domestic" && (
<Controller
name="shippingLine"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Shipping Line"
placeholder="Select shipping line..."
>
{shippingLineOptions.map((sl) => (
<SelectItem key={sl.value} value={sl.value}>
{sl.label}
</SelectItem>
))}
</SelectField>
)}
/>
)}
<div className="space-y-0 divide-y divide-border rounded-xl border border-border">
<div className="flex items-center justify-between px-4 py-3">
<div className="flex items-center gap-3">
<Flame className="h-4 w-4 shrink-0 text-red-500" />
<div>
<p className="text-sm font-medium">Hazardous Material</p>
<p className="text-xs text-muted-foreground">
Applies a Hazard Surcharge to the final bill.
</p>
</div>
</div>
<Controller
name="isHazardous"
control={form.control}
render={({ field }) => (
<Switch checked={field.value} onCheckedChange={field.onChange} />
)}
/>
</div>
<div className="flex items-center justify-between px-4 py-3">
<div className="flex items-center gap-3">
<Snowflake className="h-4 w-4 shrink-0 text-sky-500" />
<div>
<p className="text-sm font-medium">Refrigerated Cargo</p>
<p className="text-xs text-muted-foreground">
Temperature-controlled transport applies a Refrigerator Surcharge.
</p>
</div>
</div>
<Controller
name="isRefrigerated"
control={form.control}
render={({ field }) => (
<Switch checked={field.value} onCheckedChange={field.onChange} />
)}
/>
</div>
</div>
)}
{step === 1 && <Step1ContractType form={form} />}
{step === 2 && <Step2ServiceType form={form} />}
{step === 3 && (
<Step4Route
form={form}
referenceData={referenceData}
isLoading={refDataLoading}
/>
)}
{step === 4 && (
<Step5CargoDetails
form={form}
direction={direction}
referenceData={referenceData}
isLoading={refDataLoading}
/>
)}
{step === 5 && (
<Step8Review form={form} setStep={setStep} direction={direction} />
)}
</div>
</div>
</section>
<div className="sticky bottom-0 z-20 border-t border-border bg-background px-6 py-4">
<div className="mx-auto flex max-w-4xl items-center justify-between">
<Separator />
{/* ── Section 4: Cargo ── */}
<section className="space-y-4">
<div>
<h2 className="text-lg font-bold">Cargo Details</h2>
<p className="text-sm text-muted-foreground">
Define your cargo type, weight, and container configuration.
</p>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
name="cargoType"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Cargo Type *"
placeholder="Select cargo type..."
>
<SelectItem value="container">Containerized</SelectItem>
<SelectItem value="bulk">General Cargo</SelectItem>
</SelectField>
)}
/>
<Controller
name="cargoWeight"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel htmlFor="cargoWeight">
Total Cargo Weight (Tons) *
</FieldLabel>
<div className="relative">
<Weight className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
<Input
{...field}
id="cargoWeight"
type="number"
aria-invalid={fieldState.invalid}
placeholder="0.00"
className="pl-9"
min="0"
step="0.01"
/>
</div>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
</div>
{cargoType === "bulk" && (
<>
<div className="grid gap-4 sm:grid-cols-2">
<Controller
name="freightType"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Freight Type *"
placeholder="Select freight type..."
>
{freightTypeGroups.map((group) => (
<SelectItem key={group.code} value={group.code.toLowerCase()}>
{group.name}
</SelectItem>
))}
</SelectField>
)}
/>
{freightType && commodityOptions.length > 0 && (
<Controller
name="bulkCommoditytype"
control={form.control}
render={({ field, fieldState }) => (
<SelectField
field={field}
error={fieldState.error}
label="Commodity *"
placeholder="Select commodity..."
>
{commodityOptions.map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectField>
)}
/>
)}
</div>
<Controller
name="consolidationEnabled"
control={form.control}
render={({ field }) => (
<div className="flex items-center justify-between rounded-xl border border-border px-4 py-3">
<div>
<p className="text-sm font-medium">Allow Consolidation</p>
<p className="text-xs text-muted-foreground">
Combine shipments to optimize costs.
</p>
</div>
<Switch checked={field.value} onCheckedChange={field.onChange} />
</div>
)}
/>
</>
)}
{cargoType === "container" && (
<div className="space-y-4">
<div className="flex items-center justify-between">
<p className="text-xs font-semibold uppercase tracking-wide text-muted-foreground">
Containers
</p>
<Button
type="button"
variant="outline"
size="sm"
onClick={() =>
append({
type: "20ft",
containerType: "",
qty: "1",
vgm: "",
})
}
>
<Plus className="mr-1 h-3.5 w-3.5" />
Add Container
</Button>
</div>
{fields.map((field, index) => {
const containerType = containers[index]?.type;
const vgm = containers[index]?.vgm ?? 0;
const alert = (() => {
if (containerType === "20ft" && +vgm > 0) {
const limit = direction === "export" ? 25 : 20;
if (+vgm > limit) {
return `VGM ${vgm}t exceeds the ${limit}t ${direction ?? "standard"} limit for a 20ft container. An Overweight Surcharge will apply.`;
}
}
if (containerType === "40ft" && +vgm > 32.5) {
return `VGM ${vgm}t exceeds the 32.5t global limit for a 40ft container. An Overweight Surcharge will apply.`;
}
return null;
})();
return (
<div
key={field.id}
className="rounded-xl border border-border p-4 space-y-4"
>
<div className="flex items-center justify-between">
<p className="text-sm font-medium">
Container {index + 1}
</p>
{fields.length > 1 && (
<button
type="button"
onClick={() => remove(index)}
className="flex items-center gap-1 text-xs text-destructive hover:underline"
>
<Trash2 className="h-3.5 w-3.5" />
Remove
</button>
)}
</div>
<div className="grid gap-3 sm:grid-cols-4">
<Controller
name={`containers.${index}.type`}
control={form.control}
render={({ field: typeField, fieldState }) => (
<SelectField
field={typeField}
error={fieldState.error}
label="Size *"
placeholder="Size..."
>
<SelectItem value="20ft">20ft (TEU)</SelectItem>
<SelectItem value="40ft">40ft (FEU)</SelectItem>
</SelectField>
)}
/>
<Controller
name={`containers.${index}.containerType`}
control={form.control}
render={({ field: ctField, fieldState }) => (
<SelectField
field={ctField}
error={fieldState.error}
label="Type *"
placeholder="Type..."
>
{containerTypeOptions.map((option) => (
<SelectItem key={option} value={option}>
{option}
</SelectItem>
))}
</SelectField>
)}
/>
<Controller
name={`containers.${index}.qty`}
control={form.control}
render={({ field: qtyField, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel>Quantity *</FieldLabel>
<Input
value={qtyField.value ?? 1}
onChange={(e) => qtyField.onChange(e.target.value)}
onBlur={qtyField.onBlur}
type="number"
aria-invalid={fieldState.invalid}
min="1"
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
<Controller
name={`containers.${index}.vgm`}
control={form.control}
render={({ field: vgmField, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<FieldLabel>VGM (Tons) *</FieldLabel>
<Input
value={vgmField.value ?? 0}
onChange={(e) => vgmField.onChange(e.target.value)}
onBlur={vgmField.onBlur}
type="number"
aria-invalid={fieldState.invalid}
placeholder="e.g. 18.5"
min="0"
step="0.1"
/>
<FieldError errors={[fieldState.error]} />
</Field>
)}
/>
</div>
{alert && (
<AlertBox tone="warning">
<strong>Overweight Alert:</strong> {alert}
</AlertBox>
)}
</div>
);
})}
{containers && (() => {
const Ft40Wagons = containers
.filter((c) => c.type === "40ft")
.reduce((sum, c) => sum + Number(c.qty), 0);
const Ft20Wagons = containers
.filter((c) => c.type === "20ft")
.reduce((sum, c) => sum + Number(c.qty), 0);
const hasOddUnit = Ft20Wagons % 2 === 1;
if (hasOddUnit) {
return (
<AlertBox tone="warning">
<div className="flex items-start gap-2">
<div>
<p className="font-semibold">Unpaired 20ft Container</p>
<p className="mt-1 text-xs">
One 20ft container occupies only half a wagon. The wagon
will depart once a co-loader is found to fill the
remaining slot, which{" "}
<strong>may delay departure</strong> beyond the standard
lead time.
</p>
</div>
</div>
</AlertBox>
);
}
return null;
})()}
</div>
)}
</section>
<Separator />
{/* ── Section 5: Notes & Submit ── */}
<section className="space-y-4">
<div>
<h2 className="text-lg font-bold">Notes & Confirmation</h2>
<p className="text-sm text-muted-foreground">
Add any special instructions and confirm the changes.
</p>
</div>
<Controller
name="notes"
control={form.control}
render={({ field }) => (
<Field>
<FieldLabel htmlFor="notes">Additional Notes</FieldLabel>
<Textarea
{...field}
id="notes"
placeholder="Any special instructions or notes for EDR operations..."
rows={3}
/>
</Field>
)}
/>
<Controller
name="termsAccepted"
control={form.control}
render={({ field, fieldState }) => (
<Field data-invalid={fieldState.invalid}>
<label className="flex cursor-pointer items-start gap-3">
<button
type="button"
role="checkbox"
aria-checked={field.value}
aria-invalid={fieldState.invalid}
onClick={() => field.onChange(!field.value)}
className={`mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded border-2 transition ${field.value ? "border-primary bg-primary" : "border-input"
}`}
>
{field.value && (
<Check className="h-3 w-3 text-primary-foreground" />
)}
</button>
<span className="text-sm text-muted-foreground">
I confirm the information is accurate and agree to EDR's{" "}
<span className="text-primary">
freight contract terms and conditions
</span>
.
</span>
</label>
<FieldError errors={[fieldState.error, form.formState.errors.termsAccepted]} />
</Field>
)}
/>
</section>
</div>
{/* ── Submit ── */}
<div className="mt-10 flex items-center gap-3 border-t border-border pt-6">
<Button
type="button"
variant="outline"
onClick={() => setStep((currentStep) => Math.max(1, currentStep - 1))}
disabled={step === 1}
onClick={() => navigate(`/bookings/${id}`)}
>
<ChevronLeft className="mr-1 h-4 w-4" />
Back
Cancel
</Button>
<Button
type="submit"
form="edit-booking-form"
disabled={updateMutation.isPending}
>
{updateMutation.isPending ? (
<LoaderCircle className="h-4 w-4 animate-spin" />
) : (
<Check />
)}
{updateMutation.isPending ? "Saving Changes..." : "Save Changes"}
</Button>
{step < STEPS.length ? (
<Button type="button" onClick={handleContinue}>
Continue
<ChevronRight />
</Button>
) : (
<Button
type="submit"
form="edit-booking-form"
disabled={updateMutation.isPending}
>
{updateMutation.isPending ? (
<LoaderCircle className="h-4 w-4 animate-spin" />
) : (
<Check />
)}
{updateMutation.isPending ? "Saving Changes..." : "Save Changes"}
</Button>
)}
</div>
</div>
</form>