Checkpoint: Tableau de bord personnalisé pour formateurs avec statistiques (total séquences, séquences à venir, total apprenants) et liste des prochaines interventions avec détails (dates, lieu, capacité). Redirection automatique des formateurs vers /formateur au lieu de /admin. Notifications automatiques par email pour les formateurs lors de nouvelles inscriptions, annulations, capacité maximale atteinte et places disponibles. Menus Configuration, Traçabilité et Analyses masqués pour les formateurs.
This commit is contained in:
@@ -267,3 +267,136 @@ export async function getDetailSequence(sequenceId: number, formateurId: number)
|
||||
dates,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les statistiques du tableau de bord pour un formateur
|
||||
*/
|
||||
export async function getFormateurDashboardStats(formateurId: number) {
|
||||
const db = await getDb();
|
||||
if (!db) throw new Error("Database not available");
|
||||
|
||||
// Nombre total de séquences du formateur
|
||||
const totalSequences = await db
|
||||
.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(sequences)
|
||||
.where(eq(sequences.formateurId, formateurId));
|
||||
|
||||
// Nombre total d'inscrits confirmés à toutes les séquences du formateur
|
||||
const totalInscrits = await db
|
||||
.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(inscriptions)
|
||||
.innerJoin(sequences, eq(inscriptions.sequenceId, sequences.id))
|
||||
.where(
|
||||
and(
|
||||
eq(sequences.formateurId, formateurId),
|
||||
eq(inscriptions.statut, 'confirmee')
|
||||
)
|
||||
);
|
||||
|
||||
// Nombre de séquences à venir (ayant au moins une date future)
|
||||
const today = new Date();
|
||||
const sequencesAvenir = await db
|
||||
.select({ sequenceId: datesFormation.sequenceId })
|
||||
.from(datesFormation)
|
||||
.innerJoin(sequences, eq(datesFormation.sequenceId, sequences.id))
|
||||
.where(
|
||||
and(
|
||||
eq(sequences.formateurId, formateurId),
|
||||
gte(datesFormation.dateDebut, today)
|
||||
)
|
||||
)
|
||||
.groupBy(datesFormation.sequenceId);
|
||||
|
||||
return {
|
||||
totalSequences: totalSequences[0]?.count || 0,
|
||||
totalInscrits: totalInscrits[0]?.count || 0,
|
||||
sequencesAvenir: sequencesAvenir.length,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les prochaines séquences du formateur avec leurs détails
|
||||
*/
|
||||
export async function getFormateurProchainesSequences(formateurId: number, limit: number = 10) {
|
||||
const db = await getDb();
|
||||
if (!db) return [];
|
||||
|
||||
const today = new Date();
|
||||
|
||||
// Récupérer les séquences du formateur ayant au moins une date future
|
||||
const seqs = await db
|
||||
.select()
|
||||
.from(sequences)
|
||||
.where(eq(sequences.formateurId, formateurId));
|
||||
|
||||
// Pour chaque séquence, récupérer les détails
|
||||
const sequencesAvecDetails = await Promise.all(
|
||||
seqs.map(async (seq) => {
|
||||
// Récupérer toutes les dates de la séquence
|
||||
const dates = await db
|
||||
.select()
|
||||
.from(datesFormation)
|
||||
.where(eq(datesFormation.sequenceId, seq.id))
|
||||
.orderBy(datesFormation.dateDebut);
|
||||
|
||||
// Vérifier si au moins une date est future
|
||||
const hasFutureDate = dates.some(d => new Date(d.dateDebut) >= today);
|
||||
|
||||
if (!hasFutureDate) return null;
|
||||
|
||||
// Récupérer la formation
|
||||
const formation = await db
|
||||
.select()
|
||||
.from(formations)
|
||||
.where(eq(formations.id, seq.formationId))
|
||||
.limit(1);
|
||||
|
||||
// Compter les inscrits confirmés
|
||||
const inscritsCount = await db
|
||||
.select({ count: sql<number>`COUNT(*)` })
|
||||
.from(inscriptions)
|
||||
.where(
|
||||
and(
|
||||
eq(inscriptions.sequenceId, seq.id),
|
||||
eq(inscriptions.statut, 'confirmee')
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
...seq,
|
||||
formation: formation[0] || null,
|
||||
dates,
|
||||
nbInscrits: inscritsCount[0]?.count || 0,
|
||||
prochaineDateDebut: dates.find(d => new Date(d.dateDebut) >= today)?.dateDebut || dates[0]?.dateDebut,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Filtrer les séquences nulles et trier par prochaine date
|
||||
const sequencesFiltrees = sequencesAvecDetails
|
||||
.filter(s => s !== null)
|
||||
.sort((a, b) => {
|
||||
const dateA = new Date(a!.prochaineDateDebut);
|
||||
const dateB = new Date(b!.prochaineDateDebut);
|
||||
return dateA.getTime() - dateB.getTime();
|
||||
})
|
||||
.slice(0, limit);
|
||||
|
||||
return sequencesFiltrees;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer l'email du formateur à partir de son ID
|
||||
*/
|
||||
export async function getFormateurEmailById(formateurId: number): Promise<string | null> {
|
||||
const db = await getDb();
|
||||
if (!db) return null;
|
||||
|
||||
const result = await db
|
||||
.select({ email: formateurs.email })
|
||||
.from(formateurs)
|
||||
.where(eq(formateurs.id, formateurId))
|
||||
.limit(1);
|
||||
|
||||
return result[0]?.email || null;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user