People — Directory
La directory People (/dashboard/users) è l'anagrafica completa dei Paradigmini: ricerca, filtri avanzati e accesso al dettaglio profilo.
Esperienza utente
Cosa si può fare
- Cercare per nome o testo libero (debounce sulla barra di ricerca)
- Filtrare tramite sidebar laterale:
- Stato anagrafica (
mainFilter): attivi (default), archiviati, tutti - Staffed / Billed (due slider percentuali indipendenti)
- Posizione, skills, dipartimento (checkbox)
- Ordinare le colonne della tabella
- Navigare con paginazione
- Aprire il dettaglio cliccando su una riga →
/dashboard/users/:id - Menu Azioni (se
canManageUsers()): Vai al dettaglio, Archivia profilo - Aggiungere una persona (
persons.create/canManageUsers()): dialog con form multi-campo
Gli avatar non sono in tabella: GET /v1/persons restituisce righe slim (PersonListItem) e omette profileImage salvo presign=true. La directory non passa presign. I consumer che mostrano avatar (members picker, progetto) lo richiedono esplicitamente.
Cosa non si può fare
Non è esposta l'eliminazione definitiva del profilo: solo archiviazione.
Chi può accedere
Tutti gli utenti autorizzati in dashboard. Creazione e archiviazione dipendono dalle policies persons.* — vedi Accesso e ruoli.
Dettaglio tecnico
Sync filtri ↔ URL
I filtri attivi sono sincronizzati con i query params dell'URL tramite TableFiltersService e utility in src/utils/table-query-params.ts. Il badge conteggio filtri attivi include anche mainFilter diverso da active.
Flusso semplificato:
sequenceDiagram
participant U as Utente
participant T as TableUsersComponent
participant F as TableFiltersService
participant R as Router (query params)
participant P as PeopleService
U->>T: Modifica filtro / ricerca
T->>R: Aggiorna query params
R->>T: ActivatedRoute params
T->>P: getPersons(filters)
P-->>T: PeopleResponse
Caricamento dati
PeopleService.getPersons()→ Lambda people, endpoint/v1/persons. Risposta:PeopleResponse.personscomePersonListItem[](identità, BU, posizioni, role, skill id, allocation). Contratto fullPerson2è soloGET /v1/persons/:id.profileImagesolo con querypresign=true.- Archiviazione:
PeopleService.archivePerson()→PUT /v1/persons/:id/archive - Opzioni filtro (posizioni, skills, dipartimenti): caricate in modo differito — vedi sezione dedicata sotto.
Caricamento differito del catalogo filtri
Le opzioni checkbox della sidebar (Posizioni, Skill, Dipartimento) non vengono scaricate all'apertura della pagina. Il fetch avviene la prima volta che l'utente apre la sidebar laterale, all'interno di showSidebar() → loadFilterCatalogs().
sequenceDiagram
participant U as Utente
participant T as TableUsersComponent
participant TF as TableFiltersService
participant PS as PositionService
participant SS as SkillService
U->>T: Apre sidebar filtri
T->>T: isFilterCatalogLoadStarted = false → entra in loadFilterCatalogs()
T->>T: isFilterCatalogLoading = true
T->>TF: forkJoin(getPositions, getSkills, getBusinessUnits)
TF->>PS: getPositions() (cache HTTP)
TF->>SS: getSkills() (cache HTTP)
TF->>TF: getBusinessUnits() (cache shareReplay)
PS-->>TF: Position[]
SS-->>TF: SkillNamed[]
TF-->>T: risultati paralleli
T->>T: positions / skills / businessUnits segnali aggiornati
T->>T: isFilterCatalogLoading = false
Dettagli implementativi:
- Il flag
isFilterCatalogLoadStartedgarantisce che la chiamata venga eseguita una sola volta per sessione, anche se l'utente apre e chiude la sidebar più volte. In caso di errore il flag viene resettato per permettere un nuovo tentativo. - Durante il caricamento, ogni
app-native-sidebar-accordionriceve[isLoading]="isFilterCatalogLoading()"e mostra skeleton al posto della lista di checkbox vuota. TableFiltersService.getPositions()delega aPositionService.getPositions()(cache HTTP singleton).TableFiltersService.getSkills()delega aSkillService.getSkills()(stessa strategia).getBusinessUnits()usashareReplay(1)interno al servizio.
Fix breadcrumb / doppia chiamata GET /skills:
BreadcrumbComponent costruisce ProjectService in modo lazy (this.injector.get(ProjectService)) solo quando il breadcrumb corrente è "Dettaglio progetto". In precedenza l'injection al costruttore causava un secondo GET /skills su ogni pagina dashboard (inclusa la directory People). Con la costruzione lazy, quella chiamata non avviene sulla rotta /dashboard/users.
Aggiunta utente
AddUserDialogComponent usa FormDropdownValuesService per popolare i dropdown e PeopleService per la POST. Ruolo e Posizione sono p-multiSelect (array inviati come role[] e positions[]).
File principali
| File | Ruolo |
|---|---|
src/pages/table-users/table-users.component.ts |
Tabella, filtri, azioni, paginazione |
src/pages/table-users/add-user-dialog/ |
Dialog creazione |
src/pages/users/users.component.ts |
Router outlet wrapper |
src/services/people.service.ts |
API persone |
src/services/table-filters.service.ts |
Stato filtri condiviso |
src/services/policies.service.ts |
canManageUsers() |