Skip to main content

🧯 Dispenser — panoramica funzionale

La feature Dispenser consente a una company di monitorare e gestire i dispenser installati nelle proprie location (indirizzi), organizzati in stanze (rooms) e chassis (telai multi-slot).
Le principali aree sono:

  • Home: selezione location, overview dispenser/chassis e alert aggregati.
  • Dettaglio Dispenser: stato live (connessione, batteria, modalità), consumi, associazioni (smartbag/vintage, room), azioni (parental control, factory reset).
  • Chassis (lista + dettaglio): creazione/assegnazione, ordinamento slot via drag&drop, associazione a room.
  • Rooms (lista + dettaglio): gestione stanze e dispositivi associati.

La feature è caricata lazy sotto dashboard/dispenser e usa NgRx per lo stato “leggero” di contesto (location selezionata).


🧭 Routing (feature-level)

Shell con <router-outlet>: DispenserComponent

// dispenser.module.ts (estratto concettuale)
const DISPENSER_ROUTES: Route[] = [
{ path: '', loadChildren: () => import('./dispenser-home-page/dispenser-home-page.module').then(m => m.DispenserHomePageModule) },
{ path: 'rooms', loadChildren: () => import('./rooms-component/rooms-component.module').then(m => m.RoomsComponentModule) },
{ path: 'rooms/:id', loadChildren: () => import('./room-detail/room-detail.module').then(m => m.RoomDetailModule) },
{ path: 'chassis', loadChildren: () => import('./chassis-component/chassis-component.module').then(m => m.ChassisComponentModule) },
{ path: 'chassis/:id', loadChildren: () => import('./chassis-detail/chassis-detail.module').then(m => m.ChassisDetailModule) },
{ path: ':id', loadChildren: () => import('./dispenser-detail/dispenser-detail.module').then(m => m.DispenserDetailModule) },
];
  • Shared sub-module: DispenserSharedModule riesporta componenti condivisi (es. DispenserCardComponent).

🧠 Stato locale (NgRx) — dispenser feature

// state
export interface DispenserState {
locations: ICompanyAddress[];
selectedLocation: ICompanyAddress | null;
error?: string;
}

// actions (pagina/API)
initDispenserStore({ companyId })
changeSelectedLocation({ location })
initDispenserStoreSuccess({ locations, selectedLocation })
initDispenserStoreFailure({ error })

// selectors
selectLocations, selectSelectedLocation

// effects (estratto)
initDispenserStore:
if userAccess ha location -> success([location], location)
else -> CompanyLocationService.getCompanyAddresses(...)
selectedLocation da localStorage('dispenserSelectedLocation')

changeSelectedLocation:
persist su localStorage('dispenserSelectedLocation')

Il contesto della feature è la location (indirizzo) selezionata.
Se l’utente è vincolato a una location (nel suo access), quella viene forzata.


🏠 Home (DispenserHomePageComponent)

Stato & controlli

  • companyAddresses$: lista indirizzi della company.
  • selectedAddress: FormControl<AlbiOption>: selettore location → DISPENSER_PAGE_ACTIONS.changeSelectedLocation.
  • locationDispensers$: lista dispenser per location (arricchiti con smartbag?: ResolvedSmartbagType e room?).
  • locationChassis$: lista chassis per location (con dispenser agganciati).
  • Flag di caricamento: isLocationLoadingDone$, isDispenserLoadingDone$, isChassisLoadingDone$.
  • fullErrorList$: alert aggregati da tutti i dispenser (codici → pipe di traduzione).

UI

  • Top bar: titolo, descrizione, selettore location.
  • Alert board: elenco errori con link rapido al dettaglio dispenser.
  • Dispenser cards (DispenserCardComponent):
    • nome + icone stato connessione (wifiFull/wifiOffline), modalità (boost/eco/silent), batteria.
    • immagine label della smartbag/vintage (fallback immagine dispenser base/Pro).
    • click → dettaglio dispenser dashboard/dispenser/:id.
  • Link rapidi a chassis e rooms.

Navigazione

navigate(url: string)           // -> dashboard/dispenser/:id
navigateChassis(chassisId: str) // -> dashboard/dispenser/chassis/:id

🔎 Dettaglio Dispenser (DispenserDetailComponent)

Observables principali

  • dispenser$, smartbag$, room$, location$.
  • chassis$, chassisRoom$ (se montato su chassis + room di chassis).
  • roomOptions$: stanze disponibili per la location.
  • Pannelli: showDispenserEditPanel$, showParentalControlPanel$, showFactoryResetPanel$.
  • pageLoading$, pageError$.

Tabelle & options

  • dispenserConsuptionTableColumn: colonne consumi/erogazioni.
  • coolingModeOptions: boost | eco | silent.

