From 5898634cd75cfb8a4ee068a545c5bcf1d1b50cac Mon Sep 17 00:00:00 2001 From: ghost2023 Date: Sat, 6 Jun 2026 11:29:05 +0300 Subject: [PATCH] refactor(settings): decompose monolithic SettingsPage into modular tab components --- .../portal/src/pages/SettingsPage.tsx | 531 +----------------- .../src/pages/settings/TabCompanyProfile.tsx | 219 ++++++++ .../src/pages/settings/TabContactPerson.tsx | 145 +++++ .../src/pages/settings/TabDocuments.tsx | 107 ++++ .../src/pages/settings/TabGeneralManager.tsx | 161 ++++++ .../src/pages/settings/TabPowerOfAttorney.tsx | 208 +++++++ 6 files changed, 852 insertions(+), 519 deletions(-) create mode 100644 apps/edr-freight-web/portal/src/pages/settings/TabCompanyProfile.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/settings/TabContactPerson.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/settings/TabDocuments.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/settings/TabGeneralManager.tsx create mode 100644 apps/edr-freight-web/portal/src/pages/settings/TabPowerOfAttorney.tsx diff --git a/apps/edr-freight-web/portal/src/pages/SettingsPage.tsx b/apps/edr-freight-web/portal/src/pages/SettingsPage.tsx index faec1b5b2..2083fcd37 100644 --- a/apps/edr-freight-web/portal/src/pages/SettingsPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/SettingsPage.tsx @@ -1,41 +1,20 @@ -import { useState, useMemo } from "react"; import { useSearchParams } from "react-router-dom"; -import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; +import { useQuery } from "@tanstack/react-query"; import { Building2, User, Briefcase, UserCheck, FileCheck, - Loader2, - Save, - UploadCloud, - CheckCircle2, - XCircle, } from "lucide-react"; import { api } from "@/services/api"; -import { companiesService } from "@/services/companies.service"; -import PhoneInput from "@/components/auth/PhoneInput"; -import { - Card, - CardHeader, - CardTitle, - CardDescription, - CardContent, - CardFooter, - Button, - Input, - Field, - FieldLabel, - FieldError, - FieldGroup, - SmartFileInput, - Badge, -} from "@edr/ui-common"; +import { Badge } from "@edr/ui-common"; import { cn } from "@/lib/utils"; +import TabCompanyProfile from "./settings/TabCompanyProfile"; +import TabContactPerson from "./settings/TabContactPerson"; +import TabGeneralManager from "./settings/TabGeneralManager"; +import TabPowerOfAttorney from "./settings/TabPowerOfAttorney"; +import TabDocuments from "./settings/TabDocuments"; type SettingsTab = | "company" @@ -44,32 +23,6 @@ type SettingsTab = | "poa" | "documents"; -const settingsSchema = 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"), - 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(), - poaEmail: z.string().optional(), - poaPhone: z.string().optional(), - poaPhoneCountryCode: z.string().optional(), - poaLocation: z.string().optional(), - poaAddress: z.string().optional(), -}); - -type FormData = z.infer; - const TABS: { id: SettingsTab; label: string; icon: React.ReactNode }[] = [ { id: "company", label: "Company Profile", icon: }, { id: "contact", label: "Contact Person", icon: }, @@ -78,15 +31,7 @@ const TABS: { id: SettingsTab; label: string; icon: React.ReactNode }[] = [ { id: "documents", label: "Documents", icon: }, ]; -function splitPhone(fullPhone?: string | null): { code: string; number: string } { - if (!fullPhone) return { code: "+251", number: "" }; - const match = fullPhone.match(/^(\+\d{1,3})(.*)$/); - if (match) return { code: match[1], number: match[2] }; - return { code: "+251", number: fullPhone }; -} - export default function SettingsPage() { - const queryClient = useQueryClient(); const [searchParams, setSearchParams] = useSearchParams(); const tab = (searchParams.get("tab") as SettingsTab) || "company"; const setTab = (t: SettingsTab) => { @@ -96,130 +41,13 @@ export default function SettingsPage() { return next; }, { replace: true }); }; - const [documentFiles, setDocumentFiles] = useState< - Record - >({}); const profileQuery = useQuery( api.companies.getProfile.queryOptions(), ); - const docSettingQuery = useQuery( - api.fileUploadSettings.getByCode.queryOptions({ - input: { code: "customer_documents" }, - enabled: tab === "documents", - }), - ); - const profile = profileQuery.data; - const defaultValues = useMemo((): FormData => { - if (!profile) { - return { - companyName: "", - companyEmail: "", - companyPhone: "", - companyPhoneCountryCode: "+251", - companyLocation: "", - companyAddress: "", - tinNumber: "", - fanNumber: "", - contactPersonName: "", - contactPersonPhone: "", - contactPersonPhoneCountryCode: "+251", - generalManagerName: "", - generalManagerEmail: "", - generalManagerPhone: "", - generalManagerPhoneCountryCode: "+251", - poaName: "", - poaEmail: "", - poaPhone: "", - poaPhoneCountryCode: "+251", - poaLocation: "", - poaAddress: "", - }; - } - const contactPhone = splitPhone(profile.contactPersonPhone); - const gmPhone = splitPhone(profile.generalManagerPhone); - const poaPhone = splitPhone(profile.poaPhone); - return { - companyName: profile.companyName, - companyEmail: profile.companyEmail ?? "", - companyPhone: profile.companyPhone ?? "", - companyPhoneCountryCode: splitPhone(profile.companyPhone).code, - companyLocation: profile.companyLocation, - companyAddress: profile.companyAddress ?? "", - tinNumber: profile.tinNumber, - fanNumber: profile.fanNumber ?? "", - contactPersonName: profile.contactPersonName ?? "", - contactPersonPhone: contactPhone.number, - contactPersonPhoneCountryCode: contactPhone.code, - generalManagerName: profile.generalManagerName ?? "", - generalManagerEmail: profile.generalManagerEmail ?? "", - generalManagerPhone: gmPhone.number, - generalManagerPhoneCountryCode: gmPhone.code, - poaName: profile.poaName ?? "", - poaEmail: profile.poaEmail ?? "", - poaPhone: poaPhone.number, - poaPhoneCountryCode: poaPhone.code, - poaLocation: profile.poaLocation ?? "", - poaAddress: profile.poaAddress ?? "", - }; - }, [profile]); - - const { - register, - handleSubmit, - reset, - formState: { errors, isDirty }, - } = useForm({ - resolver: zodResolver(settingsSchema), - values: defaultValues, - }); - - const updateMutation = useMutation({ - mutationFn: (data: FormData) => - api.companies.updateProfile.call({ - companyName: data.companyName, - companyEmail: data.companyEmail, - companyPhone: `${data.companyPhoneCountryCode}${data.companyPhone}`, - companyLocation: data.companyLocation, - companyAddress: data.companyAddress, - tin: data.tinNumber, - fanNumber: data.fanNumber, - contactPersonName: data.contactPersonName, - contactPersonPhone: `${data.contactPersonPhoneCountryCode}${data.contactPersonPhone}`, - 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, - poaEmail: data.poaEmail || undefined, - poaLocation: data.poaLocation || undefined, - poaAddress: data.poaAddress || undefined, - }), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: api.companies.getProfile.queryKey(), - }); - }, - }); - - const docUploadMutation = useMutation({ - mutationFn: (files: Record) => - companiesService.uploadDocuments(profile!.companyId, files), - onSuccess: () => { - queryClient.invalidateQueries({ - queryKey: api.companies.getProfile.queryKey(), - }); - }, - }); - - const isPending = profileQuery.isPending || updateMutation.isPending || docUploadMutation.isPending; - if (profileQuery.isPending) { return (
@@ -236,10 +64,6 @@ export default function SettingsPage() { ); } - const onSubmit = (data: FormData) => { - updateMutation.mutate(data); - }; - return (
@@ -276,342 +100,11 @@ export default function SettingsPage() { ))}
-
- - - - {tab === "company" && <> Company Profile} - {tab === "contact" && <> Contact Person} - {tab === "gm" && <> General Manager} - {tab === "poa" && <> Power of Attorney} - {tab === "documents" && <> Documents} - - - {tab === "company" && "Edit your company registration details"} - {tab === "contact" && "Manage the primary contact person for your account"} - {tab === "gm" && "Manage the general manager information"} - {tab === "poa" && "Power of Attorney details are optional"} - {tab === "documents" && "Upload and manage required business documents"} - - - - - - {/* Company Profile Tab */} - {tab === "company" && ( - <> - - Company Name - - - - -
- - Company Email - - - - - -
- -
- - Location - - - - - - Address - - - -
- -
- - TIN Number (10 digits) - - - - - - FAN Number (16 digits) - - - -
- - )} - - {/* Contact Person Tab */} - {tab === "contact" && ( - <> - - Full Name - - - - - - - )} - - {/* General Manager Tab */} - {tab === "gm" && ( - <> - - Full Name - - - - -
- - Email Address - - - - - -
- - )} - - {/* Power of Attorney Tab */} - {tab === "poa" && ( - <> -

- Power of Attorney details are optional. Fill them in if you have - an authorized representative, or leave blank. -

- - - PoA Full Name - - - - -
- - PoA Email - - - - - -
- -
- - PoA Location - - - - - - PoA Address - - - -
- - )} - - {/* Documents Tab */} - {tab === "documents" && ( - <> - {docSettingQuery.isLoading ? ( -
- -
- ) : !docSettingQuery.data ? ( -

- No document requirements configured for your account. -

- ) : ( - - )} - - {docSettingQuery.data && ( -
-
- {docUploadMutation.isSuccess && ( - - - Documents uploaded successfully - - )} - {docUploadMutation.isError && ( - - - Upload failed - - )} -
- -
- )} - - )} -
-
- - {tab !== "documents" && ( - -
- {updateMutation.isSuccess && ( - - - Saved successfully - - )} - {updateMutation.isError && ( - - - Save failed - - )} -
-
- - -
-
- )} -
-
+ {tab === "company" && } + {tab === "contact" && } + {tab === "gm" && } + {tab === "poa" && } + {tab === "documents" && }
); } diff --git a/apps/edr-freight-web/portal/src/pages/settings/TabCompanyProfile.tsx b/apps/edr-freight-web/portal/src/pages/settings/TabCompanyProfile.tsx new file mode 100644 index 000000000..b64419565 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/settings/TabCompanyProfile.tsx @@ -0,0 +1,219 @@ +import { useMemo } 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 { Building2, CheckCircle2, Loader2, Save, XCircle } from "lucide-react"; +import { api } from "@/services/api"; +import PhoneInput from "@/components/auth/PhoneInput"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, + Button, + Input, + Field, + FieldLabel, + FieldError, + FieldGroup, +} from "@edr/ui-common"; +import type { ProfileResponse } from "@/types/profile"; + +const schema = 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"), + fanNumber: z.string().length(16, "FAN must be exactly 16 digits"), +}); + +type FormData = z.infer; + +function splitPhone(fullPhone?: string | null) { + if (!fullPhone) return { code: "+251", number: "" }; + const match = fullPhone.match(/^(\+\d{1,3})(.*)$/); + if (match) return { code: match[1], number: match[2] }; + return { code: "+251", number: fullPhone }; +} + +export default function TabCompanyProfile({ profile }: { profile: ProfileResponse }) { + const queryClient = useQueryClient(); + + const defaultValues = useMemo((): FormData => { + const phone = splitPhone(profile.companyPhone); + return { + companyName: profile.companyName, + companyEmail: profile.companyEmail ?? "", + companyPhone: phone.number, + companyPhoneCountryCode: phone.code, + companyLocation: profile.companyLocation, + companyAddress: profile.companyAddress ?? "", + tinNumber: profile.tinNumber, + fanNumber: profile.fanNumber ?? "", + }; + }, [profile]); + + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty }, + } = useForm({ + resolver: zodResolver(schema), + values: defaultValues, + }); + + const mutation = useMutation({ + mutationFn: (data: FormData) => + api.companies.updateProfile.call({ + companyName: data.companyName, + companyEmail: data.companyEmail, + companyPhone: `${data.companyPhoneCountryCode}${data.companyPhone}`, + companyLocation: data.companyLocation, + companyAddress: data.companyAddress, + tin: data.tinNumber, + fanNumber: data.fanNumber, + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: api.companies.getProfile.queryKey() }); + }, + }); + + const onSubmit = (data: FormData) => mutation.mutate(data); + + return ( + + + + + Company Profile + + Edit your company registration details + +
+ + + + Company Name + + + + +
+ + Company Email + + + + + +
+ +
+ + Location + + + + + + Address + + + +
+ +
+ + TIN Number (10 digits) + + + + + + FAN Number (16 digits) + + + +
+
+
+ +
+ {mutation.isSuccess && ( + + + Saved successfully + + )} + {mutation.isError && ( + + + Save failed + + )} +
+
+ + +
+
+
+
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/settings/TabContactPerson.tsx b/apps/edr-freight-web/portal/src/pages/settings/TabContactPerson.tsx new file mode 100644 index 000000000..bb2401be8 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/settings/TabContactPerson.tsx @@ -0,0 +1,145 @@ +import { useMemo } 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 { CheckCircle2, Loader2, Save, User, XCircle } from "lucide-react"; +import { api } from "@/services/api"; +import PhoneInput from "@/components/auth/PhoneInput"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, + Button, + Input, + Field, + FieldLabel, + FieldError, + FieldGroup, +} from "@edr/ui-common"; +import type { ProfileResponse } from "@/types/profile"; + +const schema = z.object({ + 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"), +}); + +type FormData = z.infer; + +function splitPhone(fullPhone?: string | null) { + if (!fullPhone) return { code: "+251", number: "" }; + const match = fullPhone.match(/^(\+\d{1,3})(.*)$/); + if (match) return { code: match[1], number: match[2] }; + return { code: "+251", number: fullPhone }; +} + +export default function TabContactPerson({ profile }: { profile: ProfileResponse }) { + const queryClient = useQueryClient(); + + const defaultValues = useMemo((): FormData => { + const phone = splitPhone(profile.contactPersonPhone); + return { + contactPersonName: profile.contactPersonName ?? "", + contactPersonPhone: phone.number, + contactPersonPhoneCountryCode: phone.code, + }; + }, [profile]); + + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty }, + } = useForm({ + resolver: zodResolver(schema), + values: defaultValues, + }); + + const mutation = useMutation({ + mutationFn: (data: FormData) => + api.companies.updateProfile.call({ + contactPersonName: data.contactPersonName, + contactPersonPhone: `${data.contactPersonPhoneCountryCode}${data.contactPersonPhone}`, + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: api.companies.getProfile.queryKey() }); + }, + }); + + const onSubmit = (data: FormData) => mutation.mutate(data); + + return ( + + + + + Contact Person + + Manage the primary contact person for your account + +
+ + + + Full Name + + + + + + + + +
+ {mutation.isSuccess && ( + + + Saved successfully + + )} + {mutation.isError && ( + + + Save failed + + )} +
+
+ + +
+
+
+
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/settings/TabDocuments.tsx b/apps/edr-freight-web/portal/src/pages/settings/TabDocuments.tsx new file mode 100644 index 000000000..298f7ad7c --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/settings/TabDocuments.tsx @@ -0,0 +1,107 @@ +import { useState } from "react"; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { + CheckCircle2, + FileCheck, + Loader2, + UploadCloud, + XCircle, +} from "lucide-react"; +import { api } from "@/services/api"; +import { companiesService } from "@/services/companies.service"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + Button, + SmartFileInput, +} from "@edr/ui-common"; +import type { ProfileResponse } from "@/types/profile"; + +export default function TabDocuments({ profile }: { profile: ProfileResponse }) { + const queryClient = useQueryClient(); + const [documentFiles, setDocumentFiles] = useState>({}); + + const docSettingQuery = useQuery( + api.fileUploadSettings.getByCode.queryOptions({ + input: { code: "customer_documents" }, + }), + ); + + const docUploadMutation = useMutation({ + mutationFn: (files: Record) => + companiesService.uploadDocuments(profile.companyId, files), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: api.companies.getProfile.queryKey() }); + }, + }); + + return ( + + + + + Documents + + + Upload and manage required business documents + + + + {docSettingQuery.isLoading ? ( +
+ +
+ ) : !docSettingQuery.data ? ( +

+ No document requirements configured for your account. +

+ ) : ( + + )} + + {docSettingQuery.data && ( +
+
+ {docUploadMutation.isSuccess && ( + + + Documents uploaded successfully + + )} + {docUploadMutation.isError && ( + + + Upload failed + + )} +
+ +
+ )} +
+
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/settings/TabGeneralManager.tsx b/apps/edr-freight-web/portal/src/pages/settings/TabGeneralManager.tsx new file mode 100644 index 000000000..7c7f0d4d3 --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/settings/TabGeneralManager.tsx @@ -0,0 +1,161 @@ +import { useMemo } 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 { Briefcase, CheckCircle2, Loader2, Save, XCircle } from "lucide-react"; +import { api } from "@/services/api"; +import PhoneInput from "@/components/auth/PhoneInput"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, + Button, + Input, + Field, + FieldLabel, + FieldError, + FieldGroup, +} from "@edr/ui-common"; +import type { ProfileResponse } from "@/types/profile"; + +const schema = z.object({ + 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"), +}); + +type FormData = z.infer; + +function splitPhone(fullPhone?: string | null) { + if (!fullPhone) return { code: "+251", number: "" }; + const match = fullPhone.match(/^(\+\d{1,3})(.*)$/); + if (match) return { code: match[1], number: match[2] }; + return { code: "+251", number: fullPhone }; +} + +export default function TabGeneralManager({ profile }: { profile: ProfileResponse }) { + const queryClient = useQueryClient(); + + const defaultValues = useMemo((): FormData => { + const phone = splitPhone(profile.generalManagerPhone); + return { + generalManagerName: profile.generalManagerName ?? "", + generalManagerEmail: profile.generalManagerEmail ?? "", + generalManagerPhone: phone.number, + generalManagerPhoneCountryCode: phone.code, + }; + }, [profile]); + + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty }, + } = useForm({ + resolver: zodResolver(schema), + values: defaultValues, + }); + + const mutation = useMutation({ + mutationFn: (data: FormData) => + api.companies.updateProfile.call({ + generalManagerName: data.generalManagerName, + generalManagerEmail: data.generalManagerEmail, + generalManagerPhone: `${data.generalManagerPhoneCountryCode}${data.generalManagerPhone}`, + }), + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: api.companies.getProfile.queryKey() }); + }, + }); + + const onSubmit = (data: FormData) => mutation.mutate(data); + + return ( + + + + + General Manager + + Manage the general manager information + +
+ + + + Full Name + + + + +
+ + Email Address + + + + + +
+
+
+ +
+ {mutation.isSuccess && ( + + + Saved successfully + + )} + {mutation.isError && ( + + + Save failed + + )} +
+
+ + +
+
+
+
+ ); +} diff --git a/apps/edr-freight-web/portal/src/pages/settings/TabPowerOfAttorney.tsx b/apps/edr-freight-web/portal/src/pages/settings/TabPowerOfAttorney.tsx new file mode 100644 index 000000000..e5d977e2d --- /dev/null +++ b/apps/edr-freight-web/portal/src/pages/settings/TabPowerOfAttorney.tsx @@ -0,0 +1,208 @@ +import { useMemo } 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 { CheckCircle2, Loader2, Save, UserCheck, XCircle } from "lucide-react"; +import { api } from "@/services/api"; +import PhoneInput from "@/components/auth/PhoneInput"; +import { + Card, + CardHeader, + CardTitle, + CardDescription, + CardContent, + CardFooter, + Button, + Input, + Field, + FieldLabel, + FieldError, + FieldGroup, +} from "@edr/ui-common"; +import type { ProfileResponse } from "@/types/profile"; + +const schema = z.object({ + poaName: z.string().optional(), + poaEmail: z.string().optional(), + poaPhone: z.string().optional(), + poaPhoneCountryCode: z.string().optional(), + poaLocation: z.string().optional(), + poaAddress: z.string().optional(), +}); + +type FormData = z.infer; + +function splitPhone(fullPhone?: string | null) { + if (!fullPhone) return { code: "+251", number: "" }; + const match = fullPhone.match(/^(\+\d{1,3})(.*)$/); + if (match) return { code: match[1], number: match[2] }; + return { code: "+251", number: fullPhone }; +} + +export default function TabPowerOfAttorney({ + profile, +}: { + profile: ProfileResponse; +}) { + const queryClient = useQueryClient(); + + const defaultValues = useMemo((): FormData => { + const phone = splitPhone(profile.poaPhone); + return { + poaName: profile.poaName ?? "", + poaEmail: profile.poaEmail ?? "", + poaPhone: phone.number, + poaPhoneCountryCode: profile.poaPhone ? phone.code : "", + poaLocation: profile.poaLocation ?? "", + poaAddress: profile.poaAddress ?? "", + }; + }, [profile]); + + const { + register, + handleSubmit, + reset, + formState: { errors, isDirty }, + } = useForm({ + resolver: zodResolver(schema), + + values: defaultValues, + }); + + const mutation = useMutation({ + mutationFn: (data: FormData) => + api.companies.updateProfile.call({ + poaName: data.poaName || undefined, + poaPhone: + data.poaPhone && data.poaPhoneCountryCode + ? `${data.poaPhoneCountryCode}${data.poaPhone}` + : undefined, + poaEmail: data.poaEmail || undefined, + poaLocation: data.poaLocation || undefined, + poaAddress: data.poaAddress || undefined, + }), + onSuccess: () => { + queryClient.invalidateQueries({ + queryKey: api.companies.getProfile.queryKey(), + }); + }, + }); + + const onSubmit = (data: FormData) => mutation.mutate(data); + + return ( + + + + + Power of Attorney + + + Power of Attorney details are optional. Fill them in if you have an + authorized representative, or leave blank. + + +
+ + +

+ Power of Attorney details are optional. Fill them in if you have + an authorized representative, or leave blank. +

+ + + PoA Full Name + + + + +
+ + PoA Email + + + + + +
+ +
+ + PoA Location + + + + + + PoA Address + + + +
+
+
+ +
+ {mutation.isSuccess && ( + + + Saved successfully + + )} + {mutation.isError && ( + + + Save failed + + )} +
+
+ + +
+
+
+
+ ); +}