fix(front) : retours revue M3 prestataires (consultation, validations, redirection)
This commit is contained in:
@@ -406,8 +406,6 @@
|
|||||||
"back": "Retour au répertoire",
|
"back": "Retour au répertoire",
|
||||||
"loading": "Chargement…",
|
"loading": "Chargement…",
|
||||||
"notFound": "Prestataire introuvable.",
|
"notFound": "Prestataire introuvable.",
|
||||||
"emptyContacts": "Aucun contact.",
|
|
||||||
"emptyAddresses": "Aucune adresse.",
|
|
||||||
"confirmArchive": "Archiver ce prestataire ? Il n'apparaîtra plus dans le répertoire actif.",
|
"confirmArchive": "Archiver ce prestataire ? Il n'apparaîtra plus dans le répertoire actif.",
|
||||||
"confirmRestore": "Restaurer ce prestataire ? Il réapparaîtra dans le répertoire actif."
|
"confirmRestore": "Restaurer ce prestataire ? Il réapparaîtra dans le répertoire actif."
|
||||||
},
|
},
|
||||||
@@ -429,6 +427,7 @@
|
|||||||
"sites": "Site"
|
"sites": "Site"
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
|
"nameRequired": "Le nom du prestataire est obligatoire.",
|
||||||
"siteRequired": "Sélectionnez au moins un site.",
|
"siteRequired": "Sélectionnez au moins un site.",
|
||||||
"categoryRequired": "Sélectionnez au moins une catégorie."
|
"categoryRequired": "Sélectionnez au moins une catégorie."
|
||||||
},
|
},
|
||||||
@@ -485,6 +484,7 @@
|
|||||||
"exportError": "L'export du répertoire prestataires a échoué. Réessayez.",
|
"exportError": "L'export du répertoire prestataires a échoué. Réessayez.",
|
||||||
"createSuccess": "Prestataire créé avec succès",
|
"createSuccess": "Prestataire créé avec succès",
|
||||||
"updateSuccess": "Prestataire mis à jour avec succès",
|
"updateSuccess": "Prestataire mis à jour avec succès",
|
||||||
|
"addComplete": "Prestataire ajouté",
|
||||||
"archiveSuccess": "Prestataire archivé avec succès",
|
"archiveSuccess": "Prestataire archivé avec succès",
|
||||||
"restoreSuccess": "Prestataire restauré avec succès"
|
"restoreSuccess": "Prestataire restauré avec succès"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,14 +69,14 @@ describe('useProviderForm', () => {
|
|||||||
permState.accountingManage = false
|
permState.accountingManage = false
|
||||||
})
|
})
|
||||||
|
|
||||||
it('RG-3.03/RG-3.09 (front) : bloque le POST si aucun site / aucune categorie', async () => {
|
it('front : formulaire principal vide -> erreurs sur nom + site + categorie, pas de POST', async () => {
|
||||||
const form = useProviderForm()
|
const form = useProviderForm()
|
||||||
form.main.companyName = 'Maintenance Pro'
|
|
||||||
|
|
||||||
const created = await form.submitMain()
|
const created = await form.submitMain()
|
||||||
|
|
||||||
expect(created).toBe(false)
|
expect(created).toBe(false)
|
||||||
expect(mockPost).not.toHaveBeenCalled()
|
expect(mockPost).not.toHaveBeenCalled()
|
||||||
|
expect(form.mainErrors.errors.companyName).toBe('technique.providers.form.errors.nameRequired')
|
||||||
expect(form.mainErrors.errors.sites).toBe('technique.providers.form.errors.siteRequired')
|
expect(form.mainErrors.errors.sites).toBe('technique.providers.form.errors.siteRequired')
|
||||||
expect(form.mainErrors.errors.categories).toBe('technique.providers.form.errors.categoryRequired')
|
expect(form.mainErrors.errors.categories).toBe('technique.providers.form.errors.categoryRequired')
|
||||||
expect(form.mainLocked.value).toBe(false)
|
expect(form.mainLocked.value).toBe(false)
|
||||||
@@ -122,18 +122,17 @@ describe('useProviderForm', () => {
|
|||||||
expect(form.unlockedIndex.value).toBe(0)
|
expect(form.unlockedIndex.value).toBe(0)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('omet companyName vide du payload (laisse la 422 NotBlank back mordre)', async () => {
|
it('front : nom vide/espaces -> erreur inline sur companyName, pas de POST', async () => {
|
||||||
mockPost.mockResolvedValueOnce({ id: 1, companyName: null })
|
|
||||||
const form = useProviderForm()
|
const form = useProviderForm()
|
||||||
form.main.companyName = ' '
|
form.main.companyName = ' '
|
||||||
form.main.categoryIris = [CAT_MAINT]
|
form.main.categoryIris = [CAT_MAINT]
|
||||||
form.main.siteIris = [SITE_86]
|
form.main.siteIris = [SITE_86]
|
||||||
|
|
||||||
await form.submitMain()
|
const created = await form.submitMain()
|
||||||
|
|
||||||
const body = (mockPost.mock.calls[0] ?? [])[1] as Record<string, unknown>
|
expect(created).toBe(false)
|
||||||
expect(body).not.toHaveProperty('companyName')
|
expect(mockPost).not.toHaveBeenCalled()
|
||||||
expect(body).toEqual({ categories: [CAT_MAINT], sites: [SITE_86] })
|
expect(form.mainErrors.errors.companyName).toBe('technique.providers.form.errors.nameRequired')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('409 doublon (RG-3.10) : erreur inline dediee sur companyName, pas de verrouillage', async () => {
|
it('409 doublon (RG-3.10) : erreur inline dediee sur companyName, pas de verrouillage', async () => {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
buildProviderContactPayload,
|
buildProviderContactPayload,
|
||||||
isProviderContactBlank,
|
isProviderContactBlank,
|
||||||
|
isProviderContactNamed,
|
||||||
} from '~/modules/technique/utils/forms/providerContact'
|
} from '~/modules/technique/utils/forms/providerContact'
|
||||||
import {
|
import {
|
||||||
buildProviderAddressPayload,
|
buildProviderAddressPayload,
|
||||||
@@ -111,6 +112,10 @@ export function useProviderForm() {
|
|||||||
*/
|
*/
|
||||||
function validateMainFront(): boolean {
|
function validateMainFront(): boolean {
|
||||||
let valid = true
|
let valid = true
|
||||||
|
if (!main.companyName?.trim()) {
|
||||||
|
mainErrors.setError('companyName', t('technique.providers.form.errors.nameRequired'))
|
||||||
|
valid = false
|
||||||
|
}
|
||||||
if (main.siteIris.length === 0) {
|
if (main.siteIris.length === 0) {
|
||||||
mainErrors.setError('sites', t('technique.providers.form.errors.siteRequired'))
|
mainErrors.setError('sites', t('technique.providers.form.errors.siteRequired'))
|
||||||
valid = false
|
valid = false
|
||||||
@@ -299,10 +304,11 @@ export function useProviderForm() {
|
|||||||
// Erreurs 422 par ligne (alignees sur l'index du v-for), peuplees par submitRows.
|
// Erreurs 422 par ligne (alignees sur l'index du v-for), peuplees par submitRows.
|
||||||
const contactErrors = ref<Record<string, string>[]>([])
|
const contactErrors = ref<Record<string, string>[]>([])
|
||||||
|
|
||||||
// « + Nouveau contact » desactive tant que le dernier bloc est vide (RG-3.04).
|
// « + Nouveau contact » desactive tant que le dernier bloc n'a pas de nom OU
|
||||||
|
// prenom (RG-3.04, aligne M1/M2 — fonction/tel/email seuls ne suffisent pas).
|
||||||
const canAddContact = computed(() => {
|
const canAddContact = computed(() => {
|
||||||
const last = contacts.value[contacts.value.length - 1]
|
const last = contacts.value[contacts.value.length - 1]
|
||||||
return last !== undefined && !isProviderContactBlank(last)
|
return last !== undefined && isProviderContactNamed(last)
|
||||||
})
|
})
|
||||||
|
|
||||||
function addContact(): void {
|
function addContact(): void {
|
||||||
|
|||||||
@@ -333,6 +333,7 @@ const { provider, loading, error, load } = useProvider(providerId)
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
main,
|
main,
|
||||||
|
providerId: formProviderId,
|
||||||
mainErrors,
|
mainErrors,
|
||||||
mainSubmitting,
|
mainSubmitting,
|
||||||
tabSubmitting,
|
tabSubmitting,
|
||||||
@@ -389,6 +390,9 @@ function prefill(): void {
|
|||||||
const d = provider.value
|
const d = provider.value
|
||||||
if (!d) return
|
if (!d) return
|
||||||
|
|
||||||
|
// Indispensable : pilote les URLs des PATCH/POST par onglet (sinon les submits no-op).
|
||||||
|
formProviderId.value = d.id
|
||||||
|
|
||||||
main.companyName = d.companyName ?? null
|
main.companyName = d.companyName ?? null
|
||||||
main.categoryIris = irisOf(d.categories)
|
main.categoryIris = irisOf(d.categories)
|
||||||
main.siteIris = irisOf(d.sites)
|
main.siteIris = irisOf(d.sites)
|
||||||
|
|||||||
@@ -78,9 +78,6 @@
|
|||||||
:model-value="contact"
|
:model-value="contact"
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
<p v-if="contacts.length === 0" class="text-center text-black/60">
|
|
||||||
{{ t('technique.providers.consultation.emptyContacts') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -97,9 +94,6 @@
|
|||||||
:country-options="countryOptionsFor(view.draft.country)"
|
:country-options="countryOptionsFor(view.draft.country)"
|
||||||
readonly
|
readonly
|
||||||
/>
|
/>
|
||||||
<p v-if="addressViews.length === 0" class="text-center text-black/60">
|
|
||||||
{{ t('technique.providers.consultation.emptyAddresses') }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -182,6 +176,7 @@ import {
|
|||||||
siteOptionsOf,
|
siteOptionsOf,
|
||||||
} from '~/modules/technique/utils/forms/providerDetail'
|
} from '~/modules/technique/utils/forms/providerDetail'
|
||||||
import { isBankRequiredForPaymentType, isRibRequiredForPaymentType } from '~/modules/technique/utils/forms/providerAccounting'
|
import { isBankRequiredForPaymentType, isRibRequiredForPaymentType } from '~/modules/technique/utils/forms/providerAccounting'
|
||||||
|
import { emptyProviderAddress, emptyProviderContact } from '~/modules/technique/types/providerForm'
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -222,16 +217,26 @@ const mainSiteIris = computed(() => irisOf(provider.value?.sites))
|
|||||||
const mainCategoryOptions = computed(() => categoryOptionsOf(provider.value?.categories))
|
const mainCategoryOptions = computed(() => categoryOptionsOf(provider.value?.categories))
|
||||||
const mainSiteOptions = computed(() => siteOptionsOf(provider.value?.sites))
|
const mainSiteOptions = computed(() => siteOptionsOf(provider.value?.sites))
|
||||||
|
|
||||||
const contacts = computed(() => (provider.value?.contacts ?? []).map(mapContactToDraft))
|
// Au moins un bloc affiche meme sans donnee (bloc vide en lecture seule, comme
|
||||||
|
// l'onglet Comptabilite et les autres modules — pas de message « Aucun … »).
|
||||||
|
const contacts = computed(() => {
|
||||||
|
const list = (provider.value?.contacts ?? []).map(mapContactToDraft)
|
||||||
|
return list.length > 0 ? list : [emptyProviderContact()]
|
||||||
|
})
|
||||||
// Contacts rattachables (pour resoudre les libelles des contacts lies aux adresses).
|
// Contacts rattachables (pour resoudre les libelles des contacts lies aux adresses).
|
||||||
const contactOptions = computed(() => contactOptionsOf(provider.value?.contacts))
|
const contactOptions = computed(() => contactOptionsOf(provider.value?.contacts))
|
||||||
|
|
||||||
// Vue par adresse : brouillon + options propres a l'adresse (sites/categories embarques).
|
// Vue par adresse : brouillon + options propres a l'adresse (sites/categories embarques).
|
||||||
const addressViews = computed(() => (provider.value?.addresses ?? []).map(address => ({
|
const addressViews = computed(() => {
|
||||||
draft: mapAddressToDraft(address),
|
const views = (provider.value?.addresses ?? []).map(address => ({
|
||||||
siteOptions: siteOptionsOf(address.sites),
|
draft: mapAddressToDraft(address),
|
||||||
categoryOptions: categoryOptionsOf(address.categories),
|
siteOptions: siteOptionsOf(address.sites),
|
||||||
})))
|
categoryOptions: categoryOptionsOf(address.categories),
|
||||||
|
}))
|
||||||
|
return views.length > 0
|
||||||
|
? views
|
||||||
|
: [{ draft: emptyProviderAddress(), siteOptions: [], categoryOptions: [] }]
|
||||||
|
})
|
||||||
|
|
||||||
/** Pays : une seule option (la valeur courante), suffisant pour l'affichage readonly. */
|
/** Pays : une seule option (la valeur courante), suffisant pour l'affichage readonly. */
|
||||||
function countryOptionsFor(country: string): { value: string, label: string }[] {
|
function countryOptionsFor(country: string): { value: string, label: string }[] {
|
||||||
|
|||||||
@@ -85,7 +85,7 @@
|
|||||||
<MalioButton
|
<MalioButton
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:label="t('technique.providers.form.submit')"
|
:label="t('technique.providers.form.submit')"
|
||||||
:disabled="tabSubmitting"
|
:disabled="tabSubmitting || providerId === null"
|
||||||
@click="onSubmitContacts"
|
@click="onSubmitContacts"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
<MalioButton
|
<MalioButton
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:label="t('technique.providers.form.submit')"
|
:label="t('technique.providers.form.submit')"
|
||||||
:disabled="tabSubmitting"
|
:disabled="tabSubmitting || providerId === null"
|
||||||
@click="onSubmitAddresses"
|
@click="onSubmitAddresses"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -251,7 +251,7 @@
|
|||||||
<MalioButton
|
<MalioButton
|
||||||
variant="primary"
|
variant="primary"
|
||||||
:label="t('technique.providers.form.submit')"
|
:label="t('technique.providers.form.submit')"
|
||||||
:disabled="tabSubmitting"
|
:disabled="tabSubmitting || providerId === null"
|
||||||
@click="onSubmitAccounting"
|
@click="onSubmitAccounting"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -314,6 +314,7 @@ const referentials = useProviderReferentials()
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
main,
|
main,
|
||||||
|
providerId,
|
||||||
mainLocked,
|
mainLocked,
|
||||||
mainSubmitting,
|
mainSubmitting,
|
||||||
mainErrors,
|
mainErrors,
|
||||||
@@ -362,15 +363,33 @@ function apiErrorMessage(error: unknown): string {
|
|||||||
return extractApiErrorMessage(data) || t('technique.providers.toast.error')
|
return extractApiErrorMessage(data) || t('technique.providers.toast.error')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dernier onglet REMPLISSABLE par le role : tabKeys exclut deja la Comptabilite
|
||||||
|
// si l'user n'a pas accounting.view. Sa validation cloture l'ajout (redirection).
|
||||||
|
const lastFillableTab = computed(() => tabKeys.value[tabKeys.value.length - 1])
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apres validation d'un onglet (creation) : si c'est le dernier onglet du role,
|
||||||
|
* l'ajout est termine -> toast final + retour au repertoire (miroir M1/M2) ; sinon
|
||||||
|
* toast de mise a jour (l'onglet suivant a deja ete deverrouille par completeTab).
|
||||||
|
*/
|
||||||
|
function onTabSaved(key: string): void {
|
||||||
|
if (key === lastFillableTab.value) {
|
||||||
|
toast.success({ title: t('technique.providers.toast.addComplete') })
|
||||||
|
router.push('/providers')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
toast.success({ title: t('technique.providers.toast.updateSuccess') })
|
||||||
|
}
|
||||||
|
|
||||||
// ── Onglet Contact ──────────────────────────────────────────────────────────
|
// ── Onglet Contact ──────────────────────────────────────────────────────────
|
||||||
/** Valide l'onglet Contact ; toast de succes si l'onglet a ete finalise. */
|
/** Valide l'onglet Contact ; redirige si c'est le dernier onglet du role. */
|
||||||
async function onSubmitContacts(): Promise<void> {
|
async function onSubmitContacts(): Promise<void> {
|
||||||
const ok = await submitContacts(error => toast.error({
|
const ok = await submitContacts(error => toast.error({
|
||||||
title: t('technique.providers.toast.error'),
|
title: t('technique.providers.toast.error'),
|
||||||
message: apiErrorMessage(error),
|
message: apiErrorMessage(error),
|
||||||
}))
|
}))
|
||||||
if (ok) {
|
if (ok) {
|
||||||
toast.success({ title: t('technique.providers.toast.updateSuccess') })
|
onTabSaved('contact')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,14 +432,14 @@ function onAddressDegraded(): void {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Valide l'onglet Adresse ; toast de succes si l'onglet a ete finalise. */
|
/** Valide l'onglet Adresse ; redirige si c'est le dernier onglet du role. */
|
||||||
async function onSubmitAddresses(): Promise<void> {
|
async function onSubmitAddresses(): Promise<void> {
|
||||||
const ok = await submitAddresses(error => toast.error({
|
const ok = await submitAddresses(error => toast.error({
|
||||||
title: t('technique.providers.toast.error'),
|
title: t('technique.providers.toast.error'),
|
||||||
message: apiErrorMessage(error),
|
message: apiErrorMessage(error),
|
||||||
}))
|
}))
|
||||||
if (ok) {
|
if (ok) {
|
||||||
toast.success({ title: t('technique.providers.toast.updateSuccess') })
|
onTabSaved('address')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -450,7 +469,7 @@ function askRemoveRib(index: number): void {
|
|||||||
askConfirm(t('technique.providers.form.confirmDelete.rib'), () => removeRib(index))
|
askConfirm(t('technique.providers.form.confirmDelete.rib'), () => removeRib(index))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Valide l'onglet Comptabilite ; toast de succes si l'onglet a ete finalise. */
|
/** Valide l'onglet Comptabilite ; redirige si c'est le dernier onglet du role. */
|
||||||
async function onSubmitAccounting(): Promise<void> {
|
async function onSubmitAccounting(): Promise<void> {
|
||||||
const ok = await submitAccounting(
|
const ok = await submitAccounting(
|
||||||
isBankRequired.value,
|
isBankRequired.value,
|
||||||
@@ -461,7 +480,7 @@ async function onSubmitAccounting(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
if (ok) {
|
if (ok) {
|
||||||
toast.success({ title: t('technique.providers.toast.updateSuccess') })
|
onTabSaved('accounting')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import {
|
|||||||
buildProviderContactPayload,
|
buildProviderContactPayload,
|
||||||
hasAtLeastOneFilledContact,
|
hasAtLeastOneFilledContact,
|
||||||
isProviderContactBlank,
|
isProviderContactBlank,
|
||||||
|
isProviderContactNamed,
|
||||||
} from '../providerContact'
|
} from '../providerContact'
|
||||||
import { emptyProviderContact } from '~/modules/technique/types/providerForm'
|
import { emptyProviderContact } from '~/modules/technique/types/providerForm'
|
||||||
|
|
||||||
@@ -34,15 +35,28 @@ describe('providerContact helpers', () => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('hasAtLeastOneFilledContact (RG-3.12)', () => {
|
describe('isProviderContactNamed (RG-3.04 — prenom OU nom)', () => {
|
||||||
it('false si tous les blocs sont vides', () => {
|
it('vrai avec un prenom seul ou un nom seul', () => {
|
||||||
expect(hasAtLeastOneFilledContact([emptyProviderContact(), emptyProviderContact()])).toBe(false)
|
expect(isProviderContactNamed({ ...emptyProviderContact(), firstName: 'Jean' })).toBe(true)
|
||||||
|
expect(isProviderContactNamed({ ...emptyProviderContact(), lastName: 'Dupont' })).toBe(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('true des qu\'un bloc porte une donnee', () => {
|
it('faux si seuls fonction / telephone / email sont remplis (ne suffit pas)', () => {
|
||||||
|
expect(isProviderContactNamed({ ...emptyProviderContact(), jobTitle: 'Directeur' })).toBe(false)
|
||||||
|
expect(isProviderContactNamed({ ...emptyProviderContact(), email: 'a@b.fr' })).toBe(false)
|
||||||
|
expect(isProviderContactNamed({ ...emptyProviderContact(), phonePrimary: '0102030405' })).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('hasAtLeastOneFilledContact (RG-3.12 — au moins un contact nomme)', () => {
|
||||||
|
it('false si aucun bloc n\'est nomme', () => {
|
||||||
|
expect(hasAtLeastOneFilledContact([emptyProviderContact(), { ...emptyProviderContact(), email: 'a@b.fr' }])).toBe(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('true des qu\'un bloc porte un nom ou prenom', () => {
|
||||||
expect(hasAtLeastOneFilledContact([
|
expect(hasAtLeastOneFilledContact([
|
||||||
emptyProviderContact(),
|
emptyProviderContact(),
|
||||||
{ ...emptyProviderContact(), email: 'a@b.fr' },
|
{ ...emptyProviderContact(), lastName: 'Dupont' },
|
||||||
])).toBe(true)
|
])).toBe(true)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -32,12 +32,21 @@ export function isProviderContactBlank(contact: ProviderContactFormDraft): boole
|
|||||||
].some(isFilled)
|
].some(isFilled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RG-3.04 : un contact est « nomme » (valide) des qu'il porte un prenom OU un nom
|
||||||
|
* — aligne sur le M1/M2. Sert le gating « + Nouveau contact » et la notion de
|
||||||
|
* contact valide (la fonction / le telephone / l'email seuls ne suffisent pas).
|
||||||
|
*/
|
||||||
|
export function isProviderContactNamed(contact: ProviderContactFormDraft): boolean {
|
||||||
|
return isFilled(contact.firstName) || isFilled(contact.lastName)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RG-3.12 : l'onglet Contact ne peut etre finalise que s'il reste au moins un
|
* RG-3.12 : l'onglet Contact ne peut etre finalise que s'il reste au moins un
|
||||||
* bloc non vide (au moins un contact valide).
|
* contact nomme (prenom ou nom).
|
||||||
*/
|
*/
|
||||||
export function hasAtLeastOneFilledContact(contacts: ProviderContactFormDraft[]): boolean {
|
export function hasAtLeastOneFilledContact(contacts: ProviderContactFormDraft[]): boolean {
|
||||||
return contacts.some(contact => !isProviderContactBlank(contact))
|
return contacts.some(isProviderContactNamed)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user