espace-paie-odentas/components/staff/contracts/ManualSignedContractUpload.tsx
odentas a58d4982e6 fix: Corriger les noms de colonnes dans upload-signed-pdf
- organization_id -> org_id
- api_name -> name (puis slugification)
- Ajouter logs détaillés pour debug
- Améliorer la gestion des cas où org_id est null
2025-10-22 18:10:34 +02:00

201 lines
6.3 KiB
TypeScript

// components/staff/contracts/ManualSignedContractUpload.tsx
"use client";
import { useState } from "react";
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { toast } from "sonner";
import { Upload, FileText, Loader2, CheckCircle2, AlertCircle } from "lucide-react";
interface ManualSignedContractUploadProps {
contractId: string;
contractNumber: string;
open: boolean;
onOpenChange: (open: boolean) => void;
onSuccess?: () => void;
}
export function ManualSignedContractUpload({
contractId,
contractNumber,
open,
onOpenChange,
onSuccess,
}: ManualSignedContractUploadProps) {
const [selectedFile, setSelectedFile] = useState<File | null>(null);
const [uploading, setUploading] = useState(false);
const [uploadSuccess, setUploadSuccess] = useState(false);
const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
if (file) {
if (file.type !== "application/pdf") {
toast.error("Le fichier doit être un PDF");
return;
}
if (file.size > 10 * 1024 * 1024) {
toast.error("Le fichier ne doit pas dépasser 10 Mo");
return;
}
setSelectedFile(file);
setUploadSuccess(false);
}
};
const handleUpload = async () => {
if (!selectedFile) {
toast.error("Veuillez sélectionner un fichier PDF");
return;
}
console.log("📤 [UPLOAD CLIENT] Début upload pour contrat:", contractId);
setUploading(true);
try {
const formData = new FormData();
formData.append("file", selectedFile);
const url = `/api/staff/contrats/${contractId}/upload-signed-pdf`;
console.log("📤 [UPLOAD CLIENT] URL de l'API:", url);
const response = await fetch(url, {
method: "POST",
body: formData,
});
console.log("📤 [UPLOAD CLIENT] Réponse reçue, status:", response.status);
if (!response.ok) {
const errorData = await response.json();
console.error("❌ [UPLOAD CLIENT] Erreur:", errorData);
throw new Error(errorData.error || "Erreur lors de l'upload");
}
const result = await response.json();
console.log("✅ [UPLOAD CLIENT] Upload réussi:", result);
setUploadSuccess(true);
toast.success("Contrat signé uploadé avec succès");
// Attendre 1 seconde puis fermer et notifier le succès
setTimeout(() => {
onOpenChange(false);
if (onSuccess) {
onSuccess();
}
// Réinitialiser l'état
setSelectedFile(null);
setUploadSuccess(false);
}, 1000);
} catch (error) {
console.error("Erreur lors de l'upload:", error);
toast.error(error instanceof Error ? error.message : "Erreur lors de l'upload");
} finally {
setUploading(false);
}
};
const handleClose = () => {
if (!uploading) {
setSelectedFile(null);
setUploadSuccess(false);
onOpenChange(false);
}
};
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center gap-2">
<Upload className="size-5" />
Upload manuel du contrat signé
</DialogTitle>
</DialogHeader>
<div className="space-y-4 py-4">
<div className="text-sm text-muted-foreground">
<p>Contrat: <span className="font-medium text-foreground">{contractNumber}</span></p>
<p className="mt-2">
Uploadez le PDF du contrat signé. Il sera stocké dans S3 au même emplacement
qu'un contrat signé via Docuseal.
</p>
</div>
<div className="space-y-3">
<label
htmlFor="pdf-upload"
className="flex flex-col items-center justify-center w-full h-32 border-2 border-dashed border-gray-300 rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100 transition-colors"
>
<div className="flex flex-col items-center justify-center pt-5 pb-6">
{selectedFile ? (
<>
<FileText className="size-8 text-green-600 mb-2" />
<p className="text-sm text-gray-600">
{selectedFile.name}
</p>
<p className="text-xs text-gray-500">
{(selectedFile.size / 1024 / 1024).toFixed(2)} Mo
</p>
</>
) : (
<>
<Upload className="size-8 text-gray-400 mb-2" />
<p className="text-sm text-gray-600">
Cliquez pour sélectionner un PDF
</p>
<p className="text-xs text-gray-500">
Maximum 10 Mo
</p>
</>
)}
</div>
<input
id="pdf-upload"
type="file"
accept="application/pdf"
className="hidden"
onChange={handleFileChange}
disabled={uploading || uploadSuccess}
/>
</label>
{uploadSuccess && (
<div className="flex items-center gap-2 text-sm text-green-600 bg-green-50 p-3 rounded-lg">
<CheckCircle2 className="size-4" />
<span>Upload réussi</span>
</div>
)}
</div>
<div className="flex justify-end gap-2 pt-2">
<Button
variant="outline"
onClick={handleClose}
disabled={uploading}
>
Annuler
</Button>
<Button
onClick={handleUpload}
disabled={!selectedFile || uploading || uploadSuccess}
>
{uploading ? (
<>
<Loader2 className="size-4 mr-2 animate-spin" />
Upload en cours...
</>
) : (
<>
<Upload className="size-4 mr-2" />
Uploader
</>
)}
</Button>
</div>
</div>
</DialogContent>
</Dialog>
);
}