Passa al contenuto principale

Componenti UI — dominio edu

🎯 Cosa fa

Sotto TrainingHub.BackOffice/Components/CRUD/edu/ vivono i componenti Blazor CRUD auto-generati per le ~38 entità del dominio formazione. Il pattern di generazione è lo stesso di inv e reg (triade razor + razor.tt.cs + razor.cs + Forms/ + FormPopups/). Vedi componenti UI inv per i dettagli.

Questa pagina documenta le particolarità edu-specifiche.

🗺️ Entità core in scope

EntitàFileNote
CategoryCategory.razorAnagrafica semplice
CourseCourse.razorForm ricco con cascade variante normativa
LessonLesson.razorAnagrafica con durata
LessonsCourseLessonsCourse.razorAssociazione con metadata programma
CourseSessionCourseSession.razorCiclo vita stati
AppointmentAppointment.razor (grid CRUD) + Pages/AppointmentsCalendar/ (vista calendario page-level)Doppia vista griglia + calendario
LocationLocation.razorAule con FK a reg.headquarters
TeacherTeacher.razorAnagrafica docente
AppointmentsTeacherAppointmentsTeacher.razorM:N con hourlyAmount
TeacherCostTeacherCost.razorCosti per organizer
TeacherSkillTeacherSkill.razorCompetenze docente

🧩 Pattern chiave edu-specifici

Vista calendario appuntamenti

Pages/AppointmentsCalendar/AppointmentsCalendar.razor (route /appointments-calendar) è la vista calendario principale degli appuntamenti, complementare alla griglia standard Appointment.razor. Scritta a mano (non CRUD-generata). Sub-componenti:

  • CalendarGrid.razor — rendering mese/settimana/giorno.
  • AppointmentDetailPanel.razor — pannello laterale con dettagli appuntamento + contesto sessione + azioni Edit/Duplica/Modifica sessione.

Caratteristiche principali (per dettaglio vedi panoramica edu — Vista calendario custom):

  • Filtri Corso/Sede/Docente/Sessione con cascade Corso → Sessione.
  • Colore appuntamento derivato da trainingTopics.color (primo topic alfabetico della session).
  • Click slot apre AppointmentFormPopup; pulsante "Nuova Sessione" apre SessionPlannerPopup.
  • Conflict detection via ISessionPlannerService.DetectConflictsAsync con toast warning post-save.
  • Query param ?teacherId=<guid> per landing dal teacher-calendar.

⚠️ Esiste ancora un legacy CRUD/edu/AppointmentsCalendar.razor auto-generato: è la vecchia vista CRUD, mantenuta come placeholder finché i conf JSON non vengono ripuliti. Quella autoritativa è sotto Pages/AppointmentsCalendar/.

Wizard pianificazione sessione

Components/edu/SessionPlanner/SessionPlannerPopup.razor è un wizard 6-step per pianificare una sessione formativa end-to-end. Le partial class SessionPlannerPopup.Step1Session.csStep6Review.cs contengono la logica per-step.

Il backend è ISessionPlannerService (in Shared/Services/SessionPlanner/) con stato persistito su SessionPlannerState + SessionCoursePlan[].

Step:

  1. Sessione — data inizio + argomento opzionale (filtra corsi).
  2. Corsi — aggiunge uno o più corsi in co-erogazione con finestre orarie per-corso.
  3. Date e sedi — appuntamenti fisici e sede.
  4. Docenti — assegnazione con "Suggerisci docenti per skill" (filtro su teacherSkills del corso).
  5. Iscritti — aggiunta workers con scelta del corso di destinazione; integrazione con coda richieste e scadenziario.
  6. Riepilogo costi — stima Iscrizioni + Docenze + Aule prima del confirm finale.

Punti d'ingresso: lista corsi, lista sessioni, coda richieste, scadenziario, scheda lavoratore, calendario (pulsante "Nuova Sessione").

Vista dati aggregati

AppointmentsData.razor fornisce una vista di dati aggregati per analisi (totale ore per docente, saturazione aule, ecc.), distinta dal CRUD. Non è un CRUD: è una pagina di reportistica.

