Checkpoint: Trois améliorations majeures du champ formateur :

 **1. Affichage dans le tableau des séquences**
- Nouvelle colonne "Formateur" ajoutée au tableau de gestion
- Affiche le nom du formateur ou "-" si non renseigné

 **2. Liste prédéfinie avec autocomplétion**
- Nouvelle table `formateurs` dans la base de données
- Procédures tRPC (list, create, delete) pour gérer les formateurs
- Composant `FormateurCombobox` avec autocomplétion intelligente
- Possibilité d'ajouter rapidement un nouveau formateur depuis le formulaire
- Recherche en temps réel parmi les formateurs existants

 **3. Inclusion dans les emails d'invitation**
- Le nom du formateur est maintenant affiché dans les emails de confirmation d'inscription
- Affichage conditionnel (uniquement si un formateur est renseigné)
- Améliore la communication avec les apprenants

Ces améliorations standardisent la gestion des formateurs, évitent les doublons/fautes de frappe, et enrichissent l'information transmise aux apprenants.
This commit is contained in:
Manus Sandbox
2025-11-19 09:38:06 -05:00
parent 703d0e4961
commit 16351ea3d5
11 changed files with 1095 additions and 9 deletions

View File

@@ -5,6 +5,7 @@ import {
users,
formations,
apprenants,
formateurs,
sequences,
datesFormation,
inscriptions,
@@ -18,10 +19,12 @@ import {
InsertInscription,
InsertFormation,
InsertApprenant,
InsertFormateur,
Sequence,
DateFormation,
Apprenant,
Formation,
Formateur,
EmailTemplate,
InsertEmailTemplate
} from "../drizzle/schema";
@@ -656,3 +659,37 @@ export async function updateEmailConfig(id: number, data: Partial<InsertEmailCon
})
.where(eq(emailConfig.id, id));
}
// ===== FORMATEURS =====
/**
* Récupérer tous les formateurs actifs
*/
export async function getFormateurs() {
const db = await getDb();
if (!db) return [];
const result = await db.select().from(formateurs).where(eq(formateurs.actif, true));
return result;
}
/**
* Créer un nouveau formateur
*/
export async function createFormateur(data: InsertFormateur) {
const db = await getDb();
if (!db) return null;
const result = await db.insert(formateurs).values(data);
return result;
}
/**
* Supprimer un formateur (désactivation logique)
*/
export async function deleteFormateur(id: number) {
const db = await getDb();
if (!db) return;
await db.update(formateurs).set({ actif: false }).where(eq(formateurs.id, id));
}

View File

@@ -45,6 +45,7 @@ export async function sendInscriptionConfirmation(params: {
sequenceNom: string;
dates: Array<{ dateDebut: Date; dateFin: Date; ordre: number }>;
lieu: string;
formateur?: string;
statut: 'confirmee' | 'liste_attente';
}): Promise<boolean> {
const isConfirmed = params.statut === 'confirmee';
@@ -88,6 +89,7 @@ export async function sendInscriptionConfirmation(params: {
<p><strong>Formation :</strong> ${params.formationNom}</p>
<p><strong>Séquence :</strong> ${params.sequenceNom}</p>
<p><strong>Lieu :</strong> ${params.lieu}</p>
${params.formateur ? `<p><strong>Formateur :</strong> ${params.formateur}</p>` : ''}
<h4>Dates de formation (${params.dates.length} séance${params.dates.length > 1 ? 's' : ''}) :</h4>
${datesHTML}
</div>

View File

@@ -72,6 +72,25 @@ export const appRouter = router({
}),
}),
// ===== FORMATEURS =====
formateurs: router({
list: adminProcedure.query(async () => {
return db.getFormateurs();
}),
create: adminProcedure.input(z.object({
nom: z.string().min(1),
})).mutation(async ({ input }) => {
await db.createFormateur(input);
return { success: true };
}),
delete: adminProcedure.input(z.object({ id: z.number() })).mutation(async ({ input }) => {
await db.deleteFormateur(input.id);
return { success: true };
}),
}),
// ===== APPRENANTS =====
apprenants: router({
list: adminProcedure.query(async () => {
@@ -330,6 +349,7 @@ export const appRouter = router({
ordre: d.ordre,
})),
lieu: inscriptionSequence.lieu,
formateur: inscriptionSequence.formateur || undefined,
statut,
});
}