type fixes

This commit is contained in:
ghost2023
2026-06-16 15:13:11 +03:00
parent e0e6020020
commit cf3db76bb1
14 changed files with 284 additions and 387 deletions

View File

@@ -1,6 +1,6 @@
import { Fragment } from "react";
import { Link } from "react-router-dom";
import { ChevronRight, Home } from "lucide-react";
import { ChevronRight } from "lucide-react";
export interface BreadcrumbItem {
label: string;
@@ -34,10 +34,7 @@ export default function Breadcrumbs({ items }: BreadcrumbsProps) {
<ChevronRight className="mx-2 h-4 w-4 text-slate-300" />
{item.href && !isLast ? (
<Link
to={item.href}
className="transition hover:text-[#10B981]"
>
<Link to={item.href} className="transition hover:text-[#10B981]">
{item.label}
</Link>
) : (

View File

@@ -1,62 +0,0 @@
import { FormEvent, useState } from "react";
import { Button, FormField } from "@edr/ui-common";
import type { CreateBookingPayload } from "../../services/bookings.service";
export interface BookingFormProps {
onSubmit: (payload: CreateBookingPayload) => void;
isSubmitting?: boolean;
}
const BookingForm = ({ onSubmit, isSubmitting }: BookingFormProps) => {
const [reference, setReference] = useState("");
const [customerId, setCustomerId] = useState("");
const [scheduledDate, setScheduledDate] = useState("");
const [totalAmount, setTotalAmount] = useState("0");
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit({
reference,
customerId,
scheduledDate,
totalAmount: Number(totalAmount),
});
};
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-3">
<FormField
label="Reference"
value={reference}
onChange={(e) => setReference(e.target.value)}
required
/>
<FormField
label="Customer ID"
value={customerId}
onChange={(e) => setCustomerId(e.target.value)}
required
/>
<FormField
label="Scheduled date"
type="date"
value={scheduledDate}
onChange={(e) => setScheduledDate(e.target.value)}
required
/>
<FormField
label="Total amount"
type="number"
value={totalAmount}
onChange={(e) => setTotalAmount(e.target.value)}
min="0"
/>
<Button type="submit" isLoading={isSubmitting}>
Create booking
</Button>
</form>
);
};
export default BookingForm;

View File

@@ -1,81 +0,0 @@
import { FormEvent, useState } from "react";
import { Button, FormField } from "@edr/ui-common";
export interface ConsignmentFormProps {
onSubmit: (payload: {
bookingId: string;
trackingNumber: string;
cargoType: string;
weightKg: number;
originStation: string;
destinationStation: string;
}) => void;
isSubmitting?: boolean;
}
const ConsignmentForm = ({ onSubmit, isSubmitting }: ConsignmentFormProps) => {
const [bookingId, setBookingId] = useState("");
const [trackingNumber, setTrackingNumber] = useState("");
const [cargoType, setCargoType] = useState("GENERAL");
const [weightKg, setWeightKg] = useState("0");
const [originStation, setOriginStation] = useState("");
const [destinationStation, setDestinationStation] = useState("");
const handleSubmit = (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
onSubmit({
bookingId,
trackingNumber,
cargoType,
weightKg: Number(weightKg),
originStation,
destinationStation,
});
};
return (
<form onSubmit={handleSubmit} className="flex flex-col gap-3">
<FormField
label="Booking ID"
value={bookingId}
onChange={(e) => setBookingId(e.target.value)}
required
/>
<FormField
label="Tracking #"
value={trackingNumber}
onChange={(e) => setTrackingNumber(e.target.value)}
required
/>
<FormField
label="Cargo type"
value={cargoType}
onChange={(e) => setCargoType(e.target.value)}
/>
<FormField
label="Weight (kg)"
type="number"
value={weightKg}
onChange={(e) => setWeightKg(e.target.value)}
min="0"
/>
<FormField
label="Origin station"
value={originStation}
onChange={(e) => setOriginStation(e.target.value)}
required
/>
<FormField
label="Destination station"
value={destinationStation}
onChange={(e) => setDestinationStation(e.target.value)}
required
/>
<Button type="submit" isLoading={isSubmitting}>
Create consignment
</Button>
</form>
);
};
export default ConsignmentForm;

View File

@@ -1,30 +0,0 @@
import type { Freight } from "@edr/types";
import { Table, type TableColumn } from "@edr/ui-common";
export interface ConsignmentTableProps {
consignments: Freight.IConsignment[];
}
const columns: TableColumn<Freight.IConsignment>[] = [
{ key: "trackingNumber", header: "Tracking #" },
{ key: "cargoType", header: "Cargo" },
{ key: "status", header: "Status" },
{ key: "originStation", header: "Origin" },
{ key: "destinationStation", header: "Destination" },
{
key: "weightKg",
header: "Weight (kg)",
render: (row) => row.weightKg.toFixed(2),
},
];
const ConsignmentTable = ({ consignments }: ConsignmentTableProps) => (
<Table
columns={columns}
data={consignments}
rowKey={(row) => row.id}
emptyMessage="No consignments yet"
/>
);
export default ConsignmentTable;

View File

@@ -2,7 +2,6 @@ import { useQuery } from "@tanstack/react-query";
import { cn } from "@/lib/utils";
import { api } from "@/services/api";
import { Loader2, AlertCircle } from "lucide-react";
import * as React from "react";
import {
Select,
@@ -64,11 +63,7 @@ export function DynamicSelect({
const options = [...data.children].sort((a, b) => a.order - b.order);
return (
<Select
value={value}
onValueChange={onValueChange}
disabled={disabled}
>
<Select value={value} onValueChange={onValueChange} disabled={disabled}>
<SelectTrigger className={cn("w-full", className)}>
<SelectValue placeholder={placeholder ?? `Select ${data.label}`} />
</SelectTrigger>

View File

@@ -17,7 +17,7 @@ const TrackingTimeline = ({ events }: TrackingTimelineProps) => {
<div className="flex flex-col gap-1">
<div className="flex items-center gap-2 text-sm font-medium text-gray-900">
<span>{event.location}</span>
<Badge tone="info">{event.status}</Badge>
<Badge>{event.status}</Badge>
</div>
<time className="text-xs text-gray-500">
{new Date(event.occurredAt).toLocaleString()}

View File

@@ -1,8 +1,7 @@
export * from './table';
export * from './badge';
export * from './button';
export * from './dialog';
export * from './input';
export * from './label';
export * from './textarea';
export * from './Breadcrumbs';
export * from "./table";
export * from "./badge";
export * from "./button";
export * from "./dialog";
export * from "./input";
export * from "./label";
export * from "./textarea";

View File

@@ -1,16 +0,0 @@
import { useQuery } from "@tanstack/react-query";
import { bookingsService } from "../services/bookings.service";
export const useBookings = () =>
useQuery({
queryKey: ["bookings"],
queryFn: bookingsService.list,
});
export const useBooking = (id: string) =>
useQuery({
queryKey: ["bookings", id],
queryFn: () => bookingsService.get(id),
enabled: Boolean(id),
});

View File

@@ -1,16 +0,0 @@
import { useQuery } from "@tanstack/react-query";
import { consignmentsService } from "../services/consignments.service";
export const useConsignments = () =>
useQuery({
queryKey: ["consignments"],
queryFn: consignmentsService.list,
});
export const useConsignment = (id: string) =>
useQuery({
queryKey: ["consignments", id],
queryFn: () => consignmentsService.get(id),
enabled: Boolean(id),
});

View File

@@ -1,50 +0,0 @@
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { customersService } from "@/services/customers.service";
import type {
CreateCustomerDto,
UpdateCustomerDto,
} from "@/types/customers";
const KEY = ["customers"] as const;
export const useCustomers = () =>
useQuery({
queryKey: KEY,
queryFn: customersService.list,
});
export const useCustomer = (id: string | undefined) =>
useQuery({
queryKey: [...KEY, "id", id],
queryFn: () => customersService.getById(id!),
enabled: Boolean(id),
});
export const useCreateCustomer = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (dto: CreateCustomerDto) => customersService.create(dto),
onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
});
};
export const useUpdateCustomer = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: ({ id, dto }: { id: string; dto: UpdateCustomerDto }) =>
customersService.update(id, dto),
onSuccess: (_data, { id }) => {
qc.invalidateQueries({ queryKey: KEY });
qc.invalidateQueries({ queryKey: [...KEY, "id", id] });
},
});
};
export const useDeleteCustomer = () => {
const qc = useQueryClient();
return useMutation({
mutationFn: (id: string) => customersService.remove(id),
onSuccess: () => qc.invalidateQueries({ queryKey: KEY }),
});
};

View File

@@ -1,4 +1,15 @@
import { Box, Button, Divider, Group, Loader, SimpleGrid, Stack, Text, TextInput, ThemeIcon } from "@mantine/core";
import {
Box,
Button,
Divider,
Group,
Loader,
SimpleGrid,
Stack,
Text,
TextInput,
ThemeIcon,
} from "@mantine/core";
import { zodResolver } from "@hookform/resolvers/zod";
import { useQuery } from "@tanstack/react-query";
import {
@@ -8,7 +19,6 @@ import {
CheckCircle2,
ChevronLeft,
FileText,
Loader2,
UploadCloud,
User,
} from "lucide-react";
@@ -32,7 +42,10 @@ const onboardingSchema = z.object({
companyLocation: z.string().min(1, "Location is required"),
companyAddress: z.string().min(1, "Address is required"),
tinNumber: z.string().length(10, "TIN must be exactly 10 digits"),
vatNumber: z.string().min(1, "VAT number is required").length(10, "VAT number must be exactly 10 digits"),
vatNumber: z
.string()
.min(1, "VAT number is required")
.length(10, "VAT number must be exactly 10 digits"),
fanNumber: z.string().length(16, "FAN must be exactly 16 digits"),
contactPersonName: z.string().min(1, "Contact person name is required"),
contactPersonPhone: z.string().min(1, "Contact person phone is required"),
@@ -52,8 +65,26 @@ const onboardingSchema = z.object({
type FormData = z.infer<typeof onboardingSchema>;
const stepFields: Record<CompanyStep, (keyof FormData)[]> = {
company: ["companyName", "companyEmail", "companyPhone", "companyPhoneCountryCode", "companyLocation", "companyAddress", "tinNumber", "vatNumber", "fanNumber"],
personnel: ["contactPersonName", "contactPersonPhone", "contactPersonPhoneCountryCode", "generalManagerName", "generalManagerEmail", "generalManagerPhone", "generalManagerPhoneCountryCode"],
company: [
"companyName",
"companyEmail",
"companyPhone",
"companyPhoneCountryCode",
"companyLocation",
"companyAddress",
"tinNumber",
"vatNumber",
"fanNumber",
],
personnel: [
"contactPersonName",
"contactPersonPhone",
"contactPersonPhoneCountryCode",
"generalManagerName",
"generalManagerEmail",
"generalManagerPhone",
"generalManagerPhoneCountryCode",
],
poa: [],
documents: [],
confirm: [],
@@ -76,7 +107,10 @@ function buildPayload(data: FormData, _user: AuthUser): CreateCompanyPayload {
generalManagerEmail: data.generalManagerEmail,
generalManagerPhone: `${data.generalManagerPhoneCountryCode}${data.generalManagerPhone}`,
poaName: data.poaName || undefined,
poaPhone: data.poaPhone && data.poaPhoneCountryCode ? `${data.poaPhoneCountryCode}${data.poaPhone}` : undefined,
poaPhone:
data.poaPhone && data.poaPhoneCountryCode
? `${data.poaPhoneCountryCode}${data.poaPhone}`
: undefined,
poaAddress: data.poaAddress || undefined,
poaEmail: data.poaEmail || undefined,
poaLocation: data.poaLocation || undefined,
@@ -102,22 +136,50 @@ export default function CompanyProfileForm({
onBack: () => void;
}) {
const [step, setStep] = useState<CompanyStep>("company");
const [internalFiles, setInternalFiles] = useState<Record<string, File | File[] | null>>({});
const [internalFiles, setInternalFiles] = useState<
Record<string, File | File[] | null>
>({});
const documentFiles = controlledFiles ?? internalFiles;
const setDocumentFiles = onDocumentFilesChange ?? setInternalFiles;
const { data: uploadSetting, isLoading: loadingDocuments } = useQuery(
api.fileUploadSettings.getByCode.queryOptions({ input: { code: documentSettingCode }, refetchOnMount: false }),
api.fileUploadSettings.getByCode.queryOptions({
input: { code: documentSettingCode },
refetchOnMount: false,
}),
);
const { register, handleSubmit, trigger, watch, formState: { errors } } = useForm<FormData>({
const {
register,
handleSubmit,
trigger,
watch,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(onboardingSchema),
defaultValues: {
companyName: "", companyEmail: "", companyPhone: "", companyPhoneCountryCode: "+251",
companyLocation: "", companyAddress: "", tinNumber: "", vatNumber: "", fanNumber: "",
contactPersonName: "", contactPersonPhone: "", contactPersonPhoneCountryCode: "+251",
generalManagerName: "", generalManagerEmail: "", generalManagerPhone: "", generalManagerPhoneCountryCode: "+251",
poaName: "", poaPhone: "", poaPhoneCountryCode: "+251", poaAddress: "", poaEmail: "", poaLocation: "",
companyName: "",
companyEmail: "",
companyPhone: "",
companyPhoneCountryCode: "+251",
companyLocation: "",
companyAddress: "",
tinNumber: "",
vatNumber: "",
fanNumber: "",
contactPersonName: "",
contactPersonPhone: "",
contactPersonPhoneCountryCode: "+251",
generalManagerName: "",
generalManagerEmail: "",
generalManagerPhone: "",
generalManagerPhoneCountryCode: "+251",
poaName: "",
poaPhone: "",
poaPhoneCountryCode: "+251",
poaAddress: "",
poaEmail: "",
poaLocation: "",
},
});
@@ -126,9 +188,18 @@ export default function CompanyProfileForm({
const totalSteps = 5;
const nextStep = async () => {
if (step === "poa") { setStep("documents"); return; }
if (step === "documents") { setStep("confirm"); return; }
if (step === "confirm") { handleSubmit((data) => onSubmit(buildPayload(data, user)))(); return; }
if (step === "poa") {
setStep("documents");
return;
}
if (step === "documents") {
setStep("confirm");
return;
}
if (step === "confirm") {
handleSubmit((data) => onSubmit(buildPayload(data, user)))();
return;
}
const isValid = await trigger(stepFields[step]);
if (!isValid) return;
setStep(step === "company" ? "personnel" : "poa");
@@ -158,7 +229,13 @@ export default function CompanyProfileForm({
confirm: `Step 5 of ${totalSteps} — Review & Confirm`,
};
const stepOrder: CompanyStep[] = ["company", "personnel", "poa", "documents", "confirm"];
const stepOrder: CompanyStep[] = [
"company",
"personnel",
"poa",
"documents",
"confirm",
];
const currentIdx = stepOrder.indexOf(step);
return (
@@ -175,13 +252,24 @@ export default function CompanyProfileForm({
Change account type
</Button>
<Group justify="space-between" align="center" className="relative max-w-lg mx-auto px-2">
<Group
justify="space-between"
align="center"
className="relative max-w-lg mx-auto px-2"
>
<Box className="absolute top-1/2 left-2 right-2 h-0.5 bg-edr-border -translate-y-1/2 z-0" />
{STEPS.map(({ key, icon }, i) => {
const done = i < currentIdx;
const active = i === currentIdx;
return done || active ? (
<ThemeIcon key={key} size={40} radius="xl" variant="filled" color="edr-green" className="relative z-10">
<ThemeIcon
key={key}
size={40}
radius="xl"
variant="filled"
color="edr-green"
className="relative z-10"
>
{done ? <CheckCircle2 size={18} /> : icon}
</ThemeIcon>
) : (
@@ -223,7 +311,10 @@ export default function CompanyProfileForm({
/>
<PhoneInput
countryCode={{ ...register("companyPhoneCountryCode") }}
phone={{ ...register("companyPhone"), placeholder: "912345678" }}
phone={{
...register("companyPhone"),
placeholder: "912345678",
}}
countryCodeError={errors.companyPhoneCountryCode}
phoneError={errors.companyPhone}
label="Company Phone"
@@ -271,7 +362,9 @@ export default function CompanyProfileForm({
{step === "personnel" && (
<>
<Text fw={600} size="sm" c="edr-text">Contact Person</Text>
<Text fw={600} size="sm" c="edr-text">
Contact Person
</Text>
<SimpleGrid cols={2} spacing="md">
<TextInput
label="Name"
@@ -281,7 +374,10 @@ export default function CompanyProfileForm({
/>
<PhoneInput
countryCode={{ ...register("contactPersonPhoneCountryCode") }}
phone={{ ...register("contactPersonPhone"), placeholder: "912345678" }}
phone={{
...register("contactPersonPhone"),
placeholder: "912345678",
}}
countryCodeError={errors.contactPersonPhoneCountryCode}
phoneError={errors.contactPersonPhone}
label="Phone"
@@ -290,7 +386,9 @@ export default function CompanyProfileForm({
<Divider color="edr-border" />
<Text fw={600} size="sm" c="edr-text">General Manager</Text>
<Text fw={600} size="sm" c="edr-text">
General Manager
</Text>
<TextInput
label="Name"
placeholder="Abebe Bikila"
@@ -306,8 +404,13 @@ export default function CompanyProfileForm({
{...register("generalManagerEmail")}
/>
<PhoneInput
countryCode={{ ...register("generalManagerPhoneCountryCode") }}
phone={{ ...register("generalManagerPhone"), placeholder: "912345678" }}
countryCode={{
...register("generalManagerPhoneCountryCode"),
}}
phone={{
...register("generalManagerPhone"),
placeholder: "912345678",
}}
countryCodeError={errors.generalManagerPhoneCountryCode}
phoneError={errors.generalManagerPhone}
label="Phone"
@@ -319,7 +422,8 @@ export default function CompanyProfileForm({
{step === "poa" && (
<>
<Text size="sm" c="edr-muted">
Power of Attorney details are optional. Fill them in if you have them, or skip to continue.
Power of Attorney details are optional. Fill them in if you have
them, or skip to continue.
</Text>
<TextInput
label="PoA Name"
@@ -371,51 +475,126 @@ export default function CompanyProfileForm({
No document requirements found for your account type.
</Text>
) : (
<SmartFileInput file={uploadSetting} value={documentFiles} onChange={setDocumentFiles} />
<SmartFileInput
file={uploadSetting}
value={documentFiles}
onChange={setDocumentFiles}
/>
)}
</>
)}
{step === "confirm" && (
<Box p={16} className="rounded-2xl border border-edr-border bg-edr-card">
<Text fw={600} c="edr-text">Review your registration</Text>
<Box
p={16}
className="rounded-2xl border border-edr-border bg-edr-card"
>
<Text fw={600} c="edr-text">
Review your registration
</Text>
<Text size="sm" c="edr-muted" mt={4} mb="md">
Confirm the company details below before saving.
</Text>
<SimpleGrid cols={2} spacing="sm">
<ReviewRow label="Company name" value={formValues.companyName} />
<ReviewRow label="Company email" value={formValues.companyEmail} />
<ReviewRow label="Company phone" value={formValues.companyPhone} />
<ReviewRow label="Location" value={formValues.companyLocation} />
<ReviewRow
label="Company name"
value={formValues.companyName}
/>
<ReviewRow
label="Company email"
value={formValues.companyEmail}
/>
<ReviewRow
label="Company phone"
value={formValues.companyPhone}
/>
<ReviewRow
label="Location"
value={formValues.companyLocation}
/>
<ReviewRow label="Address" value={formValues.companyAddress} />
<ReviewRow label="TIN" value={formValues.tinNumber} />
<ReviewRow label="VAT" value={formValues.vatNumber} />
<ReviewRow label="FAN" value={formValues.fanNumber} />
<ReviewRow label="Contact person" value={formValues.contactPersonName} />
<ReviewRow label="Contact phone" value={`${formValues.contactPersonPhoneCountryCode}${formValues.contactPersonPhone}`} />
<ReviewRow label="General manager" value={formValues.generalManagerName} />
<ReviewRow label="GM email" value={formValues.generalManagerEmail} />
<ReviewRow label="GM phone" value={`${formValues.generalManagerPhoneCountryCode}${formValues.generalManagerPhone}`} />
<ReviewRow label="PoA name" value={formValues.poaName || undefined} />
<ReviewRow label="PoA phone" value={formValues.poaPhone && formValues.poaPhoneCountryCode ? `${formValues.poaPhoneCountryCode}${formValues.poaPhone}` : undefined} />
<ReviewRow label="PoA email" value={formValues.poaEmail || undefined} />
<ReviewRow label="PoA location" value={formValues.poaLocation || undefined} />
<ReviewRow
label="Contact person"
value={formValues.contactPersonName}
/>
<ReviewRow
label="Contact phone"
value={`${formValues.contactPersonPhoneCountryCode}${formValues.contactPersonPhone}`}
/>
<ReviewRow
label="General manager"
value={formValues.generalManagerName}
/>
<ReviewRow
label="GM email"
value={formValues.generalManagerEmail}
/>
<ReviewRow
label="GM phone"
value={`${formValues.generalManagerPhoneCountryCode}${formValues.generalManagerPhone}`}
/>
<ReviewRow
label="PoA name"
value={formValues.poaName || undefined}
/>
<ReviewRow
label="PoA phone"
value={
formValues.poaPhone && formValues.poaPhoneCountryCode
? `${formValues.poaPhoneCountryCode}${formValues.poaPhone}`
: undefined
}
/>
<ReviewRow
label="PoA email"
value={formValues.poaEmail || undefined}
/>
<ReviewRow
label="PoA location"
value={formValues.poaLocation || undefined}
/>
</SimpleGrid>
</Box>
)}
<Group justify="space-between" pt="xs">
<Button variant="default" onClick={prevStep} leftSection={<ArrowLeft size={16} />}>
{step === "company" ? "Change Type" : step === "confirm" ? "Back to Documents" : "Back"}
<Button
variant="default"
onClick={prevStep}
leftSection={<ArrowLeft size={16} />}
>
{step === "company"
? "Change Type"
: step === "confirm"
? "Back to Documents"
: "Back"}
</Button>
<Button
color="edr-green"
onClick={step === "confirm" ? handleSubmit((data) => onSubmit(buildPayload(data, user))) : nextStep}
disabled={isPending || (step === "documents" && !hasDocuments && loadingDocuments)}
onClick={
step === "confirm"
? handleSubmit((data) => onSubmit(buildPayload(data, user)))
: nextStep
}
disabled={
isPending ||
(step === "documents" && !hasDocuments && loadingDocuments)
}
loading={isPending}
rightSection={!isPending && step !== "confirm" && step !== "documents" ? <ArrowRight size={16} /> : undefined}
rightSection={
!isPending && step !== "confirm" && step !== "documents" ? (
<ArrowRight size={16} />
) : undefined
}
>
{step === "documents" ? "Continue" : step === "confirm" ? "Submit Registration" : "Next Step"}
{step === "documents"
? "Continue"
: step === "confirm"
? "Submit Registration"
: "Next Step"}
</Button>
</Group>
</Stack>
@@ -427,10 +606,20 @@ export default function CompanyProfileForm({
function ReviewRow({ label, value }: { label: string; value?: string | null }) {
return (
<Box p={12} className="rounded-xl bg-edr-bg">
<Text size="xs" fw={600} c="edr-muted" className="uppercase tracking-wide">
<Text
size="xs"
fw={600}
c="edr-muted"
className="uppercase tracking-wide"
>
{label}
</Text>
<Text size="sm" fw={500} c={value?.trim() ? "edr-text" : "edr-muted"} mt={4}>
<Text
size="sm"
fw={500}
c={value?.trim() ? "edr-text" : "edr-muted"}
mt={4}
>
{value?.trim() ? value : "Not provided"}
</Text>
</Box>

View File

@@ -1,30 +0,0 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useNavigate } from "react-router-dom";
import BookingForm from "../../components/bookings/BookingForm";
import { bookingsService } from "../../services/bookings.service";
const CreateBookingPage = () => {
const navigate = useNavigate();
const queryClient = useQueryClient();
const mutation = useMutation({
mutationFn: bookingsService.create,
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["bookings"] });
navigate("/bookings");
},
});
return (
<div className="max-w-lg">
<h1 className="mb-4 text-2xl font-semibold text-gray-900">New booking</h1>
<BookingForm
onSubmit={mutation.mutate}
isSubmitting={mutation.isPending}
/>
</div>
);
};
export default CreateBookingPage;

View File

@@ -10,7 +10,11 @@ import {
} from "./schema";
import { SelectField, StepHeader, StepLabel } from "./shared";
type BookingForm = UseFormReturn<BookingFormInputValues, any, BookingFormValues>;
type BookingForm = UseFormReturn<
BookingFormInputValues,
any,
BookingFormValues
>;
export function Step4Route({
form,
@@ -31,44 +35,43 @@ export function Step4Route({
const shippingLineOptions = useMemo(() => {
if (!referenceData?.shipping_line) return [];
return referenceData.shipping_line.map((sl) => ({ value: sl.name, label: sl.name }));
return referenceData.shipping_line.map((sl) => ({
value: sl.name,
label: sl.name,
}));
}, [referenceData]);
const originData = useMemo(
() => {
return yardOptions.filter((o) => o.value !== destinationYard).filter((o) => {
const dest = referenceData?.yard.find((y) => y.id === destinationYard);
if(!dest) return true;
const originData = useMemo(() => {
return yardOptions
.filter((o) => o.value !== destinationYard)
.filter((o) => {
const dest = referenceData?.yard.find((y) => y.id === destinationYard);
if (!dest) return true;
const origin = referenceData?.yard.find((y) => y.id === o.value);
// can't go from Djibouti to Djibouti
if(dest?.country === 'Djibouti' && origin?.country == 'Djibouti') return false;
return true;
});
},
[yardOptions, destinationYard],
);
console.log({yardOptions,originYard, destinationYard})
const destData = useMemo(
() => {
return yardOptions.filter((o) => o.value !== originYard).filter((d) => {
const origin = referenceData?.yard.find((y) => y.id === originYard);
if(!origin) return true;
const dest = referenceData?.yard.find((y) => y.id === d.value);
// can't go from Djibouti to Djibouti
// if(origin.country === 'Djibouti' && dest?.country == 'Djibouti') return false;
if (dest?.country === "Djibouti" && origin?.country == "Djibouti")
return false;
return true;
});
},
[yardOptions, originYard],
);
}, [yardOptions, destinationYard]);
console.log({ yardOptions, originYard, destinationYard });
const destData = useMemo(() => {
return yardOptions
.filter((o) => o.value !== originYard)
.filter((d) => {
const origin = referenceData?.yard.find((y) => y.id === originYard);
if (!origin) return true;
const direction = getRouteDirection(referenceData?.yard.find((y) => y.id === originYard), referenceData?.yard.find((y) => y.name === destinationYard));
return true;
});
}, [yardOptions, originYard]);
const direction = getRouteDirection(
referenceData?.yard.find((y) => y.id === originYard),
referenceData?.yard.find((y) => y.name === destinationYard),
);
const directionStyle: Record<string, string> = {
export: "bg-sky-50 text-sky-800 border-sky-200",

View File

@@ -8,7 +8,6 @@ import {
List,
MapPin,
MoreHorizontal,
Plus,
Search,
Train,
Trash2,