Componenti analitici cross-dominio

  • ActiveWorker.razor — vista lavoratori attivi (incrocia reg + edu)
  • AllCompletion.razor — completamenti aggregati
  • AllRequiredTraining.razor — formazione obbligatoria per lavoratore

Questi componenti non sono CRUD generati: sono viste analitiche custom che consumano dati edu + job + reg. Tecnicamente violano la separazione per dominio (stanno in CRUD/edu/ ma toccano altri domini).

Cascade variante normativa in Course

Il form CourseForm.razor ha un combobox Variante normativa che, al cambio, pre-popola campi come durata minima e obblighi (logica nel .razor.cs). Pattern simile al cascade ATECO → riskLevel del form aziende.

Location con indirizzo inline e FK a reg.companies

Il form LocationForm.razor espone i campi indirizzo direttamente sull'aula (formattedAddress + geocoded country/province/city/zipCode/address/streetNumber/latitude/longitude) e un combobox opzionale companyId per il gestore aula (azienda proprietaria, NULL = aula 3SD). Cross-dominio: edu consuma reg per le aziende gestrici.

Suggerimento docenti per skill

Il filtro per competenza non vive più sul form AppointmentForm.razor (l'appuntamento non ha più una lezione associata): lo step "Docenti" del wizard SessionPlannerPopup.Step4Teachers usa il pulsante "Suggerisci docenti per skill" che incrocia edu.teacherSkills.trainingVariantId con le varianti dei corsi erogati nella sessione (trainingSessionsCourses → courses → trainingVariantId).

📁 File chiave

  • Components/CRUD/edu/Course.razor.cs — code-behind principale
  • Components/CRUD/edu/Forms/CourseForm.razor — form con cascade variante
  • Components/Pages/AppointmentsCalendar/ — vista calendario page-level (AppointmentsCalendar.razor, CalendarGrid.razor, AppointmentDetailPanel.razor)
  • Components/edu/SessionPlanner/ — wizard 6-step pianificazione sessione (SessionPlannerPopup.razor + Step1..Step6.cs)
  • Components/CRUD/edu/AppointmentsData.razor — vista dati aggregati
  • Components/CRUD/edu/ActiveWorker.razor, AllCompletion.razor, AllRequiredTraining.razor — viste cross-dominio
  • Components/CRUD/edu/_conf/*.dxgrid.conf.json — configurazione

🔌 Estensione tipica

Segue il pattern generale (vedi componenti UI inv). Specificità edu:

  • Aggiungere un campo a Course. Rigenera + valuta se il cascade variante va aggiornato per precompilare anche il nuovo campo.
  • Nuova vista analitica cross-dominio. Aggiungi una nuova pagina Razor non-CRUD in CRUD/edu/ (es. MyAnalytics.razor), inject i service necessari. Non serve conf.json. Routing dichiarato con @page direttiva.
  • Estendere vista calendario. I file in Pages/AppointmentsCalendar/ sono scritti a mano — non CRUD generato. Modifiche dirette senza rigenerazione. Per aggiungere un filtro nuovo: aggiungi il combobox in AppointmentsCalendar.razor, il backing field nel code-behind, e passa il valore a IAppointmentsCalendarService.GetAppointmentsAsync.

⚠️ Debito tecnico

  • Cross-dominio in CRUD/edu/. ActiveWorker, AllCompletion, AllRequiredTraining vivono in CRUD edu ma aggregano dati reg/job/edu. Valutare spostamento in cartella dedicata CRUD/analytics/ o simile.
  • AppointmentsCalendar senza test UI. Componente complesso custom, non coperto da test automatici.
  • Duplicazione calendario CRUD vs Pages. Esistono ancora CRUD/edu/AppointmentsCalendar.razor (legacy CRUD-generato) e Pages/AppointmentsCalendar/AppointmentsCalendar.razor (autoritativo page-level). La legacy va droppata quando i conf JSON CRUD vengono ripuliti (vedi BACKLOG.md "Drop partial classes alias").
  • Filtro docenti per competenza. Coperto dallo step 4 del SessionPlannerPopup ("Suggerisci docenti per skill") che usa la skill matrix edu.teacherSkills.

🔗 Vedi anche