Architettura
Cosa fa
TrainingHub è una piattaforma .NET 10 / Blazor Server multi-tenant per la gestione della formazione sulla sicurezza sul lavoro.
Componenti principali
TrainingHub.BackOffice— Blazor Server web app (UI principale)TrainingHub.Database— progetto SQL Server (DACPAC deployment)TrainingHub.Import— console app per import massivi da ExcelTrainingHub.Shared— shared project (models/costanti)
Framework 3SD
L'app usa l'ecosistema interno 3SD: Scarnas, Brighela, Oss, Servel, Ploc, Oster, Mulet, DeFa, KSet, Mola, Tabiot.
Pattern ricorrenti
View SQL → CRUD auto-generato (read-only)
Per esporre dati aggregati o derivati senza scrivere componenti UI custom si usa il pattern vista SQL + CRUD generato:
- Creare una vista in
TrainingHub.Database/<schema>/Views/vw_<nome>.sql, esponendo le colonne con i loro identificatori*Id(no*Label: il generator costruisce le FK). - Allineare il database (deploy DACPAC).
- MCP
3sd-generator(generator_runcondefaultsCompiler) produce ilmodel.conf.jsone ildxgrid.conf.jsonpartendo dalla view. - Configurare a mano nel
dxgrid.conf.json:- FK manuali sulle colonne
*Id(la view non ha vincoli FK):FkSchema,FkTableName,FkClassName, eventualeCellLink+CellPolicy. AllowInsert,AllowUpdate,AllowDelete,AllowClone=false.IgnoreFormPopup = true+IgnoreForm = true(evita di generare popup/form per un'entità non scrivibile).
- FK manuali sulle colonne
- Rigenerare: si ottiene un
<Entity>.razorstandard usabile come grid stand-alone o come step nei wizard.
Esempi attuali:
edu.vw_workerTrainingStatus— stato per-formazione di ogni lavoratore.job.vw_workerComplianceSummary— rollup per-lavoratore (stato peggiore + conteggi). Usata come step "Conformità formativa" nel wizard azienda.
Persistenza markup custom in conf.json
Il generator rigenera .razor e .razor.tt.cs ad ogni run: ciò che
viene scritto a mano nel .razor viene perso. I dxgrid.conf.json
offrono diversi hook per persistere markup custom:
| Hook | Posizione resa | Quando usarlo |
|---|---|---|
FormPopup.MarkupBeforeForm / MarkupAfterForm | Prima/dopo <EditForm> nel popup | Wizard, banner contestuali |
FormPopup.MarkupEnd | Dopo </DxPopup> | Popup secondari (anteprima, conferme) |
Columns.{name}.RenderFragmentParameters.CellDisplayTemplate | Cella della grid | Badge stato, link condizionali |
Form.TabPages.Tabs.{tab}.Groups.{group}.Columns.{col}.Template | Sostituisce l'editor del campo nel form | Wrap con badge, override editor |
Regola pratica: prima di scrivere markup custom direttamente nel
.razor, verificare se esiste un hook in conf.json; se manca, va
aggiunto al generator piuttosto che accettare la perdita su regen.
⚠️ Domande aperte / debito tecnico
- (Da popolare durante la scrittura della doc dominio
inv)