mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-08 04:15:43 +00:00
refactor(bookings): Streamline new booking process, integrate useAuth for customer data, and simplify API payloads
This commit is contained in:
@@ -13,7 +13,6 @@ import {
|
|||||||
Truck,
|
Truck,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
|
||||||
import DeleteBookingDialog from "./DeleteBookingDialog";
|
import DeleteBookingDialog from "./DeleteBookingDialog";
|
||||||
import { getMyBookings } from "@/lib/currentCustomer";
|
import { getMyBookings } from "@/lib/currentCustomer";
|
||||||
import { deleteBooking, type Booking, type BookingStatus } from "./bookings.mock";
|
import { deleteBooking, type Booking, type BookingStatus } from "./bookings.mock";
|
||||||
@@ -183,8 +182,6 @@ export default function MyBookings() {
|
|||||||
return (
|
return (
|
||||||
<div className="min-h-screen p-6">
|
<div className="min-h-screen p-6">
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<Breadcrumbs items={[{ label: "My Bookings" }]} />
|
|
||||||
|
|
||||||
{/* Header Section Card */}
|
{/* Header Section Card */}
|
||||||
<Card className="p-6 flex-row justify-between">
|
<Card className="p-6 flex-row justify-between">
|
||||||
<div>
|
<div>
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { CheckCircle2, ChevronLeft, ChevronRight } from "lucide-react";
|
import { CheckCircle2, ChevronLeft, ChevronRight } from "lucide-react";
|
||||||
import { Button } from "@edr/ui-common";
|
import { Button } from "@edr/ui-common";
|
||||||
import Breadcrumbs from "@/components/Breadcrumbs";
|
import Breadcrumbs from "@/components/Breadcrumbs";
|
||||||
import { getCurrentCustomer } from "@/lib/currentCustomer";
|
|
||||||
import { api } from "@/services/api";
|
import { api } from "@/services/api";
|
||||||
import type { CreateBookingPayload } from "@/services/bookings.service";
|
import type { CreateBookingPayload } from "@/services/bookings.service";
|
||||||
import {
|
import {
|
||||||
@@ -30,6 +29,7 @@ import {
|
|||||||
Step7Documents,
|
Step7Documents,
|
||||||
Step8Review,
|
Step8Review,
|
||||||
} from "./new-booking-form/steps";
|
} from "./new-booking-form/steps";
|
||||||
|
import useAuth from "@/hooks/useAuth";
|
||||||
|
|
||||||
export default function NewBookingPage() {
|
export default function NewBookingPage() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@@ -37,6 +37,7 @@ export default function NewBookingPage() {
|
|||||||
const [step, setStep] = useState(1);
|
const [step, setStep] = useState(1);
|
||||||
const [renewalValidating, setRenewalValidating] = useState(false);
|
const [renewalValidating, setRenewalValidating] = useState(false);
|
||||||
const [renewalValid, setRenewalValid] = useState<boolean | null>(null);
|
const [renewalValid, setRenewalValid] = useState<boolean | null>(null);
|
||||||
|
const { customer } = useAuth();
|
||||||
const createMutation = useMutation({
|
const createMutation = useMutation({
|
||||||
mutationFn: (payload: CreateBookingPayload) =>
|
mutationFn: (payload: CreateBookingPayload) =>
|
||||||
api.bookings.create.call(payload),
|
api.bookings.create.call(payload),
|
||||||
@@ -129,7 +130,6 @@ export default function NewBookingPage() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const me = getCurrentCustomer();
|
|
||||||
const reference = data.previousContractRef;
|
const reference = data.previousContractRef;
|
||||||
|
|
||||||
const totalWeight =
|
const totalWeight =
|
||||||
@@ -142,7 +142,7 @@ export default function NewBookingPage() {
|
|||||||
|
|
||||||
const apiPayload = {
|
const apiPayload = {
|
||||||
reference,
|
reference,
|
||||||
customerId: String(me.id),
|
customerId: customer!.id,
|
||||||
scheduledDate: new Date().toISOString().slice(0, 10),
|
scheduledDate: new Date().toISOString().slice(0, 10),
|
||||||
totalAmount: 0,
|
totalAmount: 0,
|
||||||
contractType:
|
contractType:
|
||||||
@@ -194,7 +194,7 @@ export default function NewBookingPage() {
|
|||||||
createMutation.mutate(apiPayload);
|
createMutation.mutate(apiPayload);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (createMutation.isSuccess && createMutation.data) {
|
if (createMutation.isSuccess) {
|
||||||
return (
|
return (
|
||||||
<div className="flex min-h-screen items-center justify-center p-6">
|
<div className="flex min-h-screen items-center justify-center p-6">
|
||||||
<div className="w-full max-w-sm rounded-2xl border border-border bg-card p-10 text-center shadow-sm">
|
<div className="w-full max-w-sm rounded-2xl border border-border bg-card p-10 text-center shadow-sm">
|
||||||
@@ -207,7 +207,7 @@ export default function NewBookingPage() {
|
|||||||
notified once approved.
|
notified once approved.
|
||||||
</p>
|
</p>
|
||||||
<p className="mt-4 font-mono text-sm font-semibold text-primary">
|
<p className="mt-4 font-mono text-sm font-semibold text-primary">
|
||||||
{createMutation.data.reference}
|
{createMutation.data?.reference}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -161,7 +161,7 @@ export const bookingFormSchema = z
|
|||||||
destinationYard: z.string(),
|
destinationYard: z.string(),
|
||||||
cargoType: z.enum(["container", "bulk"], "Select a cargo type."),
|
cargoType: z.enum(["container", "bulk"], "Select a cargo type."),
|
||||||
cargoWeight: z.string(),
|
cargoWeight: z.string(),
|
||||||
freightType: z.enum(["bulk", "break_bulk"]),
|
freightType: z.enum(["bulk", "break_bulk"]).optional(),
|
||||||
bulkCommodity: z.string(),
|
bulkCommodity: z.string(),
|
||||||
bulkCommodityOther: z.string(),
|
bulkCommodityOther: z.string(),
|
||||||
breakBulkType: z.string(),
|
breakBulkType: z.string(),
|
||||||
|
|||||||
@@ -2,7 +2,13 @@ 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, Switch } from "@edr/ui-common";
|
||||||
import { type BookingFormValues, getRouteDirection, STATIONS } from "./schema";
|
import { type BookingFormValues, getRouteDirection, STATIONS } from "./schema";
|
||||||
import { SelectField, SelectOptions, StepHeader, StepLabel } from "./shared";
|
import {
|
||||||
|
AlertBox,
|
||||||
|
SelectField,
|
||||||
|
SelectOptions,
|
||||||
|
StepHeader,
|
||||||
|
StepLabel,
|
||||||
|
} from "./shared";
|
||||||
import { useDropdownSettingByCode } from "@/hooks/useDropdownSettings";
|
import { useDropdownSettingByCode } from "@/hooks/useDropdownSettings";
|
||||||
import { DropdownOption } from "@/types/dropdownSettings";
|
import { DropdownOption } from "@/types/dropdownSettings";
|
||||||
|
|
||||||
@@ -145,12 +151,10 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function getStationOptions(options?: DropdownOption[]): DropdownOption[] {
|
function getStationOptions(options?: DropdownOption[]): DropdownOption[] {
|
||||||
return [...(options ?? [])].sort((a, b) => a.order - b.order);
|
return [...(options ?? [])].sort((a, b) => a.order - b.order);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function StationSelectOptions({
|
function StationSelectOptions({
|
||||||
options,
|
options,
|
||||||
excludeValue,
|
excludeValue,
|
||||||
@@ -193,4 +197,4 @@ function StationSelectOptions({
|
|||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ export function Step5CargoDetails({
|
|||||||
selected={cargoType === "container"}
|
selected={cargoType === "container"}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
field.onChange("container");
|
field.onChange("container");
|
||||||
form.setValue("freightType", "", { shouldDirty: true });
|
form.setValue("freightType", undefined, {
|
||||||
|
shouldDirty: true,
|
||||||
|
});
|
||||||
form.setValue("cargoWeight", "", { shouldDirty: true });
|
form.setValue("cargoWeight", "", { shouldDirty: true });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,31 +1,23 @@
|
|||||||
import type { Freight, PaginatedResponse } from "@edr/types";
|
import type { Freight, PaginatedResponse } from "@edr/types";
|
||||||
import { client as api } from "@/utils/api";
|
|
||||||
|
import { client } from "../utils/api";
|
||||||
|
|
||||||
export type CreateBookingPayload = Freight.CreateBookingDto;
|
export type CreateBookingPayload = Freight.CreateBookingDto;
|
||||||
|
|
||||||
export const bookingsService = {
|
export const bookingsService = {
|
||||||
list: async (): Promise<PaginatedResponse<Freight.IBooking>> => {
|
list: async (): Promise<PaginatedResponse<Freight.IBooking>> => {
|
||||||
const { data } = await api.get("/bookings");
|
const { data } = await client.get("/bookings");
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
get: async (id: string): Promise<Freight.IBooking> => {
|
get: async (id: string): Promise<Freight.IBooking> => {
|
||||||
const { data } = await api.get(`/bookings/${id}`);
|
const { data } = await client.get(`/bookings/${id}`);
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
create: async (payload: CreateBookingPayload): Promise<Freight.IBooking> => {
|
create: async (payload: CreateBookingPayload): Promise<Freight.IBooking> => {
|
||||||
const fd = new FormData();
|
const { data } = await client.post("/api/bookings", payload);
|
||||||
for (const [key, value] of Object.entries(payload)) {
|
|
||||||
if (value === undefined || value === null) continue;
|
|
||||||
if (Array.isArray(value) || typeof value === "object") {
|
|
||||||
fd.append(key, JSON.stringify(value));
|
|
||||||
} else {
|
|
||||||
fd.append(key, String(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const { data } = await api.post("/api/bookings", fd);
|
|
||||||
return data.data;
|
return data.data;
|
||||||
},
|
},
|
||||||
remove: async (id: string): Promise<void> => {
|
remove: async (id: string): Promise<void> => {
|
||||||
await api.delete(`/bookings/${id}`);
|
await client.delete(`/bookings/${id}`);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user