From ea0f264d72c922b840b7e4433ffeac0ade9bec50 Mon Sep 17 00:00:00 2001 From: Nathnael Date: Fri, 10 Jul 2026 08:19:12 +0000 Subject: [PATCH] fea: add poa section to backoffice customer detail page --- .../pages/customers/CustomerDetailPage.tsx | 167 ++++++++++++++++++ .../src/services/customers.service.ts | 5 + .../backoffice/src/types/customer.ts | 5 + 3 files changed, 177 insertions(+) diff --git a/apps/edr-freight-web/backoffice/src/pages/customers/CustomerDetailPage.tsx b/apps/edr-freight-web/backoffice/src/pages/customers/CustomerDetailPage.tsx index 317f915fb..5d0655f8d 100644 --- a/apps/edr-freight-web/backoffice/src/pages/customers/CustomerDetailPage.tsx +++ b/apps/edr-freight-web/backoffice/src/pages/customers/CustomerDetailPage.tsx @@ -7,6 +7,7 @@ import { Card, Center, Container, + Divider, Group, Loader, SimpleGrid, @@ -90,6 +91,9 @@ function tableStatus(query: { isLoading: boolean; isError: boolean }) { return query.isLoading ? "loading" : query.isError ? "error" : "success"; } +/** Matches the fileKey seeded in the API's file-upload-settings seeder. */ +const POA_DELEGATION_CODE = "poa_delegation_letter"; + export default function CustomerDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); @@ -531,6 +535,26 @@ export default function CustomerDetailPage() { (p) => p.licenseFiles && p.licenseFiles.length > 0, ); + const poaDocuments = useMemo( + () => documents.filter((d) => d.code === POA_DELEGATION_CODE), + [documents], + ); + const poaFields = [ + { label: "PoA name", value: company?.poaName }, + { label: "PoA email", value: company?.poaEmail }, + { label: "PoA phone", value: company?.poaPhone }, + { label: "PoA location", value: company?.poaLocation }, + { label: "PoA address", value: company?.poaAddress }, + ]; + const hasPoaDetails = poaFields.some((f) => f.value?.trim()); + // A freight forwarder acts on other companies' behalf, so its PoA — details + // and delegation letter both — is mandatory rather than optional. + const poaMandatory = (company?.companyProfiles ?? []).some( + (p) => p.type === "freight_forwarder", + ); + const delegationMissing = + (hasPoaDetails || poaMandatory) && poaDocuments.length === 0; + if (isLoading) { return (
@@ -672,6 +696,149 @@ export default function CustomerDetailPage() { + + + + + + Power of Attorney + + {poaMandatory && ( + + Required for freight forwarder + + )} + + {delegationMissing ? ( + + Delegation letter missing + + ) : poaDocuments.length > 0 ? ( + + Delegation letter on file + + ) : ( + + Not provided + + )} + + + {hasPoaDetails ? ( + + {poaFields.map((f) => ( + + ))} + + ) : ( + + No Power of Attorney representative recorded for this + customer. + + )} + + + + + + Delegation letter + + + {documentsQuery.isLoading ? ( + + + + Loading documents… + + + ) : documentsQuery.isError ? ( + + + Failed to load documents. + + void documentsQuery.refetch()} + > + Retry + + + ) : poaDocuments.length === 0 ? ( + + No delegation letter uploaded. + + ) : ( + poaDocuments.map((doc) => ( + + + + + view({ + name: doc.name, + url: fileViewUrl(doc.id), + mimeType: doc.mimeType, + }) + } + > + {doc.name} + + + {formatBytes(doc.size)} ·{" "} + {formatDate(doc.uploadedAt)} + + + + + view({ + name: doc.name, + url: fileViewUrl(doc.id), + mimeType: doc.mimeType, + }) + } + > + + + + + + + + )) + )} + + + + diff --git a/apps/edr-freight-web/backoffice/src/services/customers.service.ts b/apps/edr-freight-web/backoffice/src/services/customers.service.ts index 0476d41bb..c9a649946 100644 --- a/apps/edr-freight-web/backoffice/src/services/customers.service.ts +++ b/apps/edr-freight-web/backoffice/src/services/customers.service.ts @@ -33,6 +33,11 @@ function mapCompany(dto: Record): Company { generalManagerName: (attrs.generalManagerName as string | null) ?? null, generalManagerEmail: (attrs.generalManagerEmail as string | null) ?? null, generalManagerPhone: (attrs.generalManagerPhone as string | null) ?? null, + poaName: (attrs.poaName as string | null) ?? null, + poaEmail: (attrs.poaEmail as string | null) ?? null, + poaPhone: (attrs.poaPhone as string | null) ?? null, + poaLocation: (attrs.poaLocation as string | null) ?? null, + poaAddress: (attrs.poaAddress as string | null) ?? null, }; } diff --git a/apps/edr-freight-web/backoffice/src/types/customer.ts b/apps/edr-freight-web/backoffice/src/types/customer.ts index 7328decf0..5931ea5d6 100644 --- a/apps/edr-freight-web/backoffice/src/types/customer.ts +++ b/apps/edr-freight-web/backoffice/src/types/customer.ts @@ -127,6 +127,11 @@ export interface Company { generalManagerName?: string | null; generalManagerEmail?: string | null; generalManagerPhone?: string | null; + poaName?: string | null; + poaEmail?: string | null; + poaPhone?: string | null; + poaLocation?: string | null; + poaAddress?: string | null; website?: string | null; attributes?: Record | null; companyProfiles: CompanyProfile[];