mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
128 lines
3.9 KiB
TypeScript
128 lines
3.9 KiB
TypeScript
import { Box, Button, Group, Paper, Stack, Text } from "@mantine/core";
|
|
import { FileText, Upload } from "lucide-react";
|
|
import type { ReactNode } from "react";
|
|
import type { Freight } from "@edr/types";
|
|
|
|
import { PhasedFileDropzone } from "@/components/contracts/PhasedFileDropzone";
|
|
import { PhasedUploadedFileRow, findWorkflowFile } from "@/components/contracts/PhasedUploadedFileRow";
|
|
|
|
export interface PhasedDocumentUploadFieldProps {
|
|
fields: Array<{ key: string; label: string }>;
|
|
files: Record<string, File | null>;
|
|
onChange: (key: string, file: File | null) => void;
|
|
workflowFiles?: Freight.ClearanceWorkflowFile[];
|
|
helperText: string;
|
|
/** Extra inputs rendered above the dropzones (e.g. required DO dates). */
|
|
extraFields?: ReactNode;
|
|
replaceMode?: boolean;
|
|
loading?: boolean;
|
|
disabled?: boolean;
|
|
submitLabel?: string;
|
|
onSubmit: () => void;
|
|
onViewFile?: (file: { name: string; url: string }) => void;
|
|
onDownloadFile?: (file: { id: string; name: string }) => void;
|
|
}
|
|
|
|
/** Consistent phased customs document upload with drag-and-drop, preview, and uploaded rows. */
|
|
export function PhasedDocumentUploadField({
|
|
fields,
|
|
files,
|
|
onChange,
|
|
workflowFiles = [],
|
|
helperText,
|
|
extraFields,
|
|
replaceMode = false,
|
|
loading = false,
|
|
disabled = false,
|
|
submitLabel,
|
|
onSubmit,
|
|
onViewFile,
|
|
onDownloadFile,
|
|
}: PhasedDocumentUploadFieldProps) {
|
|
const hasStaged = Object.values(files).some(Boolean);
|
|
const uploaded = fields
|
|
.map((f) => ({ ...f, file: findWorkflowFile(workflowFiles, f.key) }))
|
|
.filter((f) => f.file);
|
|
const multiField = fields.length > 1;
|
|
|
|
return (
|
|
<Stack gap="md">
|
|
{uploaded.length > 0 ? (
|
|
<Stack gap={8}>
|
|
<Text size="xs" fw={700} c="dimmed" tt="uppercase">
|
|
Current file{uploaded.length > 1 ? "s" : ""}
|
|
</Text>
|
|
{uploaded.map((row) => (
|
|
<PhasedUploadedFileRow
|
|
key={row.key}
|
|
label={row.label}
|
|
file={row.file!}
|
|
onView={onViewFile}
|
|
onDownload={onDownloadFile}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
) : null}
|
|
|
|
<Paper withBorder radius="md" p="md" bg="var(--mantine-color-gray-0)">
|
|
<Group gap={8} mb="sm" wrap="nowrap">
|
|
<Box
|
|
c="edr-green"
|
|
style={{
|
|
width: 32,
|
|
height: 32,
|
|
borderRadius: 8,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
background: "var(--mantine-color-edr-green-1)",
|
|
}}
|
|
>
|
|
<FileText size={16} />
|
|
</Box>
|
|
<Box>
|
|
<Text size="sm" fw={700}>
|
|
{replaceMode ? "Replace document" : "Upload document"}
|
|
</Text>
|
|
<Text size="xs" c="dimmed">
|
|
{helperText}
|
|
</Text>
|
|
</Box>
|
|
</Group>
|
|
|
|
<Stack gap="md">
|
|
{extraFields}
|
|
{fields.map((f) => (
|
|
<PhasedFileDropzone
|
|
key={f.key}
|
|
label={multiField ? f.label : "Choose file"}
|
|
description={
|
|
multiField
|
|
? uploaded.some((u) => u.key === f.key)
|
|
? "Drop a new file to replace the current one."
|
|
: `Upload ${f.label} (optional if another declaration type is provided).`
|
|
: undefined
|
|
}
|
|
value={files[f.key] ?? null}
|
|
onChange={(file) => onChange(f.key, file)}
|
|
replaceMode={replaceMode || uploaded.some((u) => u.key === f.key)}
|
|
onPreview={onViewFile}
|
|
/>
|
|
))}
|
|
</Stack>
|
|
</Paper>
|
|
|
|
<Button
|
|
color="edr-green"
|
|
loading={loading}
|
|
disabled={disabled || !hasStaged}
|
|
leftSection={<Upload size={16} />}
|
|
onClick={onSubmit}
|
|
fullWidth
|
|
>
|
|
{submitLabel ?? (replaceMode ? "Replace document" : "Upload document")}
|
|
</Button>
|
|
</Stack>
|
|
);
|
|
}
|