Files
edr-platform/apps/edr-freight-web/portal/src/pages/documents/NewDocumentPage.tsx
2026-05-18 23:23:51 +03:00

243 lines
8.0 KiB
TypeScript

import { useState, type ReactNode } from "react";
import { FileUp, Hash } from "lucide-react";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button";
import { Textarea } from "@/components/ui/textarea";
import { bookings } from "../bookings/bookings.mock";
import { consignments } from "../consignments/consignments.mock";
import { customers } from "../customers/customers.mock";
import type {
DocumentLinkType,
DocumentStatus,
DocumentType,
} from "./documents.mock";
export interface DocumentFormData {
name?: string;
type?: DocumentType;
status?: DocumentStatus;
linkedType?: DocumentLinkType;
linkedReference?: string;
notes?: string;
}
export interface NewDocumentPageProps {
mode?: "create" | "edit";
document?: DocumentFormData;
children?: ReactNode;
}
const selectClass =
"flex h-10 w-full rounded-md border border-slate-200 bg-white px-3 py-2 text-sm text-slate-700 shadow-xs outline-none transition hover:border-slate-300 focus:border-[#10B981]/50 focus:ring-2 focus:ring-[#10B981]/20";
export default function NewDocumentPage({
mode = "create",
document,
children,
}: NewDocumentPageProps = {}) {
const isEdit = mode === "edit";
const title = isEdit ? "Edit Document" : "Upload Document";
const description = isEdit
? "Update document metadata."
: "Upload a freight document and link it to a booking, consignment, or invoice.";
const submitLabel = isEdit ? "Save Changes" : "Upload";
const [linkedType, setLinkedType] = useState<DocumentLinkType>(
document?.linkedType ?? "Booking",
);
const [fileName, setFileName] = useState<string>("");
const referenceOptions = (() => {
if (linkedType === "Booking") {
return bookings.map((b) => ({
value: b.reference,
label: `${b.reference}${b.customer}`,
}));
}
if (linkedType === "Consignment") {
return consignments.map((c) => ({
value: c.trackingNumber,
label: `${c.trackingNumber}${c.customer}`,
}));
}
if (linkedType === "Customer") {
return customers.map((c) => ({
value: c.company,
label: c.company,
}));
}
return [] as Array<{ value: string; label: string }>;
})();
return (
<Dialog>
<DialogTrigger asChild>
{children ?? <Button>{isEdit ? "Edit" : "Upload Document"}</Button>}
</DialogTrigger>
<DialogContent className="max-h-[90vh] overflow-y-auto sm:max-w-3xl rounded-3xl">
<DialogHeader>
<DialogTitle className="text-2xl font-bold">{title}</DialogTitle>
<DialogDescription>{description}</DialogDescription>
</DialogHeader>
<div className="grid gap-5 py-4 md:grid-cols-2">
{/* File picker — drop zone */}
{!isEdit ? (
<div className="md:col-span-2">
<Label>File</Label>
<label
htmlFor="document-file-input"
className="mt-1 flex cursor-pointer flex-col items-center justify-center gap-2 rounded-2xl border-2 border-dashed border-slate-200 bg-[#10B981]/5 p-8 text-center transition hover:border-[#10B981]/40 hover:bg-[#10B981]/10"
>
<div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[#10B981] text-white">
<FileUp className="h-6 w-6" />
</div>
<p className="text-sm font-medium text-slate-900">
{fileName || "Click to choose a file or drag it here"}
</p>
<p className="text-xs text-slate-500">
PDF, DOCX, XLSX, PNG, JPG · max 25 MB
</p>
<input
id="document-file-input"
type="file"
className="hidden"
accept=".pdf,.docx,.xlsx,.png,.jpg,.jpeg"
onChange={(e) =>
setFileName(e.target.files?.[0]?.name ?? "")
}
/>
</label>
</div>
) : null}
{/* Document Name */}
<div className="space-y-2">
<Label>Document Name *</Label>
<div className="relative">
<Hash className="absolute left-3 top-3 h-4 w-4 text-slate-400" />
<Input
defaultValue={document?.name ?? ""}
placeholder="e.g. bill-of-lading-bk-026003.pdf"
className="pl-10"
/>
</div>
</div>
{/* Document Type */}
<div className="space-y-2">
<Label>Document Type *</Label>
<select
defaultValue={document?.type ?? "Bill of Lading"}
className={selectClass}
>
<option>Bill of Lading</option>
<option>Commercial Invoice</option>
<option>Packing List</option>
<option>Customs Declaration</option>
<option>Certificate of Origin</option>
<option>Insurance Certificate</option>
<option>Delivery Receipt</option>
<option>Proof of Delivery</option>
<option>Contract</option>
<option>Other</option>
</select>
</div>
{/* Linked To */}
<div className="space-y-2">
<Label>Linked To</Label>
<select
value={linkedType}
onChange={(e) =>
setLinkedType(e.target.value as DocumentLinkType)
}
className={selectClass}
>
<option>Booking</option>
<option>Consignment</option>
<option>Shipment</option>
<option>Customer</option>
<option>Invoice</option>
<option>None</option>
</select>
</div>
{/* Reference */}
<div className="space-y-2">
<Label>Reference</Label>
{referenceOptions.length > 0 ? (
<select
defaultValue={document?.linkedReference ?? ""}
className={selectClass}
>
<option value="" disabled>
Select {linkedType.toLowerCase()}
</option>
{referenceOptions.map((opt) => (
<option key={opt.value} value={opt.value}>
{opt.label}
</option>
))}
</select>
) : (
<Input
defaultValue={document?.linkedReference ?? ""}
placeholder={
linkedType === "None"
? "Not linked"
: `Enter ${linkedType.toLowerCase()} reference`
}
disabled={linkedType === "None"}
/>
)}
</div>
{/* Status */}
<div className="space-y-2">
<Label>Status</Label>
<select
defaultValue={document?.status ?? "Draft"}
className={selectClass}
>
<option>Draft</option>
<option>Pending Review</option>
<option>Approved</option>
<option>Rejected</option>
<option>Expired</option>
</select>
</div>
{/* Notes */}
<div className="space-y-2 md:col-span-2">
<Label>Notes</Label>
<Textarea
defaultValue={document?.notes ?? ""}
placeholder="Any extra context for this document..."
/>
</div>
</div>
<div className="flex justify-end gap-3">
<Button variant="outline">Cancel</Button>
<Button className="bg-[#10B981] text-white hover:bg-[#10B981]/90">
{submitLabel}
</Button>
</div>
</DialogContent>
</Dialog>
);
}