Checkpoint: Ajout de l'authentification formateur et upload logo/signature pour attestations

## Authentification formateur
- Ajout du rôle "formateur" dans l'enum de la table users
- Ajout du champ formateurId pour lier un compte à un formateur
- Mise à jour de l'interface AdminUsers avec sélection du formateur
- Redirection automatique selon le rôle (admin → /admin, formateur → /formateur)
- Badges et filtres mis à jour pour inclure le rôle formateur

## Upload logo et signature
- Création du composant ImageUpload.tsx avec prévisualisation
- Création de l'endpoint /api/upload-image avec multer pour l'upload vers S3
- Intégration dans AdminAttestations.tsx
- Modification du service PDF pour inclure le logo (en haut à droite) et la signature (en bas)
- Mise à jour des procédures tRPC pour accepter logoUrl, logoS3Key, signatureUrl, signatureS3Key
This commit is contained in:
Manus
2026-01-07 10:34:53 -05:00
parent 023711df92
commit 6484e3e892
15 changed files with 2524 additions and 23 deletions

View File

@@ -79,7 +79,19 @@ export async function genererAttestationPDF(inscriptionId: number): Promise<{ s3
doc.on("error", reject);
// Construction du PDF
try {
(async () => {
try {
// Logo en haut à droite si disponible
if (config?.logoUrl) {
try {
const logoResponse = await fetch(config.logoUrl);
const logoBuffer = Buffer.from(await logoResponse.arrayBuffer());
doc.image(logoBuffer, doc.page.width - 150, 50, { width: 100 });
} catch (error) {
console.error("Erreur lors du chargement du logo:", error);
}
}
// Titre
doc.fontSize(24).font("Helvetica-Bold").text("ATTESTATION DE FORMATION", { align: "center" });
doc.moveDown(2);
@@ -125,18 +137,33 @@ export async function genererAttestationPDF(inscriptionId: number): Promise<{ s3
doc.text(`Fait le ${format(new Date(), "dd MMMM yyyy", { locale: fr })}`, { align: "right" });
doc.moveDown(3);
if (config?.nomSignataire) {
doc.text(config.nomSignataire, { align: "right" });
}
if (config?.fonctionSignataire) {
doc.text(config.fonctionSignataire, { align: "right" });
}
// Signature si disponible
if (config?.signatureUrl) {
try {
const signatureResponse = await fetch(config.signatureUrl);
const signatureBuffer = Buffer.from(await signatureResponse.arrayBuffer());
const signatureX = doc.page.width - 200;
const signatureY = doc.y;
doc.image(signatureBuffer, signatureX, signatureY, { width: 150, height: 50 });
doc.moveDown(3);
} catch (error) {
console.error("Erreur lors du chargement de la signature:", error);
}
}
doc.end();
} catch (error) {
doc.end();
reject(error);
}
if (config?.nomSignataire) {
doc.text(config.nomSignataire, { align: "right" });
}
if (config?.fonctionSignataire) {
doc.text(config.fonctionSignataire, { align: "right" });
}
doc.end();
} catch (error) {
doc.end();
reject(error);
}
})();
});
}