From 1b8391186d0af468e0deb1a9d9e756b5182af1b7 Mon Sep 17 00:00:00 2001 From: yaschalew Date: Sat, 30 May 2026 09:47:26 +0300 Subject: [PATCH] customer registration --- apps/edr-freight-web/portal/src/App.tsx | 4 + .../on_boarding/CustomerOnboardingPage.tsx | 527 ++++++++++++ .../DjiboutiFreightForwardingAgent.tsx | 558 +++++++++++++ .../on_boarding/ImportExportOnBoarding.tsx | 771 ++++++++++++++++++ .../on_boarding/TransportrOnBoarding.tsx | 287 +++++++ 5 files changed, 2147 insertions(+) create mode 100644 apps/edr-freight-web/portal/src/pages/customers/on_boarding/CustomerOnboardingPage.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/customers/on_boarding/DjiboutiFreightForwardingAgent.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/customers/on_boarding/ImportExportOnBoarding.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/customers/on_boarding/TransportrOnBoarding.tsx diff --git a/apps/edr-freight-web/portal/src/App.tsx b/apps/edr-freight-web/portal/src/App.tsx index 139e74280..17dd94533 100644 --- a/apps/edr-freight-web/portal/src/App.tsx +++ b/apps/edr-freight-web/portal/src/App.tsx @@ -32,6 +32,8 @@ import NewBookingPage from "./pages/bookings/NewBookingPage"; import TrackingPage from "./pages/tracking/TrackingPage"; import BillingPage from "./pages/billing/BillingPage"; import { useEffect } from "react"; +import CustomerOnBoarding from "./pages/customers/on_boarding/TransportrOnBoarding"; +import CustomerOnboardingPage from "./pages/customers/on_boarding/CustomerOnboardingPage"; const sidebarItems: SidebarItem[] = [ { label: "Home", href: "/", icon: }, @@ -77,6 +79,8 @@ const App = () => { return ; } + return + const displayName = user?.name?.en || user?.username || user?.email || "User"; const userEmail = user?.email; diff --git a/apps/edr-freight-web/portal/src/pages/customers/on_boarding/CustomerOnboardingPage.tsx b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/CustomerOnboardingPage.tsx new file mode 100644 index 000000000..994dd751e --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/CustomerOnboardingPage.tsx @@ -0,0 +1,527 @@ +import { useState } from "react"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; +import { + ArrowRight, + ArrowLeft, + Building2, + User, + FileText, + CheckCircle2, + Loader2, +} from "lucide-react"; +import useAuth from "@/hooks/useAuth"; +import { api } from "@/services/api"; +import type { CreateCustomerDto } from "@/types/customers"; +import AuthLayout from "@/components/auth/AuthLayout"; +import PhoneInput from "@/components/auth/PhoneInput"; +import { + Button, + Input, + Field, + FieldLabel, + FieldError, + FieldGroup, +} from "@edr/ui-common"; +import TransporterOnboarding from "./TransportrOnBoarding"; +import DjiboutiForwardingAgentForm from "./DjiboutiFreightForwardingAgent"; +import ImportExportOnBoarding from "./ImportExportOnBoarding"; + +type OnboardingStep = "company" | "personnel" | "poa"; + +const onboardingSchema = z.object({ + companyName: z.string().min(1, "Company name is required"), + companyEmail: z.string().email("Invalid email address"), + companyPhone: z.string().min(1, "Company phone is required"), + companyPhoneCountryCode: z.string().min(1, "Country code is required"), + 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"), + 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"), + contactPersonPhoneCountryCode: z.string().min(1, "Country code is required"), + generalManagerName: z.string().min(1, "GM name is required"), + generalManagerEmail: z.string().email("Invalid GM email"), + generalManagerPhone: z.string().min(1, "GM phone is required"), + generalManagerPhoneCountryCode: z.string().min(1, "Country code is required"), + poaName: z.string().optional(), + poaPhone: z.string().optional(), + poaPhoneCountryCode: z.string().optional(), + poaAddress: z.string().optional(), + poaEmail: z.string().optional(), + poaLocation: z.string().optional(), +}); + +type FormData = z.infer; + +const stepFields: Record = { + company: [ + "companyName", + "companyEmail", + "companyPhone", + "companyPhoneCountryCode", + "companyLocation", + "companyAddress", + "tinNumber", + "vatNumber", + "fanNumber", + ], + personnel: [ + "contactPersonName", + "contactPersonPhone", + "contactPersonPhoneCountryCode", + "generalManagerName", + "generalManagerEmail", + "generalManagerPhone", + "generalManagerPhoneCountryCode", + ], + poa: [], +}; + +export default function CustomerOnboardingPage() { + const queryClient = useQueryClient(); + const { user } = useAuth(); + const [step, setStep] = useState("company"); + + const { + register, + handleSubmit, + trigger, + formState: { errors }, + } = useForm({ + 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: "", + }, + }); + + const createCustomerMutation = useMutation({ + mutationFn: (payload: CreateCustomerDto) => + api.customers.create.call(payload), + onSuccess: () => { + if (user) + queryClient.invalidateQueries({ + queryKey: api.customers.getByUserId.queryKey({ id: user.id }), + }); + }, + }); + + const nextStep = async () => { + if (step === "poa") { + handleSubmit(onSubmit)(); + return; + } + const fields = stepFields[step]; + const isValid = await trigger(fields); + if (!isValid) return; + setStep(step === "company" ? "personnel" : "poa"); + }; + + const prevStep = () => { + if (step === "personnel") setStep("company"); + else if (step === "poa") setStep("personnel"); + }; + + const onSubmit = async (data: FormData) => { + const nameParts = (user?.name?.en ?? "").split(" "); + const payload: CreateCustomerDto = { + userId: user!.id, + firstName: nameParts[0] || "", + lastName: nameParts.slice(-1)[0] || "", + email: user!.email, + phone: user!.phoneNumber, + companyName: data.companyName, + companyEmail: data.companyEmail, + companyPhone: `${data.companyPhoneCountryCode}${data.companyPhone}`, + companyLocation: data.companyLocation, + companyAddress: data.companyAddress, + contactPersonName: data.contactPersonName, + contactPersonPhone: `${data.contactPersonPhoneCountryCode}${data.contactPersonPhone}`, + tinNumber: data.tinNumber, + vatNumber: data.vatNumber, + fanNumber: data.fanNumber, + generalManagerName: data.generalManagerName, + generalManagerEmail: data.generalManagerEmail, + generalManagerPhone: `${data.generalManagerPhoneCountryCode}${data.generalManagerPhone}`, + poaName: data.poaName || undefined, + poaPhone: + data.poaPhone && data.poaPhoneCountryCode + ? `${data.poaPhoneCountryCode}${data.poaPhone}` + : undefined, + poaAddress: data.poaAddress || undefined, + poaEmail: data.poaEmail || undefined, + poaLocation: data.poaLocation || undefined, + }; + createCustomerMutation.mutate(payload); + }; + + return ( + + + + {/* */} + {/* */} + {/*
+
+
+ } + active={step === "company"} + completed={step !== "company"} + /> + } + active={step === "personnel"} + completed={step === "poa"} + /> + } + active={step === "poa"} + completed={false} + /> +
+

+ {step === "company" && "Step 1 of 3 — Company Information"} + {step === "personnel" && "Step 2 of 3 — Personnel Details"} + {step === "poa" && "Step 3 of 3 — Power of Attorney (Optional)"} +

+
*/} + + {/*
+ + {step === "company" && ( + <> + + Company Name + + + + +
+ + Company Email + + + + + +
+ +
+ + Location + + + + + + Address + + + +
+ +
+ + TIN Number (10 digits) + + + + + + VAT Number + + + +
+ + + FAN Number (16 digits) + + + + + )} + + {step === "personnel" && ( + <> +

+ Personal details are pulled from your account. Contact and + management info is collected below. +

+ +
+

+ Contact Person +

+
+ + Name + + + + + +
+
+ +
+ +
+

+ General Manager +

+
+ + Name + + + + + + Email + + + + + +
+
+ + )} + + {step === "poa" && ( + <> +

+ Power of Attorney details are optional. Skip if not applicable. +

+ + + PoA Name + + + +
+ + PoA Email + + + + +
+ +
+ + PoA Location + + + + + PoA Address + + +
+ + )} +
+ +
+ + + +
+
*/} + + ); +} + +function StepIcon({ + icon, + active, + completed, +}: { + icon: React.ReactNode; + active: boolean; + completed: boolean; +}) { + return ( +
+ {completed ? : icon} +
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/customers/on_boarding/DjiboutiFreightForwardingAgent.tsx b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/DjiboutiFreightForwardingAgent.tsx new file mode 100644 index 000000000..93e2215a7 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/DjiboutiFreightForwardingAgent.tsx @@ -0,0 +1,558 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { + ArrowLeft, + ArrowRight, + Building2, + User, + CheckCircle2, +} from "lucide-react"; + +import PhoneInput from "@/components/auth/PhoneInput"; + +import { + Button, + Field, + FieldError, + FieldGroup, + FieldLabel, + Input, +} from "@edr/ui-common"; + +type OnboardingStep = + | "personal" + | "company" + | "representative"; + +const schema = z.object({ + // PERSONAL + firstName: z.string().min(1, "First name is required"), + + lastName: z.string().min(1, "Last name is required"), + + email: z.string().email("Invalid email address"), + + phoneNumber: z + .string() + .min(1, "Phone number is required"), + + phoneCountryCode: z.string().min(1), + + // COMPANY + companyName: z + .string() + .min(1, "Company name is required"), + + companyEmail: z + .string() + .email("Invalid company email"), + + companyPhone: z + .string() + .min(1, "Company phone is required"), + + companyPhoneCountryCode: z.string().min(1), + + companyLocation: z + .string() + .min(1, "Company location is required"), + + companyAddress: z + .string() + .min(1, "Company address is required"), + + // REPRESENTATIVE + representativeName: z + .string() + .min(1, "Representative name is required"), + + representativeEmail: z + .string() + .email("Invalid representative email"), + + representativePhone: z + .string() + .min(1, "Representative phone is required"), + + representativePhoneCountryCode: z.string().min(1), +}); + +type FormData = z.infer; + +const stepFields: Record< + OnboardingStep, + (keyof FormData)[] +> = { + personal: [ + "firstName", + "lastName", + "email", + "phoneNumber", + "phoneCountryCode", + ], + + company: [ + "companyName", + "companyEmail", + "companyPhone", + "companyPhoneCountryCode", + "companyLocation", + "companyAddress", + ], + + representative: [ + "representativeName", + "representativeEmail", + "representativePhone", + "representativePhoneCountryCode", + ], +}; + +export default function DjiboutiForwardingAgentForm() { + const [step, setStep] = + useState("personal"); + + const { + register, + handleSubmit, + trigger, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(schema), + + defaultValues: { + phoneCountryCode: "+253", + companyPhoneCountryCode: "+253", + representativePhoneCountryCode: + "+253", + }, + }); + + const nextStep = async () => { + if (step === "representative") { + handleSubmit(onSubmit)(); + return; + } + + const isValid = await trigger( + stepFields[step] + ); + + if (!isValid) return; + + if (step === "personal") { + setStep("company"); + } else { + setStep("representative"); + } + }; + + const prevStep = () => { + if (step === "company") { + setStep("personal"); + } else if ( + step === "representative" + ) { + setStep("company"); + } + }; + + const onSubmit = async ( + data: FormData + ) => { + console.log(data); + }; + + return ( + <> + {/* STEPPER */} +
+
+
+ + } + active={step === "personal"} + completed={ + step !== "personal" + } + /> + + + } + active={step === "company"} + completed={ + step === "representative" + } + /> + + } + active={ + step === "representative" + } + completed={false} + /> +
+ +

+ {step === "personal" && + "Step 1 of 3 — Personal Information"} + + {step === "company" && + "Step 2 of 3 — Company Information"} + + {step === "representative" && + "Step 3 of 3 — Representative Information"} +

+
+ + {/* FORM */} +
+ + {/* PERSONAL */} + {step === "personal" && ( + <> +
+ + + First Name + + + + + + + + + + Last Name + + + + + + +
+ +
+ + Email + + + + + + + +
+ + )} + + {/* COMPANY */} + {step === "company" && ( + <> + + + Company Name + + + + + + + +
+ + + Company Email + + + + + + + + +
+ +
+ + + Company Location / Country + + + + + + + + + + Company Address + + + + + + +
+ + )} + + {/* REPRESENTATIVE */} + {step === + "representative" && ( + <> + + + Company Representative Person + Name + + + + + + + +
+ + + Representative Email + + + + + + + + +
+ + )} +
+ + {/* FOOTER */} +
+ + + +
+
+ + ); +} + +function StepIcon({ + icon, + active, + completed, +}: { + icon: React.ReactNode; + active: boolean; + completed: boolean; +}) { + return ( +
+ {completed ? ( + + ) : ( + icon + )} +
+ ); +} \ No newline at end of file diff --git a/apps/edr-freight-web/portal/src/pages/customers/on_boarding/ImportExportOnBoarding.tsx b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/ImportExportOnBoarding.tsx new file mode 100644 index 000000000..92ef7bac8 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/ImportExportOnBoarding.tsx @@ -0,0 +1,771 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { + ArrowLeft, + ArrowRight, + Building2, + User, + FileText, + CheckCircle2, +} from "lucide-react"; + +import PhoneInput from "@/components/auth/PhoneInput"; + +import { + Button, + Field, + FieldError, + FieldGroup, + FieldLabel, + Input, +} from "@edr/ui-common"; + +type OnboardingStep = + | "personal" + | "company" + | "personnel" + | "poa"; + +const onboardingSchema = z.object({ + // PERSONAL + firstName: z.string().min(1, "First name is required"), + lastName: z.string().min(1, "Last name is required"), + email: z.string().email("Invalid email address"), + phoneNumber: z.string().min(1, "Phone number is required"), + phoneCountryCode: z.string().min(1), + + // COMPANY + companyName: z.string().min(1, "Company name is required"), + companyEmail: z.string().email("Invalid email address"), + companyPhone: z.string().min(1, "Company phone is required"), + companyPhoneCountryCode: z.string().min(1), + companyLocation: z.string().min(1, "Location is required"), + companyAddress: z.string().min(1, "Address is required"), + + // LEGAL + tinNumber: z.string().regex(/^\d{10}$/, { + message: "TIN must be exactly 10 digits", + }), + + vatNumber: z.string().min(1, "VAT number is required"), + + fanNumber: z.string().regex(/^\d{16}$/, { + message: "FAN must be exactly 16 digits", + }), + + // CONTACT PERSON + contactPersonName: z + .string() + .min(1, "Contact person name is required"), + + contactPersonPhone: z + .string() + .min(1, "Contact person phone is required"), + + contactPersonPhoneCountryCode: z.string().min(1), + + // GENERAL MANAGER + generalManagerName: z + .string() + .min(1, "General manager name is required"), + + generalManagerEmail: z + .string() + .email("Invalid email"), + + generalManagerPhone: z + .string() + .min(1, "General manager phone is required"), + + generalManagerPhoneCountryCode: z.string().min(1), + + // OPTIONAL POA + poaName: z.string().optional(), + poaPhone: z.string().optional(), + poaPhoneCountryCode: z.string().optional(), + poaAddress: z.string().optional(), + poaEmail: z.string().optional(), + poaLocation: z.string().optional(), +}); + +type FormData = z.infer; + +const stepFields: Record< + OnboardingStep, + (keyof FormData)[] +> = { + personal: [ + "firstName", + "lastName", + "email", + "phoneNumber", + "phoneCountryCode", + ], + + company: [ + "companyName", + "companyEmail", + "companyPhone", + "companyPhoneCountryCode", + "companyLocation", + "companyAddress", + "tinNumber", + "vatNumber", + "fanNumber", + ], + + personnel: [ + "contactPersonName", + "contactPersonPhone", + "contactPersonPhoneCountryCode", + "generalManagerName", + "generalManagerEmail", + "generalManagerPhone", + "generalManagerPhoneCountryCode", + ], + + poa: [], +}; + +export default function ImportExportOnBoarding() { + const [step, setStep] = + useState("personal"); + + const { + register, + handleSubmit, + trigger, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(onboardingSchema), + + defaultValues: { + phoneCountryCode: "+251", + companyPhoneCountryCode: "+251", + contactPersonPhoneCountryCode: "+251", + generalManagerPhoneCountryCode: "+251", + poaPhoneCountryCode: "+251", + }, + }); + + const nextStep = async () => { + if (step === "poa") { + handleSubmit(onSubmit)(); + return; + } + + const isValid = await trigger(stepFields[step]); + + if (!isValid) return; + + if (step === "personal") { + setStep("company"); + } else if (step === "company") { + setStep("personnel"); + } else { + setStep("poa"); + } + }; + + const prevStep = () => { + if (step === "company") { + setStep("personal"); + } else if (step === "personnel") { + setStep("company"); + } else if (step === "poa") { + setStep("personnel"); + } + }; + + const onSubmit = async (data: FormData) => { + console.log(data); + }; + + return ( + <> + {/* STEP HEADER */} +
+
+
+ + } + active={step === "personal"} + completed={ + step !== "personal" + } + /> + + } + active={step === "company"} + completed={ + step === "personnel" || + step === "poa" + } + /> + + } + active={step === "personnel"} + completed={step === "poa"} + /> + + } + active={step === "poa"} + completed={false} + /> +
+ +

