9ca9cb1d42
- Page /clients (route à plat) : MalioDataTable 6 colonnes (contact, téléphone formaté, codes catégories, badges sites), toggle « Voir les archivés » (état local), boutons Ajouter (manage) / Exporter (view, download xlsx), clic ligne vers le détail, empty state. - Composable useClientsRepository (wrapper de usePaginatedList) + util formatPhoneFR + clé i18n showArchived. - Contrat back : la liste client:read expose désormais les codes catégories (category:read) et les sites agrégés des adresses (site:read + Client::getSites) ; jointures anti N+1 dans createListQueryBuilder. Tests back + front.
83 lines
2.8 KiB
TypeScript
83 lines
2.8 KiB
TypeScript
import { ref } from 'vue'
|
|
import { usePaginatedList } from '~/shared/composables/usePaginatedList'
|
|
|
|
/**
|
|
* Site Starseed rattache a une adresse du client, tel qu'embarque en LISTE
|
|
* (groupe site:read) pour la colonne « Site(s) » du Repertoire (badges colores).
|
|
*/
|
|
export interface ClientSite {
|
|
id: number
|
|
name: string
|
|
color: string
|
|
}
|
|
|
|
/**
|
|
* Categorie rattachee au client, embarquee en LISTE (groupe category:read).
|
|
* Seul le `code` (stable, MAJUSCULE — ERP-78) est affiche dans la colonne
|
|
* « Catégories ». Les autres champs sont presents mais non utilises ici.
|
|
*/
|
|
export interface ClientCategory {
|
|
code: string
|
|
name?: string
|
|
}
|
|
|
|
/**
|
|
* Vue MINIMALE d'un client pour le Repertoire (datatable). Volontairement
|
|
* partielle : seuls les champs des colonnes + l'id (navigation) sont types ici.
|
|
* Le detail complet (onglets) est hors perimetre de cet ecran (ERP-62).
|
|
*/
|
|
export interface Client {
|
|
id: number
|
|
companyName: string
|
|
firstName: string | null
|
|
lastName: string | null
|
|
phonePrimary: string | null
|
|
email: string | null
|
|
categories: ClientCategory[]
|
|
sites: ClientSite[]
|
|
isArchived: boolean
|
|
}
|
|
|
|
/**
|
|
* Repertoire clients (ERP-62) — simple enveloppe de `usePaginatedList<Client>`
|
|
* sur la ressource `/clients` (RG-13 : pagination serveur obligatoire ; jamais
|
|
* de chargement integral en memoire).
|
|
*
|
|
* N'ajoute qu'un seul comportement metier : le toggle « Voir les archivés ».
|
|
* Desactive par defaut (la liste n'expose que les clients actifs — RG-1.24).
|
|
* Active, il pousse le filtre serveur `?includeArchived=true` (consomme par le
|
|
* ClientProvider, RG-1.25) et — garantie de `usePaginatedList` — retombe en
|
|
* page 1.
|
|
*
|
|
* Volontairement PAR INSTANCE (pas de singleton module-level) : l'etat tableau
|
|
* est propre a l'ecran Repertoire et meurt avec lui, comme tout consommateur de
|
|
* `usePaginatedList` (cf. sites.vue / categories.vue). Aucun reset au logout a
|
|
* gerer.
|
|
*/
|
|
export function useClientsRepository() {
|
|
// Etat local du toggle « Voir les archivés » — JAMAIS reflete dans l'URL
|
|
// (regle ABSOLUE n°6).
|
|
const includeArchived = ref(false)
|
|
|
|
const list = usePaginatedList<Client>({ url: '/clients' })
|
|
|
|
/**
|
|
* Bascule l'inclusion des clients archives et relance la liste. La remise
|
|
* en page 1 est assuree par `setFilters` (usePaginatedList). Quand le toggle
|
|
* repasse a false, on RETIRE le filtre (valeur `undefined`) plutot que
|
|
* d'envoyer `includeArchived=false`, pour une query propre.
|
|
*/
|
|
async function setIncludeArchived(value: boolean): Promise<void> {
|
|
includeArchived.value = value
|
|
await list.setFilters(
|
|
value ? { includeArchived: true } : { includeArchived: undefined },
|
|
)
|
|
}
|
|
|
|
return {
|
|
...list,
|
|
includeArchived,
|
|
setIncludeArchived,
|
|
}
|
|
}
|