feat(commercial) : redirection vers la liste a la fin de l'ajout d'un client (ERP-119)
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Successful in 2m6s
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Successful in 1m10s

Quand le dernier onglet remplissable par le role est valide (Adresse pour
Bureau/Commerciale, Comptabilite pour Admin), l'ajout est termine : toast
« Client ajoute » + redirection vers /clients. Le dernier onglet remplissable
est derive de tabKeys via lastFillableTabKey (dernier onglet non-placeholder),
deja role-aware, sans regle RBAC custom. completeTab retourne un booleen pour
eviter le double toast.
This commit is contained in:
2026-06-09 14:32:31 +02:00
parent 4d403ec7ed
commit ada4b156fa
4 changed files with 56 additions and 6 deletions
@@ -399,6 +399,7 @@ import {
isRibBlank,
isRibComplete,
isRibRequiredForPaymentType,
lastFillableTabKey,
MAIN_REQUIRED_NON_NULLABLE_KEYS,
omitEmptyRequired,
RIB_REQUIRED_NON_NULLABLE_KEYS,
@@ -588,6 +589,12 @@ const validated = reactive<Record<string, boolean>>({})
const tabKeys = computed(() => buildClientFormTabKeys(canAccountingView.value))
// Dernier onglet REMPLISSABLE par le role (cf. lastFillableTabKey) : deja role-aware
// via tabKeys (accounting present ssi accounting.view, et a la creation « present » =
// « editable » : aucun role createur n'a la Compta en lecture seule). Sa validation
// cloture l'ajout -> redirection vers la liste.
const lastFillableTab = computed(() => lastFillableTabKey(tabKeys.value))
// Icone (Iconify) affichee dans l'onglet, par cle. A ajuster librement.
const TAB_ICONS: Record<string, string> = {
information: 'mdi:account-outline',
@@ -615,12 +622,23 @@ function tabIndex(key: string): number {
return tabKeys.value.indexOf(key)
}
/** Marque l'onglet valide, deverrouille et avance automatiquement au suivant. */
function completeTab(key: string): void {
/**
* Marque l'onglet valide. Si c'est le dernier onglet remplissable, l'ajout est
* termine : toast final + redirection vers la liste, et on retourne true pour que
* l'appelant n'affiche pas son toast « mis a jour ». Sinon, deverrouille et avance
* a l'onglet suivant, et retourne false.
*/
function completeTab(key: string): boolean {
validated[key] = true
if (key === lastFillableTab.value) {
toast.success({ title: t('commercial.clients.toast.addComplete') })
router.push('/clients')
return true
}
const next = tabKeys.value[tabIndex(key) + 1]
unlockedIndex.value = Math.max(unlockedIndex.value, tabIndex(key) + 1)
if (next) activeTab.value = next
return false
}
// Passage automatique sur les onglets coquille (Transport, Stats, Rapports, Echanges).
@@ -658,7 +676,7 @@ async function submitInformation(): Promise<void> {
profitAmount: information.profitAmount || null,
directorName: information.directorName || null,
}, { toast: false })
completeTab('information')
if (completeTab('information')) return
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}
catch (error) {
@@ -737,7 +755,7 @@ async function submitContacts(): Promise<void> {
)
// Tant qu'un bloc reste en erreur : pas de validation d'onglet ni de toast succes.
if (hasError) return
completeTab('contact')
if (completeTab('contact')) return
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}
finally {
@@ -839,7 +857,7 @@ async function submitAddresses(): Promise<void> {
error => toast.error({ title: t('commercial.clients.toast.error'), message: apiErrorMessage(error) }),
)
if (hasError) return
completeTab('address')
if (completeTab('address')) return
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}
finally {
@@ -967,7 +985,7 @@ async function submitAccounting(): Promise<void> {
return
}
completeTab('accounting')
if (completeTab('accounting')) return
toast.success({ title: t('commercial.clients.toast.updateSuccess') })
}
finally {