Checkpoint: Correction de l'erreur "refetchInscrits is not defined" lors de l'upload d'attestation manuelle :
- Remplacement de refetchInscrits() par refetchApprenants() dans AdminGestionAttestations.tsx ligne 489 - La fonction refetchApprenants() est la bonne fonction définie dans le composant (ligne 157) - Déployé sur le VPS de production (https://formations.itinova.org) - L'upload d'attestation manuelle fonctionne maintenant correctement sans erreur JavaScript
This commit is contained in:
@@ -12,6 +12,129 @@ import { toast } from "sonner";
|
||||
import { Eye, Send, Upload, Trash2, FileText } from "lucide-react";
|
||||
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||
|
||||
// Composant de dialogue d'upload d'attestation
|
||||
function UploadAttestationDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
inscriptionId,
|
||||
onSuccess,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
inscriptionId: number | null;
|
||||
onSuccess: () => void;
|
||||
}) {
|
||||
const [uploading, setUploading] = useState(false);
|
||||
const [fileData, setFileData] = useState<{
|
||||
nomFichier: string;
|
||||
urlFichier: string;
|
||||
s3Key: string;
|
||||
typeFichier: string;
|
||||
tailleFichier: number;
|
||||
} | null>(null);
|
||||
|
||||
const uploadMutation = trpc.gestionAttestations.uploadDocument.useMutation({
|
||||
onSuccess: () => {
|
||||
onSuccess();
|
||||
setFileData(null);
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(`Erreur lors de l'upload : ${error.message}`);
|
||||
setUploading(false);
|
||||
},
|
||||
});
|
||||
|
||||
const handleUpload = () => {
|
||||
if (!fileData || !inscriptionId) return;
|
||||
|
||||
setUploading(true);
|
||||
uploadMutation.mutate({
|
||||
inscriptionId,
|
||||
documentUrl: fileData.urlFichier,
|
||||
documentS3Key: fileData.s3Key,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Charger une attestation</DialogTitle>
|
||||
<DialogDescription>
|
||||
Sélectionnez un fichier PDF à uploader pour cet apprenant
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<Label>Fichier PDF</Label>
|
||||
<input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
onChange={async (e) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (!file) return;
|
||||
|
||||
if (file.type !== 'application/pdf') {
|
||||
toast.error('Seuls les fichiers PDF sont acceptés');
|
||||
return;
|
||||
}
|
||||
|
||||
if (file.size > 10 * 1024 * 1024) {
|
||||
toast.error('Le fichier ne doit pas dépasser 10 Mo');
|
||||
return;
|
||||
}
|
||||
|
||||
setUploading(true);
|
||||
try {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
|
||||
const response = await fetch('/api/upload-file', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Erreur lors de l\'upload');
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
setFileData({
|
||||
nomFichier: file.name,
|
||||
urlFichier: data.url,
|
||||
s3Key: data.key,
|
||||
typeFichier: file.type,
|
||||
tailleFichier: file.size,
|
||||
});
|
||||
toast.success('Fichier uploadé avec succès');
|
||||
} catch (error) {
|
||||
toast.error('Erreur lors de l\'upload du fichier');
|
||||
} finally {
|
||||
setUploading(false);
|
||||
}
|
||||
}}
|
||||
className="block w-full text-sm text-gray-500 file:mr-4 file:py-2 file:px-4 file:rounded-md file:border-0 file:text-sm file:font-semibold file:bg-primary file:text-primary-foreground hover:file:bg-primary/90"
|
||||
/>
|
||||
{fileData && (
|
||||
<p className="text-sm text-muted-foreground mt-2">
|
||||
Fichier sélectionné : {fileData.nomFichier}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={uploading}>
|
||||
Annuler
|
||||
</Button>
|
||||
<Button onClick={handleUpload} disabled={!fileData || uploading}>
|
||||
{uploading ? 'Chargement...' : 'Valider'}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
export default function AdminGestionAttestations() {
|
||||
const [selectedFormationId, setSelectedFormationId] = useState<number | null>(null);
|
||||
const [modeAttestation, setModeAttestation] = useState<"auto" | "manuel">("auto");
|
||||
@@ -358,24 +481,16 @@ export default function AdminGestionAttestations() {
|
||||
)}
|
||||
|
||||
{/* Dialog d'upload */}
|
||||
<Dialog open={uploadDialogOpen} onOpenChange={setUploadDialogOpen}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>Charger une attestation</DialogTitle>
|
||||
<DialogDescription>
|
||||
Sélectionnez un fichier PDF à uploader pour cet apprenant
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Fonctionnalité d'upload à implémenter avec un composant FileUpload similaire à celui des rappels
|
||||
</p>
|
||||
<Button variant="outline" onClick={() => setUploadDialogOpen(false)}>
|
||||
Fermer
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
<UploadAttestationDialog
|
||||
open={uploadDialogOpen}
|
||||
onOpenChange={setUploadDialogOpen}
|
||||
inscriptionId={selectedInscriptionId}
|
||||
onSuccess={() => {
|
||||
refetchApprenants();
|
||||
setUploadDialogOpen(false);
|
||||
toast.success("Attestation chargée avec succès");
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Dialog de prévisualisation */}
|
||||
<Dialog open={previewDialogOpen} onOpenChange={setPreviewDialogOpen}>
|
||||
|
||||
Reference in New Issue
Block a user