File size: 7,208 Bytes
5301ae9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 | import React, { useState, useRef } from 'react';
import { Upload, FileSpreadsheet, CheckCircle2, X, Users } from 'lucide-react';
import { Button } from "@/components/ui/button";
import { motion, AnimatePresence } from 'framer-motion';
export default function UploadStep({ onFileUploaded, uploadedFile, onRemoveFile }) {
const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef(null);
const handleDragOver = (e) => {
e.preventDefault();
setIsDragging(true);
};
const handleDragLeave = (e) => {
e.preventDefault();
setIsDragging(false);
};
const handleDrop = async (e) => {
e.preventDefault();
setIsDragging(false);
const file = e.dataTransfer.files[0];
if (file && file.name.endsWith('.csv')) {
await handleFileUpload(file);
}
};
const handleFileSelect = async (e) => {
const file = e.target.files[0];
if (file && file.name.endsWith('.csv')) {
await handleFileUpload(file);
}
};
const handleFileUpload = async (file) => {
const formData = new FormData();
formData.append('file', file);
try {
const response = await fetch('/api/upload-csv', {
method: 'POST',
body: formData,
});
if (response.ok) {
const data = await response.json();
onFileUploaded({
name: file.name,
size: file.size,
contactCount: data.contact_count,
fileId: data.file_id
});
} else {
alert('Failed to upload file. Please try again.');
}
} catch (error) {
console.error('Upload error:', error);
alert('Error uploading file. Please try again.');
}
};
return (
<div className="w-full">
<AnimatePresence mode="wait">
{!uploadedFile ? (
<motion.div
key="upload"
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
exit={{ opacity: 0, y: -10 }}
transition={{ duration: 0.3 }}
>
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onClick={() => fileInputRef.current?.click()}
className={`
relative cursor-pointer rounded-2xl border-2 border-dashed
transition-all duration-300 ease-out
${isDragging
? 'border-violet-500 bg-violet-50 scale-[1.02]'
: 'border-slate-200 bg-slate-50/50 hover:border-violet-300 hover:bg-violet-50/30'
}
`}
>
<div className="flex flex-col items-center justify-center py-16 px-8">
<div className={`
mb-6 rounded-2xl p-4 transition-all duration-300
${isDragging ? 'bg-violet-100' : 'bg-white shadow-sm'}
`}>
<Upload className={`
h-8 w-8 transition-colors duration-300
${isDragging ? 'text-violet-600' : 'text-slate-400'}
`} />
</div>
<h3 className="text-lg font-semibold text-slate-800 mb-2">
Upload your Apollo CSV
</h3>
<p className="text-sm text-slate-500 text-center max-w-sm">
Drag and drop your contact list here, or click to browse
</p>
<div className="mt-6 flex items-center gap-2 text-xs text-slate-400">
<FileSpreadsheet className="h-4 w-4" />
<span>Supports .csv files exported from Apollo</span>
</div>
</div>
<input
ref={fileInputRef}
type="file"
accept=".csv"
onChange={handleFileSelect}
className="hidden"
/>
</div>
</motion.div>
) : (
<motion.div
key="uploaded"
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
exit={{ opacity: 0, scale: 0.95 }}
transition={{ duration: 0.3 }}
className="rounded-2xl border border-green-200 bg-gradient-to-br from-green-50 to-emerald-50 p-6"
>
<div className="flex items-start justify-between">
<div className="flex items-start gap-4">
<div className="rounded-xl bg-green-100 p-3">
<CheckCircle2 className="h-6 w-6 text-green-600" />
</div>
<div>
<h3 className="font-semibold text-slate-800 mb-1">
{uploadedFile.name}
</h3>
<div className="flex items-center gap-4 text-sm text-slate-500">
<span>{(uploadedFile.size / 1024).toFixed(1)} KB</span>
<div className="flex items-center gap-1.5 text-green-600 font-medium">
<Users className="h-4 w-4" />
<span>{uploadedFile.contactCount} contacts found</span>
</div>
</div>
</div>
</div>
<Button
variant="ghost"
size="icon"
onClick={onRemoveFile}
className="h-8 w-8 text-slate-400 hover:text-red-500 hover:bg-red-50"
>
<X className="h-4 w-4" />
</Button>
</div>
</motion.div>
)}
</AnimatePresence>
</div>
);
}
|