Files
edr-platform/apps/edr-freight-web/portal/src/components/onboarding/RoleLicenseStep.tsx
2026-06-26 13:07:29 +00:00

138 lines
4.1 KiB
TypeScript

import { Anchor, Group, Stack, Text } from "@mantine/core";
import { Paperclip } from "lucide-react";
import { SmartFileInput } from "@edr/ui-common";
import type { IFileUploadSetting } from "@edr/types/freight";
import type { LicenseFile } from "@/services/companies.service";
const ROLE_LABELS: Record<string, string> = {
importer: "Importer",
exporter: "Exporter",
freight_forwarder: "Freight Forwarder",
dj_freight_forwarder: "DJ Freight Forwarder",
transporter: "Transporter",
};
/** Field key the synthesized per-profile upload setting is keyed on. */
const LICENSE_FILE_KEY = "business_license";
/**
* Build a single-field upload setting so each profile's license input can reuse
* the shared SmartFileInput (same dropzone + "uploaded" state as the documents
* step), instead of a bespoke file picker.
*/
function buildLicenseSetting(
profileId: string,
profileName: string,
): IFileUploadSetting {
return {
id: `license-setting-${profileId}`,
createdAt: "",
updatedAt: "",
deletedAt: null,
code: "business_license",
label: "Business license",
description: null,
entity: "customer",
fields: [
{
id: `${LICENSE_FILE_KEY}-${profileId}`,
createdAt: "",
updatedAt: "",
deletedAt: null,
settingId: `license-setting-${profileId}`,
fileKey: LICENSE_FILE_KEY,
fileLabel: `Upload ${profileName} Business license file(s)`,
helpText: null,
isRequired: true,
isMultiple: true,
maxFiles: 10,
allowedExtensions: ["pdf", "png", "jpg", "jpeg"],
maxSizeMb: 10,
order: 1,
},
],
};
}
export interface RoleLicenseProfile {
id: string;
type: string;
reference: string;
/** License files already uploaded for this profile (rehydration). */
existingFiles: LicenseFile[];
}
interface RoleLicenseStepProps {
/** One card per operational role/profile. */
profiles: RoleLicenseProfile[];
/** Newly-selected files per profile id (not yet uploaded). */
value: Record<string, File[]>;
onChange: (value: Record<string, File[]>) => void;
}
/**
* Final onboarding step: collect a business license (one or more files) for
* each operational role the company holds. Each role gets its own SmartFileInput
* dropzone; already-uploaded files are listed (with download links) for context
* and surface the input's "uploaded" state.
*/
export default function RoleLicenseStep({
profiles,
value,
onChange,
}: RoleLicenseStepProps) {
const setFiles = (profileId: string, files: File[]) => {
onChange({ ...value, [profileId]: files });
};
return (
<Stack gap="md">
<Text size="sm" c="edr-muted">
Upload the business license for each of your operational profiles. You
can attach more than one document per profile.
</Text>
{profiles.map((profile) => {
const label = ROLE_LABELS[profile.type] ?? profile.type;
const selected = value[profile.id] ?? [];
const hasExisting = profile.existingFiles.length > 0;
return (
<>
{hasExisting && (
<Stack gap={4} mb="sm">
{profile.existingFiles.map((f) => (
<Group key={f.url} gap={6} wrap="nowrap">
<Paperclip size={13} className="text-edr-muted" />
<Anchor
href={f.url}
target="_blank"
rel="noopener noreferrer"
size="xs"
>
{f.name}
</Anchor>
</Group>
))}
</Stack>
)}
<SmartFileInput
file={buildLicenseSetting(profile.id, label)}
value={{ [LICENSE_FILE_KEY]: selected }}
uploadedKeys={hasExisting ? [LICENSE_FILE_KEY] : undefined}
onChange={(v) => {
const next = v[LICENSE_FILE_KEY];
const files = Array.isArray(next) ? next : next ? [next] : [];
setFiles(profile.id, files);
}}
/>
</>
);
})}
</Stack>
);
}