refactor(front) : champs anti-parasites via masks maska (filtrage natif, focus/curseur OK) au lieu du sanitizer @update ; email sans masque (ERP-193)
This commit is contained in:
@@ -133,6 +133,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:model-value="model.city"
|
:model-value="model.city"
|
||||||
:label="t('commercial.clients.form.address.city')"
|
:label="t('commercial.clients.form.address.city')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -184,6 +185,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.streetComplement"
|
:model-value="model.streetComplement"
|
||||||
:label="t('commercial.clients.form.address.streetComplement')"
|
:label="t('commercial.clients.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:error="errors?.streetComplement"
|
||||||
@@ -204,7 +206,7 @@ import {
|
|||||||
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
||||||
import type { CategoryOption, RefOption } from '~/modules/commercial/composables/useClientReferentials'
|
import type { CategoryOption, RefOption } from '~/modules/commercial/composables/useClientReferentials'
|
||||||
import type { AddressFormDraft } from '~/modules/commercial/types/clientForm'
|
import type { AddressFormDraft } from '~/modules/commercial/types/clientForm'
|
||||||
import { sanitizeAddress, sanitizeEmail } from '~/shared/utils/textSanitize'
|
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque code postal FR : 5 chiffres.
|
// Masque code postal FR : 5 chiffres.
|
||||||
const POSTAL_CODE_MASK = '#####'
|
const POSTAL_CODE_MASK = '#####'
|
||||||
@@ -300,23 +302,14 @@ const addressLoading = ref(false)
|
|||||||
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
||||||
let lastAddressSuggestions: AddressSuggestion[] = []
|
let lastAddressSuggestions: AddressSuggestion[] = []
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : voie / complement / ville = profil
|
// Filtrage des caracteres parasites : porte par le mask ADDRESS_MASK (maska) sur
|
||||||
// adresse, emails de facturation = profil email.
|
// les champs texte editables (complement, ville en mode degrade). La voie en
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof AddressFormDraft, (v: string) => string>> = {
|
// autocomplete (BAN) et la ville en select ne sont pas masquees (le back valide
|
||||||
street: sanitizeAddress,
|
// via Assert\Regex) ; les emails de facturation valident leur format (Assert\Email).
|
||||||
streetComplement: sanitizeAddress,
|
|
||||||
city: sanitizeAddress,
|
|
||||||
billingEmail: sanitizeEmail,
|
|
||||||
billingEmailSecondary: sanitizeEmail,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof AddressFormDraft>(field: K, value: AddressFormDraft[K]): void {
|
function update<K extends keyof AddressFormDraft>(field: K, value: AddressFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as AddressFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Revele le 2e champ email de facturation (clic sur le « + »). */
|
/** Revele le 2e champ email de facturation (clic sur le « + »). */
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.lastName"
|
:model-value="model.lastName"
|
||||||
:label="t('commercial.clients.form.contact.lastName')"
|
:label="t('commercial.clients.form.contact.lastName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.lastName"
|
:error="errors?.lastName"
|
||||||
@@ -23,6 +24,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.firstName"
|
:model-value="model.firstName"
|
||||||
:label="t('commercial.clients.form.contact.firstName')"
|
:label="t('commercial.clients.form.contact.firstName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.firstName"
|
:error="errors?.firstName"
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.jobTitle"
|
:model-value="model.jobTitle"
|
||||||
:label="t('commercial.clients.form.contact.jobTitle')"
|
:label="t('commercial.clients.form.contact.jobTitle')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.jobTitle"
|
:error="errors?.jobTitle"
|
||||||
@@ -77,7 +80,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ContactFormDraft } from '~/modules/commercial/types/clientForm'
|
import type { ContactFormDraft } from '~/modules/commercial/types/clientForm'
|
||||||
import { sanitizeEmail, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste
|
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste
|
||||||
// serveur, cf. formatPhoneFR re-applique a la valeur renvoyee).
|
// serveur, cf. formatPhoneFR re-applique a la valeur renvoyee).
|
||||||
@@ -108,22 +111,13 @@ const { t } = useI18n()
|
|||||||
// Alias local pour la lisibilite du template.
|
// Alias local pour la lisibilite du template.
|
||||||
const model = computed(() => props.modelValue)
|
const model = computed(() => props.modelValue)
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : on retire les caracteres parasites a la
|
// Filtrage des caracteres parasites : porte par les masks maska sur les champs
|
||||||
// frappe. Noms = profil personne, fonction = texte libre, email = profil email.
|
// (PERSON_NAME_MASK / FREE_TEXT_MASK), filtrage natif au focus/curseur. L'email n'a
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof ContactFormDraft, (v: string) => string>> = {
|
// pas de mask (ERP-101 : validation de format via Assert\Email + erreur inline).
|
||||||
lastName: sanitizePersonName,
|
|
||||||
firstName: sanitizePersonName,
|
|
||||||
jobTitle: sanitizeFreeText,
|
|
||||||
email: sanitizeEmail,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof ContactFormDraft>(field: K, value: ContactFormDraft[K]): void {
|
function update<K extends keyof ContactFormDraft>(field: K, value: ContactFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as ContactFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Revele le 2e numero (RG-1.02/1.20 : max 1 secondaire, le « + » disparait). */
|
/** Revele le 2e numero (RG-1.02/1.20 : max 1 secondaire, le « + » disparait). */
|
||||||
|
|||||||
@@ -104,6 +104,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:model-value="model.city"
|
:model-value="model.city"
|
||||||
:label="t('commercial.suppliers.form.address.city')"
|
:label="t('commercial.suppliers.form.address.city')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -135,6 +136,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:model-value="model.street"
|
:model-value="model.street"
|
||||||
:label="t('commercial.suppliers.form.address.street')"
|
:label="t('commercial.suppliers.form.address.street')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -147,6 +149,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.streetComplement"
|
:model-value="model.streetComplement"
|
||||||
:label="t('commercial.suppliers.form.address.streetComplement')"
|
:label="t('commercial.suppliers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:error="errors?.streetComplement"
|
||||||
@@ -182,7 +185,7 @@
|
|||||||
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
||||||
import type { CategoryOption, RefOption } from '~/modules/commercial/composables/useSupplierReferentials'
|
import type { CategoryOption, RefOption } from '~/modules/commercial/composables/useSupplierReferentials'
|
||||||
import type { SupplierAddressFormDraft, SupplierAddressType } from '~/modules/commercial/types/supplierForm'
|
import type { SupplierAddressFormDraft, SupplierAddressType } from '~/modules/commercial/types/supplierForm'
|
||||||
import { sanitizeAddress } from '~/shared/utils/textSanitize'
|
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque code postal FR : 5 chiffres.
|
// Masque code postal FR : 5 chiffres.
|
||||||
const POSTAL_CODE_MASK = '#####'
|
const POSTAL_CODE_MASK = '#####'
|
||||||
@@ -254,21 +257,14 @@ const addressLoading = ref(false)
|
|||||||
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
||||||
let lastAddressSuggestions: AddressSuggestion[] = []
|
let lastAddressSuggestions: AddressSuggestion[] = []
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : voie / complement / ville = profil
|
// Filtrage des caracteres parasites : porte par le mask ADDRESS_MASK (maska) sur
|
||||||
// adresse. Les autres champs (CP, bennes, selects) ne sont pas filtres ici.
|
// les champs texte editables (complement, ville en mode degrade, voie en repli). La
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof SupplierAddressFormDraft, (v: string) => string>> = {
|
// voie en autocomplete (BAN) et la ville en select ne sont pas masquees (le back
|
||||||
street: sanitizeAddress,
|
// valide via Assert\Regex).
|
||||||
streetComplement: sanitizeAddress,
|
|
||||||
city: sanitizeAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof SupplierAddressFormDraft>(field: K, value: SupplierAddressFormDraft[K]): void {
|
function update<K extends keyof SupplierAddressFormDraft>(field: K, value: SupplierAddressFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as SupplierAddressFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.lastName"
|
:model-value="model.lastName"
|
||||||
:label="t('commercial.suppliers.form.contact.lastName')"
|
:label="t('commercial.suppliers.form.contact.lastName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.lastName"
|
:error="errors?.lastName"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.firstName"
|
:model-value="model.firstName"
|
||||||
:label="t('commercial.suppliers.form.contact.firstName')"
|
:label="t('commercial.suppliers.form.contact.firstName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.firstName"
|
:error="errors?.firstName"
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.jobTitle"
|
:model-value="model.jobTitle"
|
||||||
:label="t('commercial.suppliers.form.contact.jobTitle')"
|
:label="t('commercial.suppliers.form.contact.jobTitle')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.jobTitle"
|
:error="errors?.jobTitle"
|
||||||
@@ -76,7 +79,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { SupplierContactFormDraft } from '~/modules/commercial/types/supplierForm'
|
import type { SupplierContactFormDraft } from '~/modules/commercial/types/supplierForm'
|
||||||
import { sanitizeEmail, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
||||||
const PHONE_MASK = '## ## ## ## ##'
|
const PHONE_MASK = '## ## ## ## ##'
|
||||||
@@ -106,22 +109,13 @@ const { t } = useI18n()
|
|||||||
// Alias local pour la lisibilite du template.
|
// Alias local pour la lisibilite du template.
|
||||||
const model = computed(() => props.modelValue)
|
const model = computed(() => props.modelValue)
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : on retire les caracteres parasites a la
|
// Filtrage des caracteres parasites : porte par les masks maska sur les champs
|
||||||
// frappe. Noms = profil personne, fonction = texte libre, email = profil email.
|
// (PERSON_NAME_MASK / FREE_TEXT_MASK), filtrage natif au focus/curseur. L'email n'a
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof SupplierContactFormDraft, (v: string) => string>> = {
|
// pas de mask (ERP-101 : validation de format via Assert\Email + erreur inline).
|
||||||
lastName: sanitizePersonName,
|
|
||||||
firstName: sanitizePersonName,
|
|
||||||
jobTitle: sanitizeFreeText,
|
|
||||||
email: sanitizeEmail,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof SupplierContactFormDraft>(field: K, value: SupplierContactFormDraft[K]): void {
|
function update<K extends keyof SupplierContactFormDraft>(field: K, value: SupplierContactFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as SupplierContactFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Revele le 2e numero (max 1 secondaire, le « + » disparait). */
|
/** Revele le 2e numero (max 1 secondaire, le « + » disparait). */
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
`manage` (ex. Compta). -->
|
`manage` (ex. Compta). -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('commercial.clients.form.main.companyName')"
|
:label="t('commercial.clients.form.main.companyName')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
@@ -106,8 +106,8 @@
|
|||||||
:error="informationErrors.errors.description"
|
:error="informationErrors.errors.description"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.competitors"
|
v-model="information.competitors"
|
||||||
@update:model-value="(v: string) => information.competitors = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('commercial.clients.form.information.competitors')"
|
:label="t('commercial.clients.form.information.competitors')"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="informationErrors.errors.competitors"
|
:error="informationErrors.errors.competitors"
|
||||||
@@ -141,8 +141,8 @@
|
|||||||
@update:model-value="onRevenueAmountInput"
|
@update:model-value="onRevenueAmountInput"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.directorName"
|
v-model="information.directorName"
|
||||||
@update:model-value="(v: string) => information.directorName = sanitizePersonName(v)"
|
:mask="PERSON_NAME_MASK"
|
||||||
:label="t('commercial.clients.form.information.directorName')"
|
:label="t('commercial.clients.form.information.directorName')"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="informationErrors.errors.directorName"
|
:error="informationErrors.errors.directorName"
|
||||||
@@ -254,8 +254,8 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.accountNumber')"
|
:label="t('commercial.clients.form.accounting.accountNumber')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -272,8 +272,8 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.nTva')"
|
:label="t('commercial.clients.form.accounting.nTva')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -336,16 +336,16 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.ribBic')"
|
:label="t('commercial.clients.form.accounting.ribBic')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.ribIban')"
|
:label="t('commercial.clients.form.accounting.ribIban')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
@@ -439,7 +439,7 @@ import {
|
|||||||
} from '~/modules/commercial/utils/forms/clientEdit'
|
} from '~/modules/commercial/utils/forms/clientEdit'
|
||||||
import { clampRevenueAmount } from '~/modules/commercial/utils/forms/amountInput'
|
import { clampRevenueAmount } from '~/modules/commercial/utils/forms/amountInput'
|
||||||
import { todayIso } from '~/shared/utils/date'
|
import { todayIso } from '~/shared/utils/date'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
import {
|
import {
|
||||||
buildClientFormTabKeys,
|
buildClientFormTabKeys,
|
||||||
isAddressValid,
|
isAddressValid,
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
automatiquement sur l'onglet Information. -->
|
automatiquement sur l'onglet Information. -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('commercial.clients.form.main.companyName')"
|
:label="t('commercial.clients.form.main.companyName')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="mainLocked"
|
:disabled="mainLocked"
|
||||||
@@ -101,8 +101,8 @@
|
|||||||
:error="informationErrors.errors.description"
|
:error="informationErrors.errors.description"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.competitors"
|
v-model="information.competitors"
|
||||||
@update:model-value="(v: string) => information.competitors = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('commercial.clients.form.information.competitors')"
|
:label="t('commercial.clients.form.information.competitors')"
|
||||||
:disabled="isValidated('information')"
|
:disabled="isValidated('information')"
|
||||||
:error="informationErrors.errors.competitors"
|
:error="informationErrors.errors.competitors"
|
||||||
@@ -136,8 +136,8 @@
|
|||||||
@update:model-value="onRevenueAmountInput"
|
@update:model-value="onRevenueAmountInput"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.directorName"
|
v-model="information.directorName"
|
||||||
@update:model-value="(v: string) => information.directorName = sanitizePersonName(v)"
|
:mask="PERSON_NAME_MASK"
|
||||||
:label="t('commercial.clients.form.information.directorName')"
|
:label="t('commercial.clients.form.information.directorName')"
|
||||||
:disabled="isValidated('information')"
|
:disabled="isValidated('information')"
|
||||||
:error="informationErrors.errors.directorName"
|
:error="informationErrors.errors.directorName"
|
||||||
@@ -252,8 +252,8 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.accountNumber')"
|
:label="t('commercial.clients.form.accounting.accountNumber')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -270,8 +270,8 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.nTva')"
|
:label="t('commercial.clients.form.accounting.nTva')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -335,16 +335,16 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.ribBic')"
|
:label="t('commercial.clients.form.accounting.ribBic')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
:label="t('commercial.clients.form.accounting.ribIban')"
|
:label="t('commercial.clients.form.accounting.ribIban')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
@@ -423,7 +423,7 @@ import {
|
|||||||
} from '~/modules/commercial/utils/forms/clientFormRules'
|
} from '~/modules/commercial/utils/forms/clientFormRules'
|
||||||
import { clampRevenueAmount } from '~/modules/commercial/utils/forms/amountInput'
|
import { clampRevenueAmount } from '~/modules/commercial/utils/forms/amountInput'
|
||||||
import { todayIso } from '~/shared/utils/date'
|
import { todayIso } from '~/shared/utils/date'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
import {
|
import {
|
||||||
buildAddressPayload,
|
buildAddressPayload,
|
||||||
buildMainPayload,
|
buildMainPayload,
|
||||||
|
|||||||
@@ -23,12 +23,12 @@
|
|||||||
roles sans `manage` (ex. Compta). Pas de contact inline (ERP-106). -->
|
roles sans `manage` (ex. Compta). Pas de contact inline (ERP-106). -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
:label="t('commercial.suppliers.form.main.companyName')"
|
:label="t('commercial.suppliers.form.main.companyName')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="mainErrors.errors.companyName"
|
:error="mainErrors.errors.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelectCheckbox
|
<MalioSelectCheckbox
|
||||||
:model-value="main.categoryIris"
|
:model-value="main.categoryIris"
|
||||||
@@ -67,11 +67,11 @@
|
|||||||
:error="informationErrors.errors.description"
|
:error="informationErrors.errors.description"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.competitors"
|
v-model="information.competitors"
|
||||||
:label="t('commercial.suppliers.form.information.competitors')"
|
:label="t('commercial.suppliers.form.information.competitors')"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="informationErrors.errors.competitors"
|
:error="informationErrors.errors.competitors"
|
||||||
@update:model-value="(v: string) => information.competitors = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
/>
|
/>
|
||||||
<!-- Date de creation jamais dans le futur (ERP-193) : :max plafonne
|
<!-- Date de creation jamais dans le futur (ERP-193) : :max plafonne
|
||||||
le calendrier a aujourd'hui et invalide une saisie future. -->
|
le calendrier a aujourd'hui et invalide une saisie future. -->
|
||||||
@@ -102,11 +102,11 @@
|
|||||||
@update:model-value="onRevenueAmountInput"
|
@update:model-value="onRevenueAmountInput"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.directorName"
|
v-model="information.directorName"
|
||||||
:label="t('commercial.suppliers.form.information.directorName')"
|
:label="t('commercial.suppliers.form.information.directorName')"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="informationErrors.errors.directorName"
|
:error="informationErrors.errors.directorName"
|
||||||
@update:model-value="(v: string) => information.directorName = sanitizePersonName(v)"
|
:mask="PERSON_NAME_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioInputAmount
|
<MalioInputAmount
|
||||||
v-model="information.profitAmount"
|
v-model="information.profitAmount"
|
||||||
@@ -223,12 +223,12 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
:label="t('commercial.suppliers.form.accounting.accountNumber')"
|
:label="t('commercial.suppliers.form.accounting.accountNumber')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.accountNumber"
|
:error="accountingErrors.errors.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.tvaModeIri"
|
:model-value="accounting.tvaModeIri"
|
||||||
@@ -241,12 +241,12 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
:label="t('commercial.suppliers.form.accounting.nTva')"
|
:label="t('commercial.suppliers.form.accounting.nTva')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.nTva"
|
:error="accountingErrors.errors.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.paymentDelayIri"
|
:model-value="accounting.paymentDelayIri"
|
||||||
@@ -305,20 +305,20 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
:label="t('commercial.suppliers.form.accounting.ribBic')"
|
:label="t('commercial.suppliers.form.accounting.ribBic')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
:label="t('commercial.suppliers.form.accounting.ribIban')"
|
:label="t('commercial.suppliers.form.accounting.ribIban')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.iban"
|
:error="ribErrors[index]?.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -428,7 +428,7 @@ import {
|
|||||||
} from '~/modules/commercial/types/supplierForm'
|
} from '~/modules/commercial/types/supplierForm'
|
||||||
import { extractApiErrorMessage } from '~/shared/utils/api'
|
import { extractApiErrorMessage } from '~/shared/utils/api'
|
||||||
import { isRowRemovable, removeCollectionRow } from '~/shared/utils/collectionRow'
|
import { isRowRemovable, removeCollectionRow } from '~/shared/utils/collectionRow'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
import { readHistoryTab } from '~/shared/utils/historyTab'
|
import { readHistoryTab } from '~/shared/utils/historyTab'
|
||||||
|
|
||||||
// Masques de saisie (la normalisation finale reste serveur).
|
// Masques de saisie (la normalisation finale reste serveur).
|
||||||
|
|||||||
@@ -18,12 +18,12 @@
|
|||||||
automatiquement sur l'onglet Information. Pas de contact inline (ERP-106). -->
|
automatiquement sur l'onglet Information. Pas de contact inline (ERP-106). -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
:label="t('commercial.suppliers.form.main.companyName')"
|
:label="t('commercial.suppliers.form.main.companyName')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="mainLocked"
|
:disabled="mainLocked"
|
||||||
:error="mainErrors.errors.companyName"
|
:error="mainErrors.errors.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelectCheckbox
|
<MalioSelectCheckbox
|
||||||
:model-value="main.categoryIris"
|
:model-value="main.categoryIris"
|
||||||
@@ -61,11 +61,11 @@
|
|||||||
:error="informationErrors.errors.description"
|
:error="informationErrors.errors.description"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.competitors"
|
v-model="information.competitors"
|
||||||
:label="t('commercial.suppliers.form.information.competitors')"
|
:label="t('commercial.suppliers.form.information.competitors')"
|
||||||
:disabled="isValidated('information')"
|
:disabled="isValidated('information')"
|
||||||
:error="informationErrors.errors.competitors"
|
:error="informationErrors.errors.competitors"
|
||||||
@update:model-value="(v: string) => information.competitors = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
/>
|
/>
|
||||||
<!-- Date de creation jamais dans le futur (ERP-193) : :max plafonne
|
<!-- Date de creation jamais dans le futur (ERP-193) : :max plafonne
|
||||||
le calendrier a aujourd'hui et invalide une saisie future. -->
|
le calendrier a aujourd'hui et invalide une saisie future. -->
|
||||||
@@ -96,11 +96,11 @@
|
|||||||
@update:model-value="onRevenueAmountInput"
|
@update:model-value="onRevenueAmountInput"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="information.directorName"
|
v-model="information.directorName"
|
||||||
:label="t('commercial.suppliers.form.information.directorName')"
|
:label="t('commercial.suppliers.form.information.directorName')"
|
||||||
:disabled="isValidated('information')"
|
:disabled="isValidated('information')"
|
||||||
:error="informationErrors.errors.directorName"
|
:error="informationErrors.errors.directorName"
|
||||||
@update:model-value="(v: string) => information.directorName = sanitizePersonName(v)"
|
:mask="PERSON_NAME_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioInputAmount
|
<MalioInputAmount
|
||||||
v-model="information.profitAmount"
|
v-model="information.profitAmount"
|
||||||
@@ -217,12 +217,12 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
:label="t('commercial.suppliers.form.accounting.accountNumber')"
|
:label="t('commercial.suppliers.form.accounting.accountNumber')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.accountNumber"
|
:error="accountingErrors.errors.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.tvaModeIri"
|
:model-value="accounting.tvaModeIri"
|
||||||
@@ -235,12 +235,12 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
:label="t('commercial.suppliers.form.accounting.nTva')"
|
:label="t('commercial.suppliers.form.accounting.nTva')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.nTva"
|
:error="accountingErrors.errors.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.paymentDelayIri"
|
:model-value="accounting.paymentDelayIri"
|
||||||
@@ -299,20 +299,20 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
:label="t('commercial.suppliers.form.accounting.ribBic')"
|
:label="t('commercial.suppliers.form.accounting.ribBic')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
:label="t('commercial.suppliers.form.accounting.ribIban')"
|
:label="t('commercial.suppliers.form.accounting.ribIban')"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="isRibRequired"
|
:required="isRibRequired"
|
||||||
:error="ribErrors[index]?.iban"
|
:error="ribErrors[index]?.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
:mask="CODE_ALNUM_MASK"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -401,7 +401,7 @@ import {
|
|||||||
} from '~/modules/commercial/types/supplierForm'
|
} from '~/modules/commercial/types/supplierForm'
|
||||||
import { extractApiErrorMessage } from '~/shared/utils/api'
|
import { extractApiErrorMessage } from '~/shared/utils/api'
|
||||||
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masques de saisie (la normalisation finale reste serveur).
|
// Masques de saisie (la normalisation finale reste serveur).
|
||||||
const SIREN_MASK = '#########'
|
const SIREN_MASK = '#########'
|
||||||
|
|||||||
@@ -85,6 +85,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:model-value="model.city"
|
:model-value="model.city"
|
||||||
:label="t('technique.providers.form.address.city')"
|
:label="t('technique.providers.form.address.city')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -128,6 +129,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.streetComplement"
|
:model-value="model.streetComplement"
|
||||||
:label="t('technique.providers.form.address.streetComplement')"
|
:label="t('technique.providers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:error="errors?.streetComplement"
|
||||||
@@ -141,7 +143,7 @@
|
|||||||
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
||||||
import type { RefOption } from '~/modules/technique/composables/useProviderReferentials'
|
import type { RefOption } from '~/modules/technique/composables/useProviderReferentials'
|
||||||
import type { ProviderAddressFormDraft } from '~/modules/technique/types/providerForm'
|
import type { ProviderAddressFormDraft } from '~/modules/technique/types/providerForm'
|
||||||
import { sanitizeAddress } from '~/shared/utils/textSanitize'
|
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque code postal FR : 5 chiffres.
|
// Masque code postal FR : 5 chiffres.
|
||||||
const POSTAL_CODE_MASK = '#####'
|
const POSTAL_CODE_MASK = '#####'
|
||||||
@@ -206,20 +208,14 @@ const addressLoading = ref(false)
|
|||||||
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
||||||
let lastAddressSuggestions: AddressSuggestion[] = []
|
let lastAddressSuggestions: AddressSuggestion[] = []
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : voie / complement / ville = profil adresse.
|
// Filtrage des caracteres parasites : porte par le mask ADDRESS_MASK (maska) sur
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof ProviderAddressFormDraft, (v: string) => string>> = {
|
// les champs texte editables (complement, ville en mode degrade). La voie en
|
||||||
street: sanitizeAddress,
|
// autocomplete (BAN) et la ville en select ne sont pas masquees (le back valide
|
||||||
streetComplement: sanitizeAddress,
|
// via Assert\Regex).
|
||||||
city: sanitizeAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof ProviderAddressFormDraft>(field: K, value: ProviderAddressFormDraft[K]): void {
|
function update<K extends keyof ProviderAddressFormDraft>(field: K, value: ProviderAddressFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as ProviderAddressFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.lastName"
|
:model-value="model.lastName"
|
||||||
:label="t('technique.providers.form.contact.lastName')"
|
:label="t('technique.providers.form.contact.lastName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.lastName"
|
:error="errors?.lastName"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.firstName"
|
:model-value="model.firstName"
|
||||||
:label="t('technique.providers.form.contact.firstName')"
|
:label="t('technique.providers.form.contact.firstName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.firstName"
|
:error="errors?.firstName"
|
||||||
@@ -34,6 +36,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.jobTitle"
|
:model-value="model.jobTitle"
|
||||||
:label="t('technique.providers.form.contact.jobTitle')"
|
:label="t('technique.providers.form.contact.jobTitle')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.jobTitle"
|
:error="errors?.jobTitle"
|
||||||
@@ -77,7 +80,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ProviderContactFormDraft } from '~/modules/technique/types/providerForm'
|
import type { ProviderContactFormDraft } from '~/modules/technique/types/providerForm'
|
||||||
import { sanitizeEmail, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
// Masque telephone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
||||||
const PHONE_MASK = '## ## ## ## ##'
|
const PHONE_MASK = '## ## ## ## ##'
|
||||||
@@ -105,22 +108,13 @@ const { t } = useI18n()
|
|||||||
// Alias local pour la lisibilite du template.
|
// Alias local pour la lisibilite du template.
|
||||||
const model = computed(() => props.modelValue)
|
const model = computed(() => props.modelValue)
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : on retire les caracteres parasites a la
|
// Filtrage des caracteres parasites : porte par les masks maska sur les champs
|
||||||
// frappe. Noms = profil personne, fonction = texte libre, email = profil email.
|
// (PERSON_NAME_MASK / FREE_TEXT_MASK), filtrage natif au focus/curseur. L'email n'a
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof ProviderContactFormDraft, (v: string) => string>> = {
|
// pas de mask (ERP-101 : validation de format via Assert\Email + erreur inline).
|
||||||
lastName: sanitizePersonName,
|
|
||||||
firstName: sanitizePersonName,
|
|
||||||
jobTitle: sanitizeFreeText,
|
|
||||||
email: sanitizeEmail,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof ProviderContactFormDraft>(field: K, value: ProviderContactFormDraft[K]): void {
|
function update<K extends keyof ProviderContactFormDraft>(field: K, value: ProviderContactFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as ProviderContactFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Revele le 2e numero (max 1 secondaire, le « + » disparait). */
|
/** Revele le 2e numero (max 1 secondaire, le « + » disparait). */
|
||||||
|
|||||||
@@ -20,12 +20,12 @@
|
|||||||
<!-- ── Bloc principal (pre-rempli, editable si `manage`) ──────────── -->
|
<!-- ── Bloc principal (pre-rempli, editable si `manage`) ──────────── -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
:label="t('technique.providers.form.main.companyName')"
|
:label="t('technique.providers.form.main.companyName')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="businessReadonly"
|
:disabled="businessReadonly"
|
||||||
:error="mainErrors.errors.companyName"
|
:error="mainErrors.errors.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelectCheckbox
|
<MalioSelectCheckbox
|
||||||
:model-value="main.categoryIris"
|
:model-value="main.categoryIris"
|
||||||
@@ -147,12 +147,12 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
:label="t('technique.providers.form.accounting.accountNumber')"
|
:label="t('technique.providers.form.accounting.accountNumber')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.accountNumber"
|
:error="accountingErrors.errors.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.tvaModeIri"
|
:model-value="accounting.tvaModeIri"
|
||||||
@@ -165,12 +165,12 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
:label="t('technique.providers.form.accounting.nTva')"
|
:label="t('technique.providers.form.accounting.nTva')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.nTva"
|
:error="accountingErrors.errors.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.paymentDelayIri"
|
:model-value="accounting.paymentDelayIri"
|
||||||
@@ -229,20 +229,20 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
:label="t('technique.providers.form.accounting.ribBic')"
|
:label="t('technique.providers.form.accounting.ribBic')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
:label="t('technique.providers.form.accounting.ribIban')"
|
:label="t('technique.providers.form.accounting.ribIban')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="ribErrors[index]?.iban"
|
:error="ribErrors[index]?.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -318,7 +318,7 @@ import {
|
|||||||
} from '~/modules/technique/types/providerForm'
|
} from '~/modules/technique/types/providerForm'
|
||||||
import { extractApiErrorMessage } from '~/shared/utils/api'
|
import { extractApiErrorMessage } from '~/shared/utils/api'
|
||||||
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque SIREN : 9 chiffres (la normalisation finale reste serveur).
|
// Masque SIREN : 9 chiffres (la normalisation finale reste serveur).
|
||||||
const SIREN_MASK = '#########'
|
const SIREN_MASK = '#########'
|
||||||
|
|||||||
@@ -19,12 +19,12 @@
|
|||||||
Selecteur de site present ici (RG-3.03, relation directe). -->
|
Selecteur de site present ici (RG-3.03, relation directe). -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.companyName"
|
v-model="main.companyName"
|
||||||
:label="t('technique.providers.form.main.companyName')"
|
:label="t('technique.providers.form.main.companyName')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="mainLocked"
|
:disabled="mainLocked"
|
||||||
:error="mainErrors.errors.companyName"
|
:error="mainErrors.errors.companyName"
|
||||||
@update:model-value="(v: string) => main.companyName = sanitizeFreeText(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelectCheckbox
|
<MalioSelectCheckbox
|
||||||
:model-value="main.categoryIris"
|
:model-value="main.categoryIris"
|
||||||
@@ -146,12 +146,12 @@
|
|||||||
:error="accountingErrors.errors.siren"
|
:error="accountingErrors.errors.siren"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.accountNumber"
|
v-model="accounting.accountNumber"
|
||||||
:label="t('technique.providers.form.accounting.accountNumber')"
|
:label="t('technique.providers.form.accounting.accountNumber')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.accountNumber"
|
:error="accountingErrors.errors.accountNumber"
|
||||||
@update:model-value="(v: string) => accounting.accountNumber = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.tvaModeIri"
|
:model-value="accounting.tvaModeIri"
|
||||||
@@ -164,12 +164,12 @@
|
|||||||
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
@update:model-value="(v: string | number | null) => accounting.tvaModeIri = v === null ? null : String(v)"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="accounting.nTva"
|
v-model="accounting.nTva"
|
||||||
:label="t('technique.providers.form.accounting.nTva')"
|
:label="t('technique.providers.form.accounting.nTva')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="accountingErrors.errors.nTva"
|
:error="accountingErrors.errors.nTva"
|
||||||
@update:model-value="(v: string) => accounting.nTva = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioSelect
|
<MalioSelect
|
||||||
:model-value="accounting.paymentDelayIri"
|
:model-value="accounting.paymentDelayIri"
|
||||||
@@ -229,20 +229,20 @@
|
|||||||
:error="ribErrors[index]?.label"
|
:error="ribErrors[index]?.label"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.bic"
|
v-model="rib.bic"
|
||||||
:label="t('technique.providers.form.accounting.ribBic')"
|
:label="t('technique.providers.form.accounting.ribBic')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="ribErrors[index]?.bic"
|
:error="ribErrors[index]?.bic"
|
||||||
@update:model-value="(v: string) => rib.bic = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="rib.iban"
|
v-model="rib.iban"
|
||||||
:label="t('technique.providers.form.accounting.ribIban')"
|
:label="t('technique.providers.form.accounting.ribIban')"
|
||||||
|
:mask="CODE_ALNUM_MASK"
|
||||||
:disabled="accountingReadonly"
|
:disabled="accountingReadonly"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="ribErrors[index]?.iban"
|
:error="ribErrors[index]?.iban"
|
||||||
@update:model-value="(v: string) => rib.iban = sanitizeCodeAlnum(v)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -302,7 +302,7 @@ import {
|
|||||||
} from '~/modules/technique/utils/forms/providerAccounting'
|
} from '~/modules/technique/utils/forms/providerAccounting'
|
||||||
import { extractApiErrorMessage } from '~/shared/utils/api'
|
import { extractApiErrorMessage } from '~/shared/utils/api'
|
||||||
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
import { isRowRemovable } from '~/shared/utils/collectionRow'
|
||||||
import { sanitizeCodeAlnum, sanitizeFreeText } from '~/shared/utils/textSanitize'
|
import { CODE_ALNUM_MASK, FREE_TEXT_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque SIREN : 9 chiffres (la normalisation finale reste serveur).
|
// Masque SIREN : 9 chiffres (la normalisation finale reste serveur).
|
||||||
const SIREN_MASK = '#########'
|
const SIREN_MASK = '#########'
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
v-else
|
v-else
|
||||||
:model-value="model.city"
|
:model-value="model.city"
|
||||||
:label="t('transport.carriers.form.address.city')"
|
:label="t('transport.carriers.form.address.city')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="true"
|
:required="true"
|
||||||
@@ -87,6 +88,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.streetComplement"
|
:model-value="model.streetComplement"
|
||||||
:label="t('transport.carriers.form.address.streetComplement')"
|
:label="t('transport.carriers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:error="errors?.streetComplement"
|
||||||
@@ -98,7 +100,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
|
||||||
import type { CarrierAddressFormDraft } from '~/modules/transport/types/carrierForm'
|
import type { CarrierAddressFormDraft } from '~/modules/transport/types/carrierForm'
|
||||||
import { sanitizeAddress } from '~/shared/utils/textSanitize'
|
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
interface RefOption {
|
interface RefOption {
|
||||||
value: string
|
value: string
|
||||||
@@ -160,21 +162,13 @@ const addressLoading = ref(false)
|
|||||||
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
// Conserve les suggestions d'adresse pour retrouver ville/CP au moment du select.
|
||||||
let lastAddressSuggestions: AddressSuggestion[] = []
|
let lastAddressSuggestions: AddressSuggestion[] = []
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : voie / complement / ville = profil
|
// Filtrage des caracteres parasites : porte par le mask ADDRESS_MASK (maska) sur les
|
||||||
// adresse. Le code postal (masque numerique) n'est pas filtre ici.
|
// champs texte editables (complement, ville en mode degrade). La voie en autocomplete
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof CarrierAddressFormDraft, (v: string) => string>> = {
|
// (BAN) et la ville en select ne sont pas masquees (le back valide via Assert\Regex).
|
||||||
street: sanitizeAddress,
|
|
||||||
streetComplement: sanitizeAddress,
|
|
||||||
city: sanitizeAddress,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Emet un nouveau brouillon avec le champ modifie (immutabilite), sanitise si besoin. */
|
/** Emet un nouveau brouillon avec le champ modifie (immutabilite). */
|
||||||
function update<K extends keyof CarrierAddressFormDraft>(field: K, value: CarrierAddressFormDraft[K]): void {
|
function update<K extends keyof CarrierAddressFormDraft>(field: K, value: CarrierAddressFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as CarrierAddressFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
/** Previent le parent (toast unique) que l'autocompletion est indisponible. */
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.lastName"
|
:model-value="model.lastName"
|
||||||
:label="t('transport.carriers.form.contact.lastName')"
|
:label="t('transport.carriers.form.contact.lastName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.lastName"
|
:error="errors?.lastName"
|
||||||
@@ -22,6 +23,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.firstName"
|
:model-value="model.firstName"
|
||||||
:label="t('transport.carriers.form.contact.firstName')"
|
:label="t('transport.carriers.form.contact.firstName')"
|
||||||
|
:mask="PERSON_NAME_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.firstName"
|
:error="errors?.firstName"
|
||||||
@@ -33,6 +35,7 @@
|
|||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.jobTitle"
|
:model-value="model.jobTitle"
|
||||||
:label="t('transport.carriers.form.contact.jobTitle')"
|
:label="t('transport.carriers.form.contact.jobTitle')"
|
||||||
|
:mask="FREE_TEXT_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.jobTitle"
|
:error="errors?.jobTitle"
|
||||||
@@ -77,7 +80,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { CarrierContactFormDraft } from '~/modules/transport/types/carrierForm'
|
import type { CarrierContactFormDraft } from '~/modules/transport/types/carrierForm'
|
||||||
import { sanitizeEmail, sanitizeFreeText, sanitizePersonName } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK, PERSON_NAME_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
// Masque téléphone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
// Masque téléphone FR : 5 groupes de 2 chiffres (la normalisation finale reste serveur).
|
||||||
const PHONE_MASK = '## ## ## ## ##'
|
const PHONE_MASK = '## ## ## ## ##'
|
||||||
@@ -105,22 +108,13 @@ const { t } = useI18n()
|
|||||||
// Alias local pour la lisibilité du template.
|
// Alias local pour la lisibilité du template.
|
||||||
const model = computed(() => props.modelValue)
|
const model = computed(() => props.modelValue)
|
||||||
|
|
||||||
// Filtres de saisie par champ (ERP-193) : on retire les caractères parasites à la
|
// Filtrage des caractères parasites : porté par les masks maska sur les champs
|
||||||
// frappe. Noms = profil personne, fonction = texte libre, email = profil email.
|
// (PERSON_NAME_MASK / FREE_TEXT_MASK), filtrage natif au focus/curseur. L'email n'a
|
||||||
const FIELD_SANITIZERS: Partial<Record<keyof CarrierContactFormDraft, (v: string) => string>> = {
|
// pas de mask (ERP-101 : validation de format via Assert\Email + erreur inline).
|
||||||
lastName: sanitizePersonName,
|
|
||||||
firstName: sanitizePersonName,
|
|
||||||
jobTitle: sanitizeFreeText,
|
|
||||||
email: sanitizeEmail,
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Émet un nouveau brouillon avec le champ modifié (immutabilité), sanitisé si besoin. */
|
/** Émet un nouveau brouillon avec le champ modifié (immutabilité). */
|
||||||
function update<K extends keyof CarrierContactFormDraft>(field: K, value: CarrierContactFormDraft[K]): void {
|
function update<K extends keyof CarrierContactFormDraft>(field: K, value: CarrierContactFormDraft[K]): void {
|
||||||
const sanitizer = FIELD_SANITIZERS[field]
|
emit('update:modelValue', { ...props.modelValue, [field]: value })
|
||||||
const next = (sanitizer && typeof value === 'string')
|
|
||||||
? (sanitizer(value) as CarrierContactFormDraft[K])
|
|
||||||
: value
|
|
||||||
emit('update:modelValue', { ...props.modelValue, [field]: next })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Révèle le 2e numéro (max 1 secondaire, le « + » disparaît). */
|
/** Révèle le 2e numéro (max 1 secondaire, le « + » disparaît). */
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
<!-- ── Formulaire principal (éditable, PATCH partiel) ─────────────── -->
|
<!-- ── Formulaire principal (éditable, PATCH partiel) ─────────────── -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.name"
|
v-model="main.name"
|
||||||
@update:model-value="(v: string) => main.name = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('transport.carriers.form.main.name')"
|
:label="t('transport.carriers.form.main.name')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:error="mainErrors.errors.name"
|
:error="mainErrors.errors.name"
|
||||||
@@ -220,7 +220,7 @@ import { useCarrierForm } from '~/modules/transport/composables/useCarrierForm'
|
|||||||
import { useCarrier } from '~/modules/transport/composables/useCarrier'
|
import { useCarrier } from '~/modules/transport/composables/useCarrier'
|
||||||
import type { QualimatCarrierRow } from '~/modules/transport/composables/useQualimatSearch'
|
import type { QualimatCarrierRow } from '~/modules/transport/composables/useQualimatSearch'
|
||||||
import { clampPercent, LIOT_PLATES_MASK, sanitizeDecimal } from '~/modules/transport/utils/forms/numberInput'
|
import { clampPercent, LIOT_PLATES_MASK, sanitizeDecimal } from '~/modules/transport/utils/forms/numberInput'
|
||||||
import { sanitizeFreeText } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
interface SelectOption {
|
interface SelectOption {
|
||||||
value: string
|
value: string
|
||||||
|
|||||||
@@ -19,8 +19,8 @@
|
|||||||
seule pour un transporteur QUALIMAT (saisie assistee, onglet Qualimat). -->
|
seule pour un transporteur QUALIMAT (saisie assistee, onglet Qualimat). -->
|
||||||
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
<div class="mt-[48px] grid grid-cols-3 xl:grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="main.name"
|
v-model="main.name"
|
||||||
@update:model-value="(v: string) => main.name = sanitizeFreeText(v)"
|
:mask="FREE_TEXT_MASK"
|
||||||
:label="t('transport.carriers.form.main.name')"
|
:label="t('transport.carriers.form.main.name')"
|
||||||
:required="true"
|
:required="true"
|
||||||
:disabled="mainLocked"
|
:disabled="mainLocked"
|
||||||
@@ -314,7 +314,7 @@ import CarrierQualimatTab from '~/modules/transport/components/CarrierQualimatTa
|
|||||||
import { useCarrierForm } from '~/modules/transport/composables/useCarrierForm'
|
import { useCarrierForm } from '~/modules/transport/composables/useCarrierForm'
|
||||||
import type { QualimatCarrierRow } from '~/modules/transport/composables/useQualimatSearch'
|
import type { QualimatCarrierRow } from '~/modules/transport/composables/useQualimatSearch'
|
||||||
import { clampPercent, LIOT_PLATES_MASK, sanitizeDecimal } from '~/modules/transport/utils/forms/numberInput'
|
import { clampPercent, LIOT_PLATES_MASK, sanitizeDecimal } from '~/modules/transport/utils/forms/numberInput'
|
||||||
import { sanitizeFreeText } from '~/shared/utils/textSanitize'
|
import { FREE_TEXT_MASK } from '~/shared/utils/textSanitize'
|
||||||
|
|
||||||
interface SelectOption {
|
interface SelectOption {
|
||||||
value: string
|
value: string
|
||||||
|
|||||||
@@ -1,70 +1,65 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { Mask, type MaskInputOptions } from 'maska'
|
||||||
import {
|
import {
|
||||||
sanitizeAddress,
|
ADDRESS_MASK,
|
||||||
sanitizeCodeAlnum,
|
CODE_ALNUM_MASK,
|
||||||
sanitizeEmail,
|
FREE_TEXT_MASK,
|
||||||
sanitizeFreeText,
|
PERSON_NAME_MASK,
|
||||||
sanitizePersonName,
|
|
||||||
} from '../textSanitize'
|
} from '../textSanitize'
|
||||||
|
|
||||||
describe('sanitizePersonName', () => {
|
/** Reproduit le traitement maska au runtime (MaskInput) : preProcess puis masked. */
|
||||||
|
function apply(mask: MaskInputOptions, value: string): string {
|
||||||
|
const pre = mask.preProcess ? mask.preProcess(value) : value
|
||||||
|
return new Mask(mask).masked(pre)
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('PERSON_NAME_MASK', () => {
|
||||||
it('garde lettres accentuees, espace, apostrophe, tiret, point', () => {
|
it('garde lettres accentuees, espace, apostrophe, tiret, point', () => {
|
||||||
expect(sanitizePersonName('Jean-Pierre')).toBe('Jean-Pierre')
|
expect(apply(PERSON_NAME_MASK, 'Jean-Pierre')).toBe('Jean-Pierre')
|
||||||
expect(sanitizePersonName('O’Brien')).toBe('O’Brien')
|
expect(apply(PERSON_NAME_MASK, 'O’Brien')).toBe('O’Brien')
|
||||||
expect(sanitizePersonName("D'Angelo")).toBe("D'Angelo")
|
expect(apply(PERSON_NAME_MASK, "D'Angelo")).toBe("D'Angelo")
|
||||||
expect(sanitizePersonName('Saint-Étienne J.')).toBe('Saint-Étienne J.')
|
expect(apply(PERSON_NAME_MASK, 'Saint-Étienne J.')).toBe('Saint-Étienne J.')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('retire chiffres et caracteres parasites', () => {
|
it('retire chiffres et caracteres parasites (ou qu\'ils soient)', () => {
|
||||||
expect(sanitizePersonName('Dupont²³')).toBe('Dupont')
|
expect(apply(PERSON_NAME_MASK, 'Dupont²³')).toBe('Dupont')
|
||||||
expect(sanitizePersonName('Jean§&#~|')).toBe('Jean')
|
expect(apply(PERSON_NAME_MASK, 'Jean§&#~|')).toBe('Jean')
|
||||||
expect(sanitizePersonName('Marie123')).toBe('Marie')
|
expect(apply(PERSON_NAME_MASK, 'Ma§rie123')).toBe('Marie') // parasite AU MILIEU
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sanitizeFreeText', () => {
|
describe('FREE_TEXT_MASK', () => {
|
||||||
it('garde &, /, parentheses, degre, chiffres (raison sociale / fonction)', () => {
|
it('garde &, /, parentheses, degre, chiffres', () => {
|
||||||
expect(sanitizeFreeText('Dupont & Fils')).toBe('Dupont & Fils')
|
expect(apply(FREE_TEXT_MASK, 'Dupont & Fils')).toBe('Dupont & Fils')
|
||||||
expect(sanitizeFreeText('Resp. Achats/Ventes')).toBe('Resp. Achats/Ventes')
|
expect(apply(FREE_TEXT_MASK, 'Resp. Achats/Ventes')).toBe('Resp. Achats/Ventes')
|
||||||
expect(sanitizeFreeText('SARL Léon (Pôle n°2)')).toBe('SARL Léon (Pôle n°2)')
|
expect(apply(FREE_TEXT_MASK, 'SARL Léon (Pôle n°2)')).toBe('SARL Léon (Pôle n°2)')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('retire les parasites ²³§~#|', () => {
|
it('retire les parasites ²³§~#|', () => {
|
||||||
expect(sanitizeFreeText('ACME²³§')).toBe('ACME')
|
expect(apply(FREE_TEXT_MASK, 'ACME²³§')).toBe('ACME')
|
||||||
expect(sanitizeFreeText('Test~#|<>{}')).toBe('Test')
|
expect(apply(FREE_TEXT_MASK, 'Te~#|st<>{}')).toBe('Test')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sanitizeAddress', () => {
|
describe('ADDRESS_MASK', () => {
|
||||||
it('garde chiffres, virgule, point, apostrophe, slash, degre, tiret', () => {
|
it('garde chiffres, virgule, point, apostrophe, slash, degre, tiret', () => {
|
||||||
expect(sanitizeAddress('12 bis, rue de l’Église')).toBe('12 bis, rue de l’Église')
|
expect(apply(ADDRESS_MASK, '12 bis, rue de l’Église')).toBe('12 bis, rue de l’Église')
|
||||||
expect(sanitizeAddress('Bât. n°3 - Zone A/B')).toBe('Bât. n°3 - Zone A/B')
|
expect(apply(ADDRESS_MASK, 'Bât. n°3 - Zone A/B')).toBe('Bât. n°3 - Zone A/B')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('retire les parasites', () => {
|
it('retire les parasites', () => {
|
||||||
expect(sanitizeAddress('5 rue X²³§&')).toBe('5 rue X')
|
expect(apply(ADDRESS_MASK, '5 rue X²³§&')).toBe('5 rue X')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('sanitizeEmail', () => {
|
describe('CODE_ALNUM_MASK', () => {
|
||||||
it('garde les caracteres email valides', () => {
|
|
||||||
expect(sanitizeEmail('jean.dupont+pro@acme-corp.fr')).toBe('jean.dupont+pro@acme-corp.fr')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('retire espaces et parasites', () => {
|
|
||||||
expect(sanitizeEmail('jean §² dupont@acme.fr')).toBe('jeandupont@acme.fr')
|
|
||||||
expect(sanitizeEmail('a&b#c@x.fr')).toBe('abc@x.fr')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('sanitizeCodeAlnum', () => {
|
|
||||||
it('force la majuscule et ne garde que A-Z 0-9', () => {
|
it('force la majuscule et ne garde que A-Z 0-9', () => {
|
||||||
expect(sanitizeCodeAlnum('411dupont')).toBe('411DUPONT')
|
expect(apply(CODE_ALNUM_MASK, '411dupont')).toBe('411DUPONT')
|
||||||
expect(sanitizeCodeAlnum('FR 12 345')).toBe('FR12345')
|
expect(apply(CODE_ALNUM_MASK, 'FR 12 345')).toBe('FR12345')
|
||||||
expect(sanitizeCodeAlnum('4-11.000§')).toBe('411000')
|
expect(apply(CODE_ALNUM_MASK, '4-11.000§')).toBe('411000')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('chaine vide reste vide', () => {
|
it('chaine vide reste vide', () => {
|
||||||
expect(sanitizeCodeAlnum('')).toBe('')
|
expect(apply(CODE_ALNUM_MASK, '')).toBe('')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,58 +1,47 @@
|
|||||||
/**
|
/**
|
||||||
* Filtres de saisie texte (retour metier ERP-193) : on retire a la frappe / au
|
* Masks de saisie texte (retour metier ERP-193) : filtrage NATIF (maska) des
|
||||||
* collage les caracteres parasites (« ²³§~#| … ») des champs texte libres.
|
* caracteres parasites (« ²³§~#| … ») dans les champs texte libres. maska gere le
|
||||||
|
* focus et le curseur (contrairement a un nettoyage manuel sur @update qui laissait
|
||||||
|
* le caractere affiche jusqu'a la frappe suivante).
|
||||||
*
|
*
|
||||||
* Miroir FRONT des patterns back `App\Shared\Domain\Validation\TextInputPattern`
|
* Miroir FRONT des patterns back `App\Shared\Domain\Validation\TextInputPattern`
|
||||||
* (allow-list par famille de champ). Le back reste l'autorite (Assert\Regex →
|
* (allow-list par famille de champ). Le back reste l'autorite (Assert\Regex →
|
||||||
* 422 inline via useFormErrors) ; ces fonctions ne font que le confort de saisie.
|
* 422 inline via useFormErrors) ; ces masks ne font que le confort de saisie.
|
||||||
* Purs / testables.
|
|
||||||
*
|
*
|
||||||
* IMPORTANT : garder les classes de caracteres STRICTEMENT alignees sur le back
|
* IMPORTANT : garder les classes de caracteres STRICTEMENT alignees sur le back.
|
||||||
* (toute divergence = soit un caractere bloque au front mais accepte au back, soit
|
*
|
||||||
* l'inverse → 422 surprise).
|
* L'EMAIL n'a PAS de mask (decision ERP-101 : un email n'a pas de structure fixe,
|
||||||
|
* on valide le FORMAT via Assert\Email + erreur inline, jamais via un masque).
|
||||||
*/
|
*/
|
||||||
|
import type { MaskInputOptions } from 'maska'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Noms de personnes (Nom, Prenom, Dirigeant) : lettres (accents), espace,
|
* Construit un mask maska « jeu de caracteres autorise, longueur libre » :
|
||||||
* apostrophe droite/courbe, tiret, point.
|
* - `preProcess` retire d'abord TOUT caractere hors charset, OU QU'IL SOIT (un
|
||||||
|
* masque positionnel seul s'arreterait au 1er caractere invalide car le token
|
||||||
|
* `multiple` est glouton) ;
|
||||||
|
* - le token `P` (`multiple`) laisse ensuite passer le reste, sans limite de longueur.
|
||||||
|
*
|
||||||
|
* @param pattern classe des caracteres AUTORISES (1 caractere, sans flag global)
|
||||||
|
* @param strip negation de `pattern`, flag global (retire les interdits)
|
||||||
|
* @param upper force la majuscule (codes : n° compte / TVA / IBAN / BIC)
|
||||||
*/
|
*/
|
||||||
export function sanitizePersonName(value: string): string {
|
function charsetMask(pattern: RegExp, strip: RegExp, upper = false): MaskInputOptions {
|
||||||
return value.replace(/[^\p{L}\p{M} '’.-]/gu, '')
|
return {
|
||||||
|
mask: 'P',
|
||||||
|
tokens: { P: { pattern, multiple: true } },
|
||||||
|
preProcess: (v: string) => (upper ? v.toUpperCase() : v).replace(strip, ''),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/** Noms de personnes (Nom, Prenom, Dirigeant) : lettres (accents), espace, apostrophe, tiret, point. */
|
||||||
* Texte societe / libre (Raison sociale, Concurrents, Fonction) : nom + chiffres,
|
export const PERSON_NAME_MASK = charsetMask(/[\p{L}\p{M} '’.-]/u, /[^\p{L}\p{M} '’.-]/gu)
|
||||||
* virgule, esperluette, slash, parentheses, degre.
|
|
||||||
*/
|
|
||||||
export function sanitizeFreeText(value: string): string {
|
|
||||||
// 0-9 (et pas \p{N}) : \p{N} engloberait les exposants ² ³ — justement parasites.
|
|
||||||
return value.replace(/[^\p{L}\p{M}0-9 '’.,&/()°-]/gu, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** Texte societe / libre (Raison sociale, Concurrents, Fonction) : + chiffres, virgule, &, /, parentheses, degre. */
|
||||||
* Adresse (voie, complement, ville) : lettres, chiffres, espace, apostrophe,
|
export const FREE_TEXT_MASK = charsetMask(/[\p{L}\p{M}0-9 '’.,&/()°-]/u, /[^\p{L}\p{M}0-9 '’.,&/()°-]/gu)
|
||||||
* point, virgule, slash, degre, tiret.
|
|
||||||
*/
|
|
||||||
export function sanitizeAddress(value: string): string {
|
|
||||||
// 0-9 (et pas \p{N}) : evite de laisser passer les exposants ² ³.
|
|
||||||
return value.replace(/[^\p{L}\p{M}0-9 '’.,/°-]/gu, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** Adresse (voie, complement, ville) : lettres, chiffres, espace, apostrophe, point, virgule, slash, degre, tiret. */
|
||||||
* Codes alphanumeriques majuscules (N° de compte comptable, N° de TVA, IBAN, BIC) :
|
export const ADDRESS_MASK = charsetMask(/[\p{L}\p{M}0-9 '’.,/°-]/u, /[^\p{L}\p{M}0-9 '’.,/°-]/gu)
|
||||||
* uniquement A-Z et 0-9, majuscule forcee.
|
|
||||||
*/
|
|
||||||
export function sanitizeCodeAlnum(value: string): string {
|
|
||||||
return value.toUpperCase().replace(/[^A-Z0-9]/g, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** Codes alphanumeriques majuscules (N° de compte, N° de TVA, IBAN, BIC) : A-Z et 0-9, majuscule forcee. */
|
||||||
* Email : retire espaces et caracteres impossibles dans une adresse, en gardant
|
export const CODE_ALNUM_MASK = charsetMask(/[A-Z0-9]/, /[^A-Z0-9]/g, true)
|
||||||
* le jeu de caracteres email valides (lettres, chiffres, @ . _ % + - '). La
|
|
||||||
* validation de FORMAT reste au back (Assert\Email) ; ici on bloque juste les
|
|
||||||
* parasites (« ²³§~#| … ») a la frappe. La normalisation lowercase est portee par
|
|
||||||
* MalioInputEmail (prop `lowercase`), on ne la duplique pas.
|
|
||||||
*/
|
|
||||||
export function sanitizeEmail(value: string): string {
|
|
||||||
return value.replace(/[^A-Za-z0-9@._%+'-]/g, '')
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user