Azioni tipiche

  • Edit (pannello):
    • form: name, room, coolingMode, altri campi vincolati ai validator.
    • save → chiamate BackendService (PATCH/PUT) + toast via HeaderMessageService.
  • Parental Control:
    • abilita/disabilita blocchi operativi → endpoint dedicato.
  • Factory Reset:
    • dialog di conferma → dispenserFactoryReset() (POST) → feedback UI.
  • Navigazione: back a dashboard/dispenser.

Errori/Alert

  • Card Errors se dispenser.alerts.length > 0 con mapping testo tramite DispenserAlertTranslatePipe (codici W01, E0x, …).

🧱 Chassis (lista & dettaglio)

Lista (ChassisComponentComponent)

  • Selettore location e opzione di creazione chassis (showChassisCreatePanel$).
  • roomsOptions$ per assegnare rapidamente chassis a una room.
  • Elenco chassis per location con conteggio dispenser agganciati.
    Click → dashboard/dispenser/chassis/:id.

Dettaglio (ChassisDetailComponent)

  • Form chassisUpdateGroup:
    • name: string, totalSlots: number, room: AlbiOption.
  • Gestione slot con CDK Drag&Drop:
    • ordinamento dei dispenser in slot (CdkDragDrop, CdkDragSortEvent).
    • salvataggio nuova disposizione → BackendService.
  • Altre azioni: riassegnazione a room, aggiornamento meta chassis.
  • Back → dashboard/dispenser/chassis.
flowchart LR
A[Load chassis] --> B[Show slot list]
B -->|drag&drop| C[Reorder slots (local)]
C --> D[Save layout -> API]
D --> E{OK?}
E -- yes --> F[Toast success]
E -- no --> G[Show error + rollback]

🚪 Rooms (lista & dettaglio)

Lista (RoomsComponentComponent)

  • Selettore location.
  • rooms$: (room con conteggio dispenser associati).
  • Create room: pannello showCreateRoomPanel$ + POST, aggiornamento lista.
  • Click su una room → dettaglio dashboard/dispenser/rooms/:id.

Dettaglio (RoomDetailComponent)

  • roomDispensers$: dispenser nella room (arricchiti con smartbag/vintage).
  • roomChassis$: chassis nella room (ognuno include i propri dispenser).
  • Azioni tipiche:
    • rename room, spostamento dispenser (via UI), link ai dettagli dei dispositivi.
sequenceDiagram
autonumber
participant Room as RoomDetail
participant BE as BackendService

Room->>BE: GET room/:id (dispensers + chassis)
BE-->>Room: { roomDispensers, roomChassis }
Room-->>Room: render elenchi + azioni
Room->>BE: PATCH rename / move
BE-->>Room: 200 OK
Room-->>Room: toast + refresh

🧩 Componenti/Pipe condivisi

  • DispenserCardComponent
    Presenta info sintetiche del dispenser (stato, modalità, batteria, etichetta smartbag, immagine modello).
    Usato in Home e/o liste.

  • DispenserAlertTranslatePipe
    Mappa codici alert → messaggi human-readable (es. W01: Serving temperature out of range, E02: Cooling system error, …).


🔗 Dipendenze e integrazioni

  • Store: USER_ACCES_SELECTORS (user access & company context), DISPENSER_SELECTORS (locations & selected).
  • Services: CompanyLocationService (indirizzi), BackendService (CRUD dispenser/chassis/rooms, azioni).
  • UI Lib (albi-ui): icone (wifi, batteria, cooling mode), tabelle, autocomplete/select, dialog, loader, vertical tabs nel layout.
  • CDK DragDrop: ordinamento slot chassis.
  • TranslateService: label e testi (dizionari DASHBOARD_DICTIONARY, SHARED_DICTIONARY).

✅ Note operative per chi subentra

  • Il contesto location governa ogni query: inizializzarlo con initDispenserStore e mantenere selectedLocation coerente (persistenza in localStorage).
  • Per aggiungere campi/azioni nel Dettaglio Dispenser:
    1. estendere il FormGroup e i validator,
    2. aggiornare il mapping nel salvataggio,
    3. gestire feedback via HeaderMessageService.
  • Per estendere Chassis:
    • mantenere la semantica degli slot; ogni modifica d’ordine deve passare da CDK DragDrop + API dedicata.
  • Rooms: trattare le room come contenitori logici; se si spostano dispenser, aggiornare entrambe le liste (room e device).
  • Non bypassare la pipe degli alert: mantiene coerenza dei messaggi visibili all’utente.
flowchart TD
subgraph Context
UA[UserAccess select] -->|companyId/location| INIT[initDispenserStore]
INIT -->|locations + selected| SEL[selectedLocation]
end

subgraph Home
SEL --> H[HomePage load]
H --> DList[Dispensers by location]
H --> CList[Chassis by location]
H --> Alerts[Aggregate alerts]
end

DList --> DDet[:id -> Dispenser Detail]
CList --> CDet[chassis/:id -> Chassis Detail]
H --> Rooms[rooms -> Rooms List] --> RDet[rooms/:id -> Room Detail]