Vai al contenuto

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.persons come PersonListItem[] (identità, BU, posizioni, role, skill id, allocation). Contratto full Person2 è solo GET /v1/persons/:id. profileImage solo con query presign=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 isFilterCatalogLoadStarted garantisce 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-accordion riceve [isLoading]="isFilterCatalogLoading()" e mostra skeleton al posto della lista di checkbox vuota.
  • TableFiltersService.getPositions() delega a PositionService.getPositions() (cache HTTP singleton). TableFiltersService.getSkills() delega a SkillService.getSkills() (stessa strategia). getBusinessUnits() usa shareReplay(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()