espace-paie-odentas/components/staff/UploadDocumentModal.tsx

260 lines
8.4 KiB
TypeScript

"use client";
import { useState } from "react";
import { X, Upload, Loader2, CheckCircle } from "lucide-react";
import { toast } from "sonner";
interface UploadDocumentModalProps {
isOpen: boolean;
onClose: () => void;
matricule: string;
salarieName: string;
onSuccess: () => void;
}
const DOCUMENT_TYPES = [
{ value: "piece_identite", label: "Pièce d'identité" },
{ value: "attestation_secu", label: "Attestation Sécurité Sociale" },
{ value: "rib", label: "RIB" },
{ value: "medecine_travail", label: "Attestation médecine du travail" },
{ value: "contrat_travail", label: "Contrat de travail" },
{ value: "diplome", label: "Diplôme" },
{ value: "justificatif", label: "Justificatif" },
{ value: "autre", label: "Autre document" },
];
export default function UploadDocumentModal({
isOpen,
onClose,
matricule,
salarieName,
onSuccess,
}: UploadDocumentModalProps) {
const [documentType, setDocumentType] = useState("piece_identite");
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [uploading, setUploading] = useState(false);
const [isDragging, setIsDragging] = useState(false);
if (!isOpen) return null;
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
validateAndSetFile(file);
}
};
const validateAndSetFile = (file: File) => {
const validTypes = ['application/pdf', 'image/jpeg', 'image/jpg', 'image/png'];
const maxSize = 10 * 1024 * 1024; // 10MB
if (!validTypes.includes(file.type)) {
toast.error('Type de fichier non autorisé. Utilisez PDF, JPG ou PNG.');
return;
}
if (file.size > maxSize) {
toast.error('Fichier trop volumineux. Taille maximum : 10 MB.');
return;
}
setSelectedFile(file);
};
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setIsDragging(true);
};
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setIsDragging(false);
};
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
e.preventDefault();
setIsDragging(false);
if (e.dataTransfer.files && e.dataTransfer.files[0]) {
const file = e.dataTransfer.files[0];
validateAndSetFile(file);
}
};
const handleUpload = async () => {
if (!selectedFile) {
toast.error("Veuillez sélectionner un fichier");
return;
}
setUploading(true);
try {
const formData = new FormData();
formData.append('file', selectedFile);
formData.append('matricule', matricule);
formData.append('type', documentType);
const response = await fetch('/api/staff/salaries/documents/upload', {
method: 'POST',
body: formData,
});
if (!response.ok) {
const error = await response.json();
throw new Error(error.error || 'Erreur lors du téléchargement');
}
toast.success('Document uploadé avec succès');
onSuccess();
handleClose();
} catch (error) {
console.error('Erreur upload:', error);
toast.error(error instanceof Error ? error.message : 'Erreur lors du téléchargement');
} finally {
setUploading(false);
}
};
const handleClose = () => {
setSelectedFile(null);
setDocumentType("piece_identite");
setIsDragging(false);
onClose();
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-black/50">
<div className="relative w-full max-w-md bg-white rounded-2xl shadow-xl">
{/* Header */}
<div className="flex items-center justify-between p-5 border-b">
<div>
<h2 className="text-lg font-semibold text-slate-800">
Ajouter un document
</h2>
<p className="text-sm text-slate-600 mt-1">
Pour {salarieName} ({matricule})
</p>
</div>
<button
onClick={handleClose}
disabled={uploading}
className="p-2 hover:bg-slate-100 rounded-lg transition-colors disabled:opacity-50"
>
<X className="size-5 text-slate-500" />
</button>
</div>
{/* Body */}
<div className="p-5 space-y-4">
{/* Type de document */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Type de document
</label>
<select
value={documentType}
onChange={(e) => setDocumentType(e.target.value)}
disabled={uploading}
className="w-full px-3 py-2 border rounded-lg text-sm focus:ring-2 focus:ring-blue-500 focus:border-transparent disabled:opacity-50"
>
{DOCUMENT_TYPES.map((type) => (
<option key={type.value} value={type.value}>
{type.label}
</option>
))}
</select>
</div>
{/* Upload zone */}
<div>
<label className="block text-sm font-medium text-slate-700 mb-2">
Fichier
</label>
{selectedFile ? (
<div className="flex items-center justify-between p-4 bg-green-50 border-2 border-green-200 rounded-lg">
<div className="flex items-center gap-3">
<CheckCircle className="size-5 text-green-600" />
<div>
<p className="text-sm font-medium text-green-800">
{selectedFile.name}
</p>
<p className="text-xs text-green-600">
{(selectedFile.size / 1024).toFixed(1)} Ko
</p>
</div>
</div>
<button
onClick={() => setSelectedFile(null)}
disabled={uploading}
className="p-1 hover:bg-green-100 rounded transition-colors disabled:opacity-50"
>
<X className="size-4 text-green-700" />
</button>
</div>
) : (
<div
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
className={`relative border-2 border-dashed rounded-lg p-8 text-center transition-colors ${
isDragging
? 'border-blue-500 bg-blue-50'
: 'border-slate-300 hover:border-slate-400'
}`}
>
<input
type="file"
onChange={handleFileChange}
disabled={uploading}
accept=".pdf,.jpg,.jpeg,.png"
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer disabled:cursor-not-allowed"
/>
<Upload className={`size-8 mx-auto mb-2 ${
isDragging ? 'text-blue-600' : 'text-slate-400'
}`} />
<p className="text-sm text-slate-600 mb-1">
{isDragging
? 'Déposez le fichier ici'
: 'Glissez un fichier ou cliquez pour choisir'}
</p>
<p className="text-xs text-slate-500">
PDF, JPG, PNG (max 10MB)
</p>
</div>
)}
</div>
</div>
{/* Footer */}
<div className="flex items-center justify-end gap-3 p-5 border-t bg-slate-50 rounded-b-2xl">
<button
onClick={handleClose}
disabled={uploading}
className="px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-200 rounded-lg transition-colors disabled:opacity-50"
>
Annuler
</button>
<button
onClick={handleUpload}
disabled={!selectedFile || uploading}
className="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 rounded-lg transition-colors disabled:opacity-50 disabled:cursor-not-allowed flex items-center gap-2"
>
{uploading ? (
<>
<Loader2 className="size-4 animate-spin" />
Upload en cours...
</>
) : (
<>
<Upload className="size-4" />
Uploader
</>
)}
</button>
</div>
</div>
</div>
);
}