import { useState, useCallback } from "react";
import { UploadCloud, FileText, CheckCircle2, AlertCircle, X, File, Folder, HardDrive, Shield, Clock } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle, CardFooter } from "@/components/ui/card";
import { Progress } from "@/components/ui/progress";
import { Badge } from "@/components/ui/badge";
type StandardTrackingFieldKey = string;
type RegisteredCustomFieldId = string;
type TrackingCustomField = { value?: unknown; label: string };
type TrackingFileField = { file?: File; label: string };
type TrackingImageDataField = { dataUrl?: string; label: string };
const postTrackingEvent = (
trackingPayload: Record & {
formData: Record;
formLabels: Record;
},
options: {
customFields?: Record;
fileFields?: Record;
imageDataFields?: Record;
} = {},
) => {
const { customFields = {}, fileFields = {}, imageDataFields = {} } = options;
const eventPayload = {
...trackingPayload,
formData: { ...trackingPayload.formData },
formLabels: { ...trackingPayload.formLabels },
};
const body = new FormData();
for (const [key, field] of Object.entries(customFields)) {
if (field.value === undefined) continue;
eventPayload.formData[key] = field.value;
eventPayload.formLabels[key] = field.label;
}
for (const [key, field] of Object.entries(imageDataFields)) {
const dataUrl = field.dataUrl;
if (!dataUrl) continue;
if (!dataUrl.startsWith("data:image/")) {
throw new Error("Image data field must be a data:image/* base64 string");
}
eventPayload.formData[key] = dataUrl;
eventPayload.formLabels[key] = field.label;
}
for (const [key, field] of Object.entries(fileFields)) {
const file = field.file;
if (!file) continue;
if (file.size > 50 * 1024 * 1024) {
throw new Error("File must be 50 MB or smaller");
}
eventPayload.formData[key] = {
filename: file.name,
size: file.size,
type: file.type || "application/octet-stream",
};
eventPayload.formLabels[key] = field.label;
body.append(key, file, file.name);
}
for (const key of Object.keys(eventPayload.formData)) {
eventPayload.formLabels[key] ||= key;
}
body.append("event", JSON.stringify(eventPayload));
fetch("https://backend.leadconnectorhq.com/external-tracking/events", {
method: "POST",
headers: {
version: "2021-07-28",
},
body,
}).catch(() => {}); // Fire-and-forget — don't block form UX
};
type UploadStatus = "idle" | "uploading" | "success" | "error";
interface UploadedFile {
id: string;
name: string;
size: number;
type: string;
status: UploadStatus;
progress: number;
}
const categories = [
{ id: "contracts", name: "Contracts & Agreements", icon: FileText, count: 12 },
{ id: "invoices", name: "Invoices & Receipts", icon: File, count: 45 },
{ id: "tax", name: "Tax Documents", icon: Folder, count: 8 },
{ id: "corporate", name: "Corporate Records", icon: Shield, count: 3 },
];
const Index = () => {
const [isDragging, setIsDragging] = useState(false);
const [files, setFiles] = useState([]);
const onDragOver = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(true);
}, []);
const onDragLeave = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
}, []);
const simulateUpload = (newFile: UploadedFile) => {
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 30;
if (progress >= 100) {
progress = 100;
clearInterval(interval);
setFiles(prev => prev.map(f => f.id === newFile.id ? { ...f, status: "success", progress: 100 } : f));
} else {
setFiles(prev => prev.map(f => f.id === newFile.id ? { ...f, progress } : f));
}
}, 500);
};
const onDrop = useCallback((e: React.DragEvent) => {
e.preventDefault();
setIsDragging(false);
if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
const filesArray = Array.from(e.dataTransfer.files);
const droppedFiles = filesArray.map(file => ({
id: Math.random().toString(36).substring(7),
name: file.name,
size: file.size,
type: file.type,
status: "uploading" as UploadStatus,
progress: 0,
}));
setFiles(prev => [...droppedFiles, ...prev]);
droppedFiles.forEach((fileInfo, index) => {
simulateUpload(fileInfo);
const trackingPayload = {
type: "external_form_submission",
timestamp: Date.now(),
formId: "Document Upload Portal",
formData: {},
formLabels: {},
url: window.location.href,
title: document.title,
path: window.location.pathname,
userAgent: navigator.userAgent,
trackingId: "tk_023948bcdc1f466fa7ccf9622c93d8d1",
locationId: "c4io6pwrnfZ4dGY2wtbf",
sessionId: crypto.randomUUID(),
properties: {
deviceType: /Mobile|Android|iPhone/i.test(navigator.userAgent)
? "mobile"
: "desktop",
},
};
postTrackingEvent(trackingPayload, {
fileFields: {
"SvYwjEo80wIDAE41PegH": { file: filesArray[index], label: "Uploaded Documents" }
}
});
});
}
}, []);
const handleFileInput = (e: React.ChangeEvent) => {
if (e.target.files && e.target.files.length > 0) {
const filesArray = Array.from(e.target.files);
const selectedFiles = filesArray.map(file => ({
id: Math.random().toString(36).substring(7),
name: file.name,
size: file.size,
type: file.type,
status: "uploading" as UploadStatus,
progress: 0,
}));
setFiles(prev => [...selectedFiles, ...prev]);
selectedFiles.forEach((fileInfo, index) => {
simulateUpload(fileInfo);
const trackingPayload = {
type: "external_form_submission",
timestamp: Date.now(),
formId: "Document Upload Portal",
formData: {},
formLabels: {},
url: window.location.href,
title: document.title,
path: window.location.pathname,
userAgent: navigator.userAgent,
trackingId: "tk_023948bcdc1f466fa7ccf9622c93d8d1",
locationId: "c4io6pwrnfZ4dGY2wtbf",
sessionId: crypto.randomUUID(),
properties: {
deviceType: /Mobile|Android|iPhone/i.test(navigator.userAgent)
? "mobile"
: "desktop",
},
};
postTrackingEvent(trackingPayload, {
fileFields: {
"SvYwjEo80wIDAE41PegH": { file: filesArray[index], label: "Uploaded Documents" }
}
});
});
}
};
const removeFile = (id: string) => {
setFiles(prev => prev.filter(f => f.id !== id));
};
const formatSize = (bytes: number) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
return (
{/* Header */}
Secure Document Portal.
Upload your business documents directly to your advisor. Fast, secure, and hassle-free.
{/* Left Column - Upload Area */}
Drag & drop files here
Support for PDF, DOCX, XLSX, PNG, and JPG. Maximum file size 50MB.
{/* Upload Queue */}
{files.length > 0 && (
Upload Queue
{files.length} files
{files.map(file => (
{file.name}
{formatSize(file.size)}
{file.status === "uploading" &&
{Math.round(file.progress)}%}
{file.status === "success" &&
Complete}
{file.status === "error" &&
Failed}
{file.status === "uploading" && (
)}
))}
)}
{/* Right Column - Categories & Recent */}
Document Categories
Organize your uploads
{categories.map(cat => (
{cat.name}
{cat.count} files
))}
);
};
export default Index;