From 5754d194505dd8f160cb7c8461e06e385ba085e7 Mon Sep 17 00:00:00 2001 From: tristan Date: Wed, 3 Jun 2026 15:34:31 +0200 Subject: [PATCH] feat(front) : ameliorations UI onglets client (compta, RIB, blocs, placeholder) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Onglet Comptabilite : grille alignee sur les autres onglets (grid-cols-4 gap-x-[44px] gap-y-4) en creation / modification / consultation. - Bloc RIB toujours visible (au moins un bloc, meme vide) en creation, modification et consultation ; un bloc vide n'est jamais persiste. - Blocs Contact / Adresse / RIB toujours affiches meme vides en consultation et modification ; suppression des messages « Aucun ... enregistre ». - Onglets a venir (Transport, Statistiques, Rapports, Echanges) : nouveau composant partage ComingSoonPlaceholder (shared/components/ui) « En cours de dev » + gif, reutilisable par tous les modules ; remplace TabPlaceholderBlank. --- frontend/i18n/locales/fr.json | 10 ++-- .../components/TabPlaceholderBlank.vue | 14 ----- .../commercial/pages/clients/[id]/edit.vue | 29 +++++----- .../commercial/pages/clients/[id]/index.vue | 36 +++++++------ .../modules/commercial/pages/clients/new.vue | 11 ++-- frontend/public/coming-soon.gif | Bin 0 -> 1557856 bytes .../components/ui/ComingSoonPlaceholder.vue | 51 ++++++++++++++++++ 7 files changed, 102 insertions(+), 49 deletions(-) delete mode 100644 frontend/modules/commercial/components/TabPlaceholderBlank.vue create mode 100644 frontend/public/coming-soon.gif create mode 100644 frontend/shared/components/ui/ComingSoonPlaceholder.vue diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index 4fa1c99..c09f4e9 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -10,7 +10,11 @@ "confirm": "Confirmer", "yes": "Oui", "no": "Non", - "actions": "Actions" + "actions": "Actions", + "comingSoon": { + "title": "En cours de dev", + "subtitle": "Cette fonctionnalité arrive bientôt." + } }, "sidebar": { "administration": { @@ -95,8 +99,6 @@ "back": "Retour au répertoire", "loading": "Chargement du client…", "notFound": "Client introuvable.", - "emptyContacts": "Aucun contact enregistré.", - "emptyAddresses": "Aucune adresse enregistrée.", "confirmArchive": { "title": "Archiver le client", "message": "Ce client n'apparaîtra plus dans le répertoire actif. Confirmer l'archivage ?" @@ -111,8 +113,6 @@ "back": "Retour au répertoire", "loading": "Chargement du client…", "notFound": "Client introuvable.", - "emptyContacts": "Aucun contact enregistré.", - "emptyAddresses": "Aucune adresse enregistrée.", "save": "Valider" }, "validation": { diff --git a/frontend/modules/commercial/components/TabPlaceholderBlank.vue b/frontend/modules/commercial/components/TabPlaceholderBlank.vue deleted file mode 100644 index 5375cb6..0000000 --- a/frontend/modules/commercial/components/TabPlaceholderBlank.vue +++ /dev/null @@ -1,14 +0,0 @@ - - - diff --git a/frontend/modules/commercial/pages/clients/[id]/edit.vue b/frontend/modules/commercial/pages/clients/[id]/edit.vue index e1f64e5..d1614ec 100644 --- a/frontend/modules/commercial/pages/clients/[id]/edit.vue +++ b/frontend/modules/commercial/pages/clients/[id]/edit.vue @@ -179,9 +179,6 @@ @update:model-value="(v) => contacts[index] = v" @remove="askRemoveContact(index)" /> -

- {{ t('commercial.clients.edit.emptyContacts') }} -

-

- {{ t('commercial.clients.edit.emptyAddresses') }} -

