Componenti UI — dominio inv
🎯 Cosa fa
Sotto TrainingHub.BackOffice/Components/CRUD/inv/ vivono i componenti
Blazor CRUD auto-generati (tramite MCP 3sd-generator) per le 9 entità
del dominio fatturazione. Ogni entità ha la stessa triade di file
(grid + form + popup) più un code-behind custom per logica non
generabile.
🗺️ Entità
| Entità | Uso |
|---|---|
Invoice | Pagina principale fattura (con wizard 5 passi) |
InvoiceLine | Grid/form delle righe fattura — usato inline nel wizard |
InvoicePayment | Grid/form rate di pagamento — usato inline nel wizard |
InvoiceAppointment | Associazioni fattura ↔ appuntamenti (gestita internamente al wizard) |
InvoiceStatusHistory | Storico stati — mostrato nel riepilogo del wizard |
Issuer | Anagrafica emittente |
VatCode | Codici IVA |
Convention | Convenzioni |
CompanyConvention | Associazioni azienda ↔ convenzione |
🧩 Pattern chiave
Triade di file per entità
Per ogni entità <Entity> la generazione produce:
CRUD/inv/
├── <Entity>.razor ← grid autogenerata
├── <Entity>.razor.tt.cs ← partial autogenerata (init, FK, query)
├── <Entity>.razor.cs ← code-behind CUSTOM (mai rigenerato)
├── Forms/
│ ├── <Entity>Form.razor ← form autogenerato
│ ├── <Entity>Form.razor.tt.cs
│ └── <Entity>Form.razor.cs ← code-behind custom form
└── FormPopups/
├── <Entity>FormPopup.razor
├── <Entity>FormPopup.razor.tt.cs
└── <Entity>FormPopup.razor.cs
Regola chiave: i .razor e .razor.tt.cs vengono sovrascritti
alla rigenerazione. Tutto il codice custom deve stare nel .razor.cs
(che è una partial class del componente).
Dalla documentazione di progetto (CLAUDE.md):
I file all'interno delle cartelle CRUD sono generati automaticamente: se li modifichi direttamente, per persistere le modifiche devi riportarle nei relativi file di configurazione (
conf.json) e rigenerare tramite i tool MCP del server3sd-generator.
File di configurazione — _conf/*.dxgrid.conf.json
Per ogni entità esiste un JSON in CRUD/inv/_conf/ che guida la
generazione. Esempio invoices.dxgrid.conf.json (estratto):
{
"PageRoute": "inv/invoices",
"PageAuthorizationPolicy": "page-inv-invoices",
"AllowView": true,
"AllowInsert": true,
"AllowUpdate": true,
"AllowClone": true,
"AllowDelete": true,
"FormPopup": { "CssClass": "min-w-80" },
"Columns": { ... },
"FormOrder": [ ... ]
}
Il conf.json controlla:
- Rotta, policy di autorizzazione (
_R,_C,_U,_D), label - Visibilità e ordine colonne in grid
- Visibilità, ordine, readonly dei campi in form (insert vs update)
MarkupBeforeForm/MarkupAfterForm— inserimento di markup Razor custom intorno al form generato (es. il wizard)EventsReplacement/CommandConditions— hook di estensione
Code-behind custom — .razor.cs
Esempio Invoice.razor.cs (36 righe):
public partial class Invoice
{
[Inject] private IInvoiceService invoiceService { get; set; } = default!;
protected async Task HandleSend(invoice item)
{
try
{
await invoiceService.SendToArubaAsync(item.id);
toast.ShowSuccess(localizer["invoice_sent_success"]);
await LoadData();
}
catch (Exception ex)
{
toast.ShowError($"{localizer["invoice_send_error"]}: {ex.Message}");
}
}
protected async Task HandleRefreshStatus(invoice item) { ... }
}
Tipicamente contiene:
- Inject di servizi di dominio (
IInvoiceService) - Handler di eventi custom chiamati dai command button definiti in
conf.json - Override di metodi virtuali della classe base
DxGridBase<T>
Wizard fattura — FormPopups/InvoiceFormPopup.*
Il FormPopup dell'entità Invoice ospita il wizard a 5 step. La logica
è splittata in partial class per step (uno per ogni passo del
wizard):
| File | Responsabilità |
|---|---|
InvoiceFormPopup.razor.cs | Inject, state, CustomEditCompleted, PopupClosing (core) |
InvoiceFormPopup.StepDraft.cs | Passo 1 — IsFormValid, SaveInvoiceDraft, IsItemChanged |
InvoiceFormPopup.StepAppointments.cs | Passo 2 — LoadAppointments, CreateLinesFromAppointments, GetConventionDiscount, record AppointmentRow/ActiveConventionRow |
InvoiceFormPopup.StepPayments.cs | Passo 4 — LoadPayments, PaymentsValid, IsPaymentsValid, SavePayments |
InvoiceFormPopup.StepSummary.cs | Passo 5 — LoadSummary, HandleSend, HandleRefreshStatus, HandlePreviewXml, CopyPreviewXmlToClipboard, ReloadInvoiceAndHistory |
Il markup del wizard (5 step + popup anteprima XML) è iniettato dal
conf.json di invoices tramite gli hook FormPopup.MarkupBeforeForm,
MarkupAfterForm e MarkupEnd, non è scritto a mano nel .razor
(che viene rigenerato).
Pattern Send / Refresh / PreviewXml condiviso tra grid invoice e
StepSummary tramite l'helper InvoiceActionRunner.ExecuteAsync (vedi
Servizi) — incapsula try / azione / toast successo / reload / catch toast errore.
Componenti CRUD "figli" riusati inline
InvoiceLine e InvoicePayment sono componenti CRUD a sé stanti (con
propria pagina) ma vengono embedded nel wizard con parametri per
disattivare la propria navigazione:
<InvoiceLine invoiceId="@dataItem.id"
SetPageData="false"
CommandColumnVisible="@(!readOnly)" />
Questo riuso evita duplicazione UI tra pagina standalone e step wizard.
📁 File chiave
CRUD/inv/Invoice.razor.cs— handler Send / RefreshStatus sulla grid fattureCRUD/inv/FormPopups/InvoiceFormPopup.razor.cs— logica wizard 5 stepCRUD/inv/_conf/invoices.dxgrid.conf.json— configurazione generazione (rotte, permessi, colonne, markup wizard)CRUD/inv/_conf/*.dxgrid.conf.json— conf delle altre 8 entità
🔌 Estensione tipica
Aggiungere un command button alla grid
- Modificare
_conf/<entity>.dxgrid.conf.json: aggiungere entry inCommandColumnItems(o equivalente) con nome, icona, policy. - Aggiungere handler nel
.razor.cscustom. - Rigenerare via MCP
3sd-generator.
Nascondere un campo in form
Modificare _conf/<entity>.dxgrid.conf.json → sezione Columns o
FormFieldSettings: impostare visibilità Insert/Update su hidden
o readonly. Rigenerare.
Aggiungere logica a un form
Scrivere la logica in Forms/<Entity>Form.razor.cs (partial class). Il
.razor.tt.cs chiama punti di estensione (OnInitializedAsync,
OnSubmit, ecc.) che il custom può sovrascrivere.
Aggiungere markup custom
Per markup che resiste alla rigenerazione (es. un wizard), inserirlo nei
campi MarkupBeforeForm / MarkupAfterForm del conf.json. Il
generatore li include nei punti giusti del .razor.
⚠️ Debito tecnico
- Commento "FormPopup.MarkupBeforeForm" nel conf: il markup inline JSON rende difficile la leggibilità/diff del wizard. Valutare estrazione in file Razor esterno incluso, se supportato dal generatore.