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:
Manus
2026-01-17 18:30:13 -05:00
parent a203b70e27
commit a79132e921
4 changed files with 175 additions and 24 deletions

View File

@@ -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}>