🧩 Composizione dell’interfaccia dopo il login
Questa pagina spiega come si compone la UI quando l’utente è autenticato e ha almeno un accesso ad una company attiva.
Il contenitore principale è DashboardComponent (src/dashboards/dashboard), che ospita la navigazione delle feature e formatta la vista tramite componenti di layout.
🔭 Contesto di routing
L’area dashboard è protetta da AuthGuard e CompanyGuard.
Dentro dashboard vengono caricati lazy i moduli delle feature (es. elabel, wines, company, dispenser, …):
{
path: 'dashboard',
component: DashboardComponent,
canActivate: [AuthGuard, CompanyGuard],
children: [
{ path: '', loadChildren: () => import('../dashboards/home-page/home-page.module').then(m => m.HomePageModule) },
{ path: 'elabel', loadChildren: () => import('../dashboards/elabel/elabel.module').then(m => m.ElabelComponentModule) },
{ path: 'wines', loadChildren: () => import('../dashboards/wine/wine.module').then(m => m.WineModule) },
{ path: 'company', loadChildren: () => import('../dashboards/company/company.module').then(m => m.CompanyModule) },
{ path: 'dispenser', loadChildren: () => import('../dashboards/dispenser/dispenser.module').then(m => m.DispenserModule) },
{ path: 'seller-panel', loadChildren: () => import('../dashboards/seller-panel/stock-listing/stock-listing.module').then(m => m.StockListingModule) },
{ path: 'cantina', loadChildren: () => import('../dashboards/cantina/cantina.module').then(m => m.CantinaModule) },
{ path: 'wine-list', loadChildren: () => import('../dashboards/wine-list/wine-list.module').then(m => m.WineListModule) },
]
}
🏗️ Struttura ad alto livello
DashboardComponent→ root della vista autenticata con company; costruisce le tab verticali (in base al tipo di company e ruolo utente) e veicola i flussi dati principali (utente, company selezionata, elenco accessi, suggerimenti ricerca, tema, ecc.).AppPageLayoutComponent→ layout della pagina (sidebar con tab verticali, top bar, messaggi, area contenuto, footer) e gestione responsive/mobile (riduzione sidebar, chiusura automatica su navigazione).AppTopBarComponent→ top bar con user info, search bar, selettore company (pannello cambio company e collegamento a nuove company), e azioni rapide (es. Stripe portal).
Il contenuto della feature corrente è renderizzato nel router-outlet interno al layout.
🧱 DashboardComponent (root area autenticata)
Responsabilità chiave
- Orchestrazione dei dati osservabili:
user$,companyAccesses$,selectedCompanyAccess$,canChangeCompany$.showEnterpriseCompanyAccessPanel$(flag UI contestuale).mainBarSuggestions$(suggerimenti ricerca).
- Costruzione dinamica delle tab verticali (
exampleTabs: TabSectionType[]) in base a:selectedCompanyAccess.company.type→WINERY(Cantina) vsPROFESSIONAL.selectedCompanyAccess.role.name→ abilita/disabilita voci (es.winessolo perAdmin/WineManagernelle Cantine).
- Gestione tema (
dark/light) applicato al root element. - Reazioni di navigazione:
- Se nessuna company selezionata dopo init store →
company-connect. - Se company in pending (role assente o status
PENDING*) →company-pending-verification. - Aggiorna
canChangeUserAccesin store valutando l’URL (calculateCanChangeUserAccessFromUrl). Se la rotta (url) contiene edit o create non permette di cambiare azienda perchè non possiamo cambiare azienda in fase di creazione o modifica (chiaramente).
- Se nessuna company selezionata dopo init store →
Struttura del template
<app-page-layout [verticalTabs]="exampleTabs">
<router-outlet></router-outlet>
</app-page-layout>
Il layout riceve le tab verticali e ospita il contenuto della feature corrente.
🧭 AppPageLayoutComponent (layout e navigazione)
Cosa fornisce
- Sidebar con tab verticali (
albi-vertical-tabs), con:- Sezione principale (Home, E-label, Wines, Dispenser, …).
- Sezione inferiore (Company, Profile).
- Riduzione/espansione sidebar e switch tema.
- Top bar (slot
<ng-content>che viene popolato daAppTopBarComponentquandoshowTopbarètrue). - Messaggi di pagina (notifiche, avvisi pending, link rapidi).
- Area contenuto con scroll e wrapper di impaginazione.
- Pannello amministrativo (solo per utenti gruppo
Admin) per selezionare company al volo (ricerca + impostazione access).
Dati/dipendenze principali
selectedUserAccess$: access attivo → abilita/disabilita voci.showEnterpriseCompanyAccessPanel$: mostra pannello enterprise se necessario.user$: dati utente (avatar, nome).- Responsive:
isMobileView$eisSidebarReduced$per UX mobile.
⬆️ AppTopBarComponent (testata e cambio company)
Funzioni principali
- User area → avatar + nome, link rapido al profilo (
profile/info). - Search bar → suggerimenti calcolati da
MAIN_BAR_SUGGESTIONS(tradotti). - Azione Stripe → apre il customer portal per la company corrente.
- Selettore company:
- Legge
companyAccesses$(gruppate per company con eventuali location figlie). - Mostra pannello di cambio company se
canChangeCompany$ètrue. changeSelectedCompany(...)→ dispatchchangeSelectedUserAcces, poi:- access ACTIVE/role presente → naviga
dashboard. - access PENDING* → naviga
company-pending-verification.
- access ACTIVE/role presente → naviga
navigateToOnboardingConnect()→ forza disaccoppiamento e navigacompany-connect.
- Legge
Sincronizzazione con il router
- Su evento
ScrollaggiornacanChangeUserAccesin store (stessa logica delDashboardComponent).
🧠 Flussi reattivi (dati condivisi)
- Store (NgRx) come fonte unica:
AUTHENTICATION_SELECTORS→ utente loggato.USER_ACCES_SELECTORS→ accessi utente, access selezionato, flag UI (es.selectShowEnterpriseAccessPanel,selectCanChangeUserAcces).
- TranslateService → costruisce etichette tab localizzate.
- BackendService → ricerche company (pannello admin nel layout), Stripe portal (top bar).
🗂️ Tab verticali (policy per tipo di company)
- Cantina (WINERY) → tab estese:
home,elabel,wines,dispenser,seller-panel(WIP),cantina,wine-list, sezione inferiorecompany,profile.
Ruoli abilitanti (esempi dal codice):wines,elabel→Admin/WineManager.dispenser→Admin/CantinaManager.
- Professional → set ridotto: niente tab creazione/gestione wines (non produce vino).
Presenza didispenser,seller-panel(WIP),cantina,wine-list,company,profilecon policy di ruolo coerenti.
🧵 Sequenza di interazione (semplificata)
sequenceDiagram
autonumber
participant Router
participant Dashboard as DashboardComponent
participant Layout as AppPageLayoutComponent
participant Topbar as AppTopBarComponent
participant Store as NgRx Store
Router->>Dashboard: entra in /dashboard (AuthGuard + CompanyGuard OK)
Dashboard->>Store: subscribe selectedCompanyAccess, user, flags
Dashboard->>Dashboard: build verticalTabs (company.type, role)
Dashboard->>Layout: [verticalTabs] + render <router-outlet>
Layout->>Topbar: proietta top bar (ng-content) se showTopbar
Topbar->>Store: subscribe selectedCompanyAccess, companyAccesses, canChangeCompany
Topbar-->>Router: cambio company -> dispatch changeSelectedUserAcces
Store-->>Dashboard: selectedCompanyAccess aggiornata
Dashboard-->>Router: se PENDING -> /company-pending-verification
Router-->>Layout: carica feature module nel router-outlet
✅ Cosa deve ricordare chi subentra
- DashboardComponent governa tabs e routing interno in base a tipo company e ruolo.
- AppPageLayoutComponent gestisce layout, sidebar, topbar, notifiche, responsive.
- AppTopBarComponent è il punto unico per cambiare company, ricerche e shortcut utente.
- Tutto è reattivo: leggere sempre dallo Store e non bypassare le guardie (
AuthGuard,CompanyGuard). - Aggiunta di nuove feature → nuovo lazy module figlio di
dashboard, nuova tab coerente con policy (ruolo/tipo company), eventuali label intranslations.