Dominio edu — Panoramica sviluppatore
🎯 Cosa fa
Il dominio edu (education / training) è il cuore applicativo di
TrainingHub: catalogo formativo, erogazione dei corsi, partecipanti,
certificati, qualifiche, nomine. Ha ~38 tabelle — le più di tutto
il solution.
Questa pagina copre il core A3 (11 tabelle): catalogo + erogazione + docenti. Le restanti ~27 tabelle sono elencate come "fuori scope" e saranno documentate in spec successive.
🗺️ Mappa moduli
Database — TrainingHub.Database/edu/
Core in questa documentazione (12):
| Area | Tabella | Ruolo |
|---|---|---|
| Catalogo | edu.categories | Categorie tematiche (piatte) |
| Catalogo | edu.courses | Definizione corso (FK trainingVariants, organizers) |
| Erogazione | edu.trainingSessions | Sessione formativa: blocco fisico (giornata/giornate). Stati: planned/open/inProgress/completed/cancelled |
| Erogazione | edu.trainingSessionsCourses | M:N session↔corso (co-erogazione: una giornata può ospitare più corsi con finestre orarie distinte) |
| Erogazione | edu.trainingSessionsTeachers | M:N session↔docente (default ereditato sugli appointment) |
| Erogazione | edu.appointments | Occorrenza fisica all'interno di una session (FK trainingSessions NOT NULL, locations override) |
| Erogazione | edu.locations | Aule (indirizzo inline; FK opzionale reg.companies come gestore) |
| Erogazione | edu.locationsTrainingVariants | M:N aula↔variant per filtrare aule abilitate alle prove pratiche (variant con requiresEquippedRoom = 1) |
| Docenti | edu.teachers | Anagrafica docenti |
| Docenti | edu.appointmentsTeachers | M:N appuntamento↔docente (override su default trainingSessionsTeachers, con hourlyAmount override) |
| Docenti | edu.teacherCosts | Costo orario per docente per organizzatore |
| Docenti | edu.teacherSkills | Skill matrix docente↔variant normativa (trainingVariantId, score) |
Cambio modello del 2026-05-08 (edu-sessions-redesign):
- Le tabelle
edu.lessons,edu.lessonsCourseseedu.courseSessionsArgumentssono state droppate.edu.courseSessionsè stata rinominataedu.trainingSessionsconDATETIMEinvece diDATEe l'aggiunta dilocationIdereditato dagli appointment.- La relazione 1:1
courseSession → courseè stata sostituita dalla M:NtrainingSessionsCoursesper supportare la co-erogazione (più corsi nella stessa giornata fisica).edu.appointmentArgumentsè stata rinominataedu.trainingSessionsCoursesArguments(FK atrainingSessionsCourses).edu.teacherSkills.lessonIdè stato sostituito datrainingVariantId(granularità a variant).- Spec di riferimento:
docs/superpowers/specs/_archive/2026-05-08-edu-sessions-redesign-design.md.
Estensioni coperte lato utente (dettaglio dev prioritizzato come lavoro futuro):
| Area | Tabelle principali | Pagina utente |
|---|---|---|
| Worker training journey | workersTrainings, workerTrainingDetails, trainingDetailAttempts, attendances, workerCompletionsCache, certificates, enrollmentCosts, priorTrainings | Scheda formativa lavoratore |
| Argomenti formativi | trainingArguments, trainingTopics, trainingVariants, variantTopicMap, trainingTopicDependencies, trainingSessionsCoursesArguments, trainingArgumentChecks, trainingVariantsOverlaps, appointmentChecks | Argomenti e varianti |
| Nomine | nominations, workersNominations, nominationsTrainingTopics | Nomine e incarichi |
| Ruoli & qualifiche | rolesRequiredTrainings, rolesForbiddenTrainings, qualificationsExemptions, qualificationsRequiredTrainings | Ruoli e requisiti |
| Attrezzature & credenziali | equipments, workerEquipments, workerCredentials | Attrezzature |
Lo schema DB dettagliato di queste tabelle estensione non è incluso in questa sezione: Schema DB copre solo le 11 core. Documentazione dev estesa pianificata come lavoro futuro.
Service layer — TrainingHub.Shared/Services/
| File | Ruolo |
|---|---|
ITrainingExpirationService.cs / TrainingExpirationService.cs | Calcola compliance formativa per azienda/lavoratore: attestati in scadenza, mancanti, statistiche |
IRiskInheritanceService.cs / RiskInheritanceService.cs | Propagazione livello di rischio tra azienda, mansione e lavoratore |
SessionPlanner/ISessionPlannerService.cs / SessionPlannerService.cs | Orchestrazione wizard 6-step (sessione + corsi + appuntamenti + iscritti + docenti + costi) e rilevamento conflitti (DetectConflictsAsync) usati anche dal calendario al salvataggio appuntamento |
IAppointmentsCalendarService.cs / AppointmentsCalendarService.cs | Data provider del calendario: GetAppointmentsAsync con filtri Corso/Sede/Docente/Sessione + range temporale, GetSessionContextAsync per il pannello dettaglio |
I primi due sono in TrainingHub.Shared perché usati anche da TrainingHub.Import oltre che dalla BackOffice. Il SessionPlanner è in Shared perché i suoi tipi (SessionPlannerState, SessionCoursePlan, ComplianceEnrollmentRequest/Result) sono consumati anche dai test.
QueryModifiers — TrainingHub.BackOffice/Services/QueryModifiers/edu/
| File | Ruolo |
|---|---|
TeachersQueryModifier.cs | Hook CRUD teachers |
PriorTrainingsQueryModifier.cs | Hook CRUD priorTrainings (fuori scope) |
TrainingVariantsQueryModifier.cs | Hook CRUD trainingVariants (fuori scope) |
TrainingVariantsOverlapsQueryModifier.cs | Hook CRUD trainingVariantsOverlaps (fuori scope) |
WorkerTrainingDetailsQueryModifier.cs | Hook CRUD workerTrainingDetails (fuori scope) |
UI CRUD — TrainingHub.BackOffice/Components/CRUD/edu/
~38 entità CRUD auto-generate. Pattern identico a inv e reg (vedi
componenti UI inv). Le 12 core di
questa spec: Category, Course, TrainingSession,
TrainingSessionsCourse, TrainingSessionsTeacher, Appointment,
Location, LocationsTrainingVariant, Teacher, AppointmentsTeacher,
TeacherCost, TeacherSkill.
Componenti specifici non standard:
Pages/AppointmentsCalendar/— pagina calendario appuntamenti scritta a mano (route/appointments-calendar). Sub-componenti:AppointmentsCalendar.razor(host),CalendarGrid.razor(vista mese/settimana/giorno),AppointmentDetailPanel.razor(pannello laterale). Integrata conSessionPlannerPopup(pulsante "Nuova Sessione") eAppointmentFormPopup(click su slot libero).Components/edu/SessionPlanner/— wizard 6-step di pianificazione sessione formativa (SessionPlannerPopup.razor+ Step1Session/ Step2Courses/Step3Schedule/Step4Teachers/Step5Workers/Step6Review).CRUD/edu/AppointmentsCalendar.razor— versione CRUD-generata legacy della vista calendario; mantenuta finché i conf JSON non vengono ripuliti, da droppare poi.AppointmentsData.razor— vista dati appuntamenti aggregataActiveWorker.razor,AllCompletion.razor,AllRequiredTraining.razor— componenti analitici cross-dominio
🔧 API pubblica
ITrainingExpirationService
public interface ITrainingExpirationService
{
Task<IEnumerable<trainingExpiration>> GetExpiringTrainingsAsync(
Guid? companyId = null,
string? status = null);
Task<ComplianceStats> GetComplianceStatsAsync(Guid? companyId = null);
}
public record ComplianceStats(
int TotalWorkers,
int CompliantWorkers,
int ExpiredCount,
int ExpiringCount,
int MissingCount);
Usato da Company.razor.cs (dominio reg) per il pannello di
compliance aggregata. Lo stato status filtra su ok, expiring,
expired, missing (vedi WorkerTrainingStatusValue in Enums.cs).
IRiskInheritanceService
Propaga rischi azienda → mansione → lavoratore. Invocato da
CompaniesQueryModifier (dominio reg) in cascade su update aziende.
🧩 Pattern chiave
Vista calendario custom
Pages/AppointmentsCalendar/AppointmentsCalendar.razor è una vista
calendario degli appuntamenti scritta a mano (non CRUD-generata).
Caratteristiche:
- Viste Mese / Settimana / Giorno con navigazione previous/Oggi/next.
- Filtri Corso / Sede / Docente / Sessione (combobox riempiti da
SimpleCRUD.GetListAsync). Il filtro Corso restringe a cascata l'elenco Sessioni via sub-query suedu.trainingSessionsCourses. - Colore appuntamento dal primo trainingTopic (alfabetico) della
session, derivato via
edu.trainingSessionsCourses → edu.courses → edu.trainingVariants.trainingTopicId → edu.trainingTopics.color. Background pastello viacolor-mixCSS, fallback grigio se topic senza colore. Legenda topic dinamica + legenda stati. - Click su slot libero → apre
AppointmentFormPopupper nuovo appuntamento; click su appuntamento →AppointmentDetailPanelcon azioni Edit / Duplica / Modifica sessione. - Pulsante "Nuova Sessione" apre
SessionPlannerPopup; al confirm ricarica gli appuntamenti del periodo. - Dopo il salvataggio di un appuntamento chiama
ISessionPlannerService.DetectConflictsAsynce mostra toast warning per ogniConflictKindrilevato (docente sovrapposto, sede occupata, iscritti oltre capienza, soglia docente, ecc.). - Query param
?teacherId=<guid>pre-popola il filtro docente (landing da Calendario docente).
Pattern utile da replicare per entità con forte dimensione temporale.
Cache materializzata
workerCompletionsCache (fuori scope) è una tabella materializzata
che aggrega i completamenti formativi per lavoratore. Refreshata da
QueryModifiers dopo operazioni che invalidano il cache (vedi
retrospettiva per riferimenti a lavori recenti sul cache).
Service di dominio per orchestrazione complessa
Per i CRUD semplici la logica vive in QueryModifier (TeachersQueryModifier)
e in .razor.cs custom. Per i flussi complessi sono stati estratti:
ISessionPlannerService— orchestrazione wizard 6-step (sessione + corsi + appuntamenti + iscritti + docenti + costi) + rilevamento conflitti (DetectConflictsAsyncritornaConflictKindenum conteacher_double_booked,location_double_booked,enrollment_overflow,teacher_threshold,worker_double_enrolled,lesson_skipped).IAppointmentsCalendarService— data provider per la vista calendario.ITrainingExpirationService— compliance/scadenze.
📦 Dipendenze
Brighela.SimpleCRUD— CRUD base + QueryModifierOss.Filters,Tabiot.Blazor.*,DevExpress.Blazor— UI stack comune
Cross-dominio:
edu.courses.organizerSlug→reg.organizers(slug)edu.locations.companyId→reg.companies(id)(opzionale)edu.teacherCosts.organizerSlug→reg.organizers(slug)edu.courses.trainingVariantId→edu.trainingVariants(id)(fuori scope)
📁 File chiave
Database/edu/Tables/*.sql(~38)BackOffice/Components/CRUD/edu/*.razor{,.cs,.tt.cs}BackOffice/Services/QueryModifiers/edu/*.csShared/Services/ITrainingExpirationService.cs,IRiskInheritanceService.cs(+ implementazioni)Shared/Services/SessionPlanner/—ISessionPlannerService.cs,SessionPlannerService.cs,SessionPlannerState.cs,SessionCoursePlan.cs,ComplianceEnrollmentRequest/Result.csShared/Services/IAppointmentsCalendarService.cs+BackOffice/Services/AppointmentsCalendarService.csBackOffice/Components/Pages/AppointmentsCalendar/— vista calendario custom (AppointmentsCalendar.razor,CalendarGrid.razor,AppointmentDetailPanel.razor)BackOffice/Components/edu/SessionPlanner/— wizard 6-step (SessionPlannerPopup.razor+ Step1..Step6 partial classes)
⚠️ Domande aperte / debito tecnico
-
trainingVariantIdobbligatorio ma variante fuori scope. Ogni corso richiede una variante normativa; la gestione delle varianti è in un altro sottodominio complesso (overlap, dipendenze). Creare corso senza aver prima configurato variante è bloccato. Ordine logico di setup da documentare. -
. Risolto in edu-sessions-redesign 2026-05-08: ora èappointments.courseSessionIdnullabletrainingSessionIdNOT NULL. - Cache
workerCompletionsCachenon atomica. Aggiornata da trigger/hook vari: coerenza in caso di failure non chiara. -
Validazione vincoli propedeutici. Risolto in edu-sessions-redesign 2026-05-08 droppandolessonsCourseslessonsCourses(i non è chiaro dove (in quale service/query) queste vengano applicate al flusso iscrizione-completamento. -
Auto-generazione appuntamenti da sessione. Coperto dalSessionPlannerService(wizard 6-step): la sessione viene creata come bozza, i corsi erogati e gli appuntamenti vengono aggiunti via gli step del wizard.
🔗 Vedi anche
- Schema DB
- Componenti UI
- Logica applicativa
- Aggiungere un campo
- Guida utente: Panoramica formazione (docs-site-user)
- Dominio
reg: panoramica (aziende che iscrivono lavoratori) - Dominio
inv: panoramica (appuntamenti fatturati)