-
+
-
+
- - - - + + + + @@ -495,6 +489,11 @@ function hydrate(detail: ClientDetail): void { contacts.value = (detail.contacts ?? []).map(mapContactToDraft) addresses.value = (detail.addresses ?? []).map(mapAddressToDraft) ribs.value = (detail.ribs ?? []).map(mapRibToDraft) + // Chaque bloc reste visible meme vide : si une collection est vide, on amorce + // un bloc vierge (non persiste tant qu'incomplet — cf. submit*/canValidate*). + if (contacts.value.length === 0) contacts.value.push(emptyContact()) + if (addresses.value.length === 0) addresses.value.push(emptyAddress()) + if (ribs.value.length === 0) ribs.value.push(emptyRib()) // Charge les listes distributeur / courtier si une relation est deja posee. if (main.relationType === 'distributeur') referentials.loadDistributors().catch(() => {}) if (main.relationType === 'courtier') referentials.loadBrokers().catch(() => {}) @@ -694,6 +693,8 @@ function askRemoveContact(index: number): void { const removed = contacts.value[index] if (removed?.id != null) removedContactIds.value.push(removed.id) contacts.value.splice(index, 1) + // Garde au moins un bloc visible (cf. amorce a l'hydratation). + if (contacts.value.length === 0) contacts.value.push(emptyContact()) }) } @@ -755,6 +756,8 @@ function askRemoveAddress(index: number): void { const removed = addresses.value[index] if (removed?.id != null) removedAddressIds.value.push(removed.id) addresses.value.splice(index, 1) + // Garde au moins un bloc visible (cf. amorce a l'hydratation). + if (addresses.value.length === 0) addresses.value.push(emptyAddress()) }) } @@ -833,6 +836,8 @@ function askRemoveRib(index: number): void { const removed = ribs.value[index] if (removed?.id != null) removedRibIds.value.push(removed.id) ribs.value.splice(index, 1) + // Garde au moins un bloc RIB visible (cf. amorce a l'hydratation). + if (ribs.value.length === 0) ribs.value.push(emptyRib()) }) } diff --git a/frontend/modules/commercial/pages/clients/[id]/index.vue b/frontend/modules/commercial/pages/clients/[id]/index.vue index 52ea3cd..a16e73c 100644 --- a/frontend/modules/commercial/pages/clients/[id]/index.vue +++ b/frontend/modules/commercial/pages/clients/[id]/index.vue @@ -159,9 +159,6 @@ :title="t('commercial.clients.form.contact.title', { n: index + 1 })" readonly /> -

- {{ t('commercial.clients.consultation.emptyContacts') }} -

@@ -179,9 +176,6 @@ :country-options="countryOptions" readonly /> -

- {{ t('commercial.clients.consultation.emptyAddresses') }} -

@@ -189,7 +183,7 @@ @@ -320,6 +314,7 @@ import { type SelectOption, } from '~/modules/commercial/utils/clientConsultation' import { formatPhoneFR } from '~/shared/utils/phone' +import { emptyAddress, emptyContact, emptyRib } from '~/modules/commercial/types/clientForm' // Masques d'affichage (purement visuels, la donnee reste celle du serveur). const PHONE_MASK = '## ## ## ## ##' @@ -372,10 +367,21 @@ const information = computed(() => ({ directorName: client.value?.directorName ?? null, })) -const contacts = computed(() => (client.value?.contacts ?? []).map(mapContactToDraft)) +// Chaque bloc reste visible meme vide en consultation : si la collection est +// vide, on affiche un bloc vierge en lecture seule (pas de message « Aucun … »). +const contacts = computed(() => { + const list = (client.value?.contacts ?? []).map(mapContactToDraft) + return list.length ? list : [emptyContact()] +}) // Vue par adresse : brouillon + options (sites/categories) propres a l'adresse. -const addressViews = computed(() => (client.value?.addresses ?? []).map(mapAddressView)) -const ribs = computed(() => (client.value?.ribs ?? []).map(mapRibToDraft)) +const addressViews = computed(() => { + const views = (client.value?.addresses ?? []).map(mapAddressView) + return views.length ? views : [{ draft: emptyAddress(), siteOptions: [], categoryOptions: [] }] +}) +const ribs = computed(() => { + const list = (client.value?.ribs ?? []).map(mapRibToDraft) + return list.length ? list : [emptyRib()] +}) // Draft comptable (tout null si l'utilisateur n'a pas accounting.view). const accounting = computed(() => mapAccountingDraft(client.value ?? ({} as ClientDetail))) diff --git a/frontend/modules/commercial/pages/clients/new.vue b/frontend/modules/commercial/pages/clients/new.vue index f651225..8502eec 100644 --- a/frontend/modules/commercial/pages/clients/new.vue +++ b/frontend/modules/commercial/pages/clients/new.vue @@ -233,7 +233,7 @@ + +