first commit
This commit is contained in:
159
src/components/onboarding/DocumentUploadStep.jsx
Normal file
159
src/components/onboarding/DocumentUploadStep.jsx
Normal file
@@ -0,0 +1,159 @@
|
||||
import React, { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Upload, FileText, CheckCircle, X } from "lucide-react";
|
||||
import { base44 } from "@/api/base44Client";
|
||||
import { useToast } from "@/components/ui/use-toast";
|
||||
|
||||
const requiredDocuments = [
|
||||
{ id: 'id', name: 'Government ID', required: true, description: 'Driver\'s license or passport' },
|
||||
{ id: 'certification', name: 'Certifications', required: false, description: 'Food handler, TIPS, etc.' },
|
||||
{ id: 'background_check', name: 'Background Check', required: false, description: 'If available' },
|
||||
];
|
||||
|
||||
export default function DocumentUploadStep({ data, onNext, onBack }) {
|
||||
const [documents, setDocuments] = useState(data || []);
|
||||
const [uploading, setUploading] = useState({});
|
||||
const { toast } = useToast();
|
||||
|
||||
const handleFileUpload = async (docType, file) => {
|
||||
if (!file) return;
|
||||
|
||||
setUploading(prev => ({ ...prev, [docType]: true }));
|
||||
|
||||
try {
|
||||
const { file_url } = await base44.integrations.Core.UploadFile({ file });
|
||||
|
||||
const newDoc = {
|
||||
type: docType,
|
||||
name: file.name,
|
||||
url: file_url,
|
||||
uploaded_at: new Date().toISOString(),
|
||||
};
|
||||
|
||||
setDocuments(prev => {
|
||||
const filtered = prev.filter(d => d.type !== docType);
|
||||
return [...filtered, newDoc];
|
||||
});
|
||||
|
||||
toast({
|
||||
title: "✅ Document Uploaded",
|
||||
description: `${file.name} uploaded successfully`,
|
||||
});
|
||||
} catch (error) {
|
||||
toast({
|
||||
title: "❌ Upload Failed",
|
||||
description: error.message,
|
||||
variant: "destructive",
|
||||
});
|
||||
} finally {
|
||||
setUploading(prev => ({ ...prev, [docType]: false }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveDocument = (docType) => {
|
||||
setDocuments(prev => prev.filter(d => d.type !== docType));
|
||||
};
|
||||
|
||||
const handleNext = () => {
|
||||
const hasRequiredDocs = requiredDocuments
|
||||
.filter(doc => doc.required)
|
||||
.every(doc => documents.some(d => d.type === doc.id));
|
||||
|
||||
if (!hasRequiredDocs) {
|
||||
toast({
|
||||
title: "⚠️ Missing Required Documents",
|
||||
description: "Please upload all required documents before continuing",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
onNext({ type: 'documents', data: documents });
|
||||
};
|
||||
|
||||
const getUploadedDoc = (docType) => documents.find(d => d.type === docType);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h2 className="text-xl font-bold text-slate-900 mb-2">Document Upload</h2>
|
||||
<p className="text-sm text-slate-500">Upload required documents for compliance</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-4">
|
||||
{requiredDocuments.map(doc => {
|
||||
const uploadedDoc = getUploadedDoc(doc.id);
|
||||
const isUploading = uploading[doc.id];
|
||||
|
||||
return (
|
||||
<Card key={doc.id} className={uploadedDoc ? "border-green-500 bg-green-50" : ""}>
|
||||
<CardContent className="p-4">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
<Label className="font-semibold">
|
||||
{doc.name}
|
||||
{doc.required && <span className="text-red-500 ml-1">*</span>}
|
||||
</Label>
|
||||
{uploadedDoc && (
|
||||
<CheckCircle className="w-5 h-5 text-green-600" />
|
||||
)}
|
||||
</div>
|
||||
<p className="text-sm text-slate-500">{doc.description}</p>
|
||||
|
||||
{uploadedDoc && (
|
||||
<div className="mt-2 flex items-center gap-2">
|
||||
<FileText className="w-4 h-4 text-slate-500" />
|
||||
<span className="text-sm text-slate-700">{uploadedDoc.name}</span>
|
||||
<button
|
||||
onClick={() => handleRemoveDocument(doc.id)}
|
||||
className="ml-2 text-red-500 hover:text-red-700"
|
||||
>
|
||||
<X className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label
|
||||
htmlFor={`upload-${doc.id}`}
|
||||
className={`cursor-pointer inline-flex items-center px-4 py-2 rounded-lg ${
|
||||
uploadedDoc
|
||||
? "bg-green-100 text-green-700 hover:bg-green-200"
|
||||
: "bg-blue-100 text-blue-700 hover:bg-blue-200"
|
||||
} transition-colors`}
|
||||
>
|
||||
<Upload className="w-4 h-4 mr-2" />
|
||||
{isUploading ? "Uploading..." : uploadedDoc ? "Replace" : "Upload"}
|
||||
</label>
|
||||
<input
|
||||
id={`upload-${doc.id}`}
|
||||
type="file"
|
||||
className="hidden"
|
||||
accept=".pdf,.jpg,.jpeg,.png"
|
||||
onChange={(e) => handleFileUpload(doc.id, e.target.files[0])}
|
||||
disabled={isUploading}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
<div className="flex justify-between pt-4">
|
||||
<Button variant="outline" onClick={onBack}>
|
||||
Back
|
||||
</Button>
|
||||
<Button onClick={handleNext} className="bg-[#0A39DF]">
|
||||
Continue to Training
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user