+ {step === "personal" && + "Step 1 of 4 — Personal Information"} + + {step === "company" && + "Step 2 of 4 — Company Information"} + + {step === "personnel" && + "Step 3 of 4 — Personnel Information"} + + {step === "poa" && + "Step 4 of 4 — Power of Attorney"} +

+
+ +
+ + {/* PERSONAL */} + {step === "personal" && ( + <> +
+ + + First Name + + + + + + + + + + Last Name + + + + + + +
+ +
+ + Email + + + + + + + +
+ + )} + + {/* COMPANY */} + {step === "company" && ( + <> + + + Company Name + + + + + + + +
+ + + Company Email + + + + + + + + +
+ +
+ + + Company Location + + + + + + + + + + Company Address + + + + + + +
+ +
+ + + TIN Number + + + + + + + + + + VAT Number + + + + + + + + + + FAN Number + + + + + + +
+ + )} + + {/* PERSONNEL */} + {step === "personnel" && ( + <> +
+

+ Contact Person +

+ +
+ + + Contact Person Name + + + + + + + + +
+
+ +
+ +
+

+ General Manager +

+ +
+ + + General Manager Name + + + + + + + + + + General Manager Email + + + + + + + + +
+
+ + )} + + {/* POA */} + {step === "poa" && ( + <> +

+ Power of Attorney details are + optional. +

+ + + PoA Name + + + + +
+ + + PoA Email + + + + + + +
+ +
+ + + PoA Location + + + + + + + + PoA Address + + + + +
+ + )} +
+ + {/* FOOTER */} +
+ + + +
+
+ + ); +} + +function StepIcon({ + icon, + active, + completed, +}: { + icon: React.ReactNode; + active: boolean; + completed: boolean; +}) { + return ( +
+ {completed ? ( + + ) : ( + icon + )} +
+ ); +} \ No newline at end of file diff --git a/apps/edr-freight-web/portal/src/pages/customers/on_boarding/TransportrOnBoarding.tsx b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/TransportrOnBoarding.tsx new file mode 100644 index 000000000..53ea34260 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/customers/on_boarding/TransportrOnBoarding.tsx @@ -0,0 +1,287 @@ +import { useState } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { z } from "zod"; + +import { + ArrowLeft, + ArrowRight, + User, + Truck, + CheckCircle2, +} from "lucide-react"; + +import PhoneInput from "@/components/auth/PhoneInput"; + +import { + Button, + Field, + FieldError, + FieldGroup, + FieldLabel, + Input, +} from "@edr/ui-common"; + +type Step = "personal" | "transport"; + +const schema = z.object({ + // PERSONAL + firstName: z.string().min(1), + lastName: z.string().min(1), + email: z.string().email(), + phoneNumber: z.string().min(1), + phoneCountryCode: z.string().min(1), + + // TRANSPORT + fanNumber: z.string().min(1), + tinNumber: z.string().min(1), + + truckType: z.enum([ + "Casoni", + "Truck Trailer", + "High Bed", + "Low Bed", + "Others", + ]), + + plateNumber: z.string().min(1), + + plateNumber2: z.string().optional(), + + vehicleModel: z.string().min(1), + yearOfManufacturing: z.string().min(1), +}); + +type FormData = z.infer; + +const stepFields: Record = { + personal: [ + "firstName", + "lastName", + "email", + "phoneNumber", + "phoneCountryCode", + ], + transport: [ + "fanNumber", + "tinNumber", + "truckType", + "plateNumber", + "plateNumber2", + "vehicleModel", + "yearOfManufacturing", + ], +}; + +export default function TransporterOnboarding() { + const [step, setStep] = useState("personal"); + + const { + register, + handleSubmit, + trigger, + watch, + formState: { errors, isSubmitting }, + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + phoneCountryCode: "+251", + }, + }); + + const truckType = watch("truckType"); + + const nextStep = async () => { + const valid = await trigger(stepFields[step]); + if (!valid) return; + + if (step === "personal") setStep("transport"); + else handleSubmit(onSubmit)(); + }; + + const prevStep = () => { + if (step === "transport") setStep("personal"); + }; + + const onSubmit = (data: FormData) => { + console.log("TRANSPORTER:", data); + }; + + return ( + <> + {/* STEPPER */} +
+
+
+ + } + active={step === "personal"} + completed={step !== "personal"} + /> + + } + active={step === "transport"} + completed={false} + /> +
+ +

+ {step === "personal" && "Step 1 of 2 — Personal Information"} + {step === "transport" && "Step 2 of 2 — Transport Information"} +

+
+ + {/* FORM */} +
+ + + {/* PERSONAL */} + {step === "personal" && ( + <> +
+ + First Name + + + + + + Last Name + + + +
+ +
+ + Email + + + + + +
+ + )} + + {/* TRANSPORT */} + {step === "transport" && ( + <> +
+ + FAN Number + + + + + + TIN Number + + + +
+ + + Truck Type + + + + +
+ + Plate Number + + + + + {truckType === "Casoni" && ( + + Second Plate Number (Casoni) + + + + )} +
+ +
+ + Vehicle Model + + + + + + Year of Manufacturing + + + +
+ + )} + +
+ + {/* FOOTER */} +
+ + + +
+
+ + ); +} + +function StepIcon({ + icon, + active, + completed, +}: { + icon: React.ReactNode; + active: boolean; + completed: boolean; +}) { + return ( +
+ {completed ? : icon} +
+ ); +} \ No newline at end of file