fix(transport) : immatriculations LIOT filtrées via mask maska (focus/curseur natifs, plus de hack) (ERP-193)
Pull Request — Quality gate / Frontend (lint + Vitest + build) (pull_request) Failing after 12m13s
Pull Request — Quality gate / Backend (PHP CS + PHPUnit) (pull_request) Has been cancelled

This commit is contained in:
2026-06-19 14:10:49 +02:00
parent 6d3122a0b8
commit e66615d40b
4 changed files with 33 additions and 51 deletions
@@ -30,9 +30,8 @@
MalioInputText (inheritAttrs:false) renvoie `class` sur l'input. --> MalioInputText (inheritAttrs:false) renvoie `class` sur l'input. -->
<div v-if="isLiot" class="col-span-2 xl:col-span-3"> <div v-if="isLiot" class="col-span-2 xl:col-span-3">
<MalioInputText <MalioInputText
:key="liotPlatesKey" v-model="main.liotPlates"
:model-value="main.liotPlates" :mask="LIOT_PLATES_MASK"
@update:model-value="onLiotPlatesInput"
:label="t('transport.carriers.form.main.liotPlates')" :label="t('transport.carriers.form.main.liotPlates')"
:hint="t('transport.carriers.form.main.liotPlatesHint')" :hint="t('transport.carriers.form.main.liotPlatesHint')"
:required="true" :required="true"
@@ -220,7 +219,7 @@ import CarrierQualimatTab from '~/modules/transport/components/CarrierQualimatTa
import { useCarrierForm } from '~/modules/transport/composables/useCarrierForm' 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, sanitizeDecimal, sanitizeLiotPlates } 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 { sanitizeFreeText } from '~/shared/utils/textSanitize'
interface SelectOption { interface SelectOption {
@@ -371,20 +370,6 @@ function onIndexationInput(value: string): void {
} }
} }
// Immatriculations LIOT : la clé force le ré-affichage quand le filtrage laisse le
// modelValue inchangé (ex: caractère interdit seul tapé) — sinon le DOM garderait
// le caractère parasite alors que le modèle est déjà propre.
const liotPlatesKey = ref(0)
/** Saisie des immatriculations LIOT : filtre la saisie et re-synchronise si filtré. */
function onLiotPlatesInput(value: string): void {
const clean = sanitizeLiotPlates(value)
main.liotPlates = clean
if (clean !== value) {
liotPlatesKey.value += 1
}
}
function goBack(): void { function goBack(): void {
router.push(`/carriers/${carrierId}`) router.push(`/carriers/${carrierId}`)
} }
@@ -33,9 +33,8 @@
`class` sur l'input interne, pas sur la cellule de grille. --> `class` sur l'input interne, pas sur la cellule de grille. -->
<div v-if="isLiot" class="col-span-2 xl:col-span-3"> <div v-if="isLiot" class="col-span-2 xl:col-span-3">
<MalioInputText <MalioInputText
:key="liotPlatesKey" v-model="main.liotPlates"
:model-value="main.liotPlates" :mask="LIOT_PLATES_MASK"
@update:model-value="onLiotPlatesInput"
:label="t('transport.carriers.form.main.liotPlates')" :label="t('transport.carriers.form.main.liotPlates')"
:hint="t('transport.carriers.form.main.liotPlatesHint')" :hint="t('transport.carriers.form.main.liotPlatesHint')"
:required="true" :required="true"
@@ -314,7 +313,7 @@ import CarrierPriceBlock from '~/modules/transport/components/CarrierPriceBlock.
import CarrierQualimatTab from '~/modules/transport/components/CarrierQualimatTab.vue' import CarrierQualimatTab from '~/modules/transport/components/CarrierQualimatTab.vue'
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, sanitizeDecimal, sanitizeLiotPlates } 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 { sanitizeFreeText } from '~/shared/utils/textSanitize'
interface SelectOption { interface SelectOption {
@@ -580,20 +579,6 @@ function onIndexationInput(value: string): void {
} }
} }
// Immatriculations LIOT : la clé force le ré-affichage quand le filtrage laisse le
// modelValue inchangé (ex: caractère interdit seul tapé) — sinon le DOM garderait
// le caractère parasite alors que le modèle est déjà propre.
const liotPlatesKey = ref(0)
/** Saisie des immatriculations LIOT : filtre la saisie et re-synchronise si filtré. */
function onLiotPlatesInput(value: string): void {
const clean = sanitizeLiotPlates(value)
main.liotPlates = clean
if (clean !== value) {
liotPlatesKey.value += 1
}
}
/** Retour vers le repertoire transporteurs (fleche d'en-tete). */ /** Retour vers le repertoire transporteurs (fleche d'en-tete). */
function goBack(): void { function goBack(): void {
router.push('/carriers') router.push('/carriers')
@@ -1,5 +1,6 @@
import { describe, expect, it } from 'vitest' import { describe, expect, it } from 'vitest'
import { clampPercent, sanitizeDecimal, sanitizeLiotPlates } from '../numberInput' import { Mask } from 'maska'
import { clampPercent, LIOT_PLATES_MASK, sanitizeDecimal } from '../numberInput'
describe('numberInput — saisie volume / indexation (ERP-170)', () => { describe('numberInput — saisie volume / indexation (ERP-170)', () => {
it('sanitizeDecimal : ne garde que chiffres + un seul point', () => { it('sanitizeDecimal : ne garde que chiffres + un seul point', () => {
@@ -20,11 +21,13 @@ describe('numberInput — saisie volume / indexation (ERP-170)', () => {
expect(clampPercent('')).toBe('') expect(clampPercent('')).toBe('')
}) })
it('sanitizeLiotPlates : garde lettres/chiffres/tiret/point-virgule, retire espaces et reste', () => { it('LIOT_PLATES_MASK : garde lettres/chiffres/tiret/point-virgule, bloque espaces et reste', () => {
expect(sanitizeLiotPlates('AB-123-CD;EF-456-GH')).toBe('AB-123-CD;EF-456-GH') // Reproduit ce que fait maska au runtime (MaskInput) : preProcess puis masked.
expect(sanitizeLiotPlates('ab-123-cd ; ef-456-gh')).toBe('ab-123-cd;ef-456-gh') // espaces retirés const masked = (v: string) => new Mask(LIOT_PLATES_MASK).masked(LIOT_PLATES_MASK.preProcess!(v))
expect(sanitizeLiotPlates('AB 123 CD')).toBe('AB123CD') // espaces retirés expect(masked('AB-123-CD;EF-456-GH')).toBe('AB-123-CD;EF-456-GH')
expect(sanitizeLiotPlates('AB.123/CD#42')).toBe('AB123CD42') // . / # retirés expect(masked('ab-123-cd ; ef-456-gh')).toBe('ab-123-cd;ef-456-gh') // espaces retirés
expect(sanitizeLiotPlates('')).toBe('') expect(masked('AB 123 CD')).toBe('AB123CD') // espaces retirés
expect(masked('AB.123/CD#42&²²')).toBe('AB123CD42') // . / # & ² retirés
expect(masked('')).toBe('')
}) })
}) })
@@ -1,7 +1,9 @@
/** /**
* Helpers de saisie numérique du formulaire principal transporteur (ERP-170). * Helpers de saisie du formulaire principal transporteur (ERP-170).
* Champs texte restreints (volume m³ décimal, indexation plafonnée). Purs / testables. * Champs texte restreints (volume m³ décimal, indexation plafonnée, immatriculations
* LIOT via mask maska). Purs / testables.
*/ */
import type { MaskInputOptions } from 'maska'
/** /**
* Restreint une saisie à un nombre décimal : chiffres + UN seul point (RG volume m³, * Restreint une saisie à un nombre décimal : chiffres + UN seul point (RG volume m³,
@@ -28,11 +30,18 @@ export function clampPercent(value: string): string {
} }
/** /**
* Restreint la saisie des immatriculations LIOT : ne garde que lettres, chiffres, * Mask maska des immatriculations LIOT : n'autorise que lettres, chiffres, tiret et
* tiret et point-virgule (séparateur de plaques). Les espaces et tout autre * point-virgule (séparateur de plaques), longueur libre. Filtrage NATIF (maska gère
* caractère sont supprimés à la frappe / au collage. La normalisation finale * le focus et le curseur, contrairement à un nettoyage manuel). Espaces et tout autre
* (majuscules + « ; » espacé) reste au back (RG-4.13). * caractère sont bloqués à la frappe / au collage. La normalisation finale (majuscules
* + « ; » espacé) reste au back (RG-4.13).
*
* `preProcess` retire d'abord tout caractère interdit (espaces, &, ², …) OÙ QU'IL
* SOIT (le masque positionnel seul s'arrêterait au 1er caractère invalide) ; le
* token `P` en `multiple: true` laisse ensuite passer le reste (longueur libre).
*/ */
export function sanitizeLiotPlates(value: string): string { export const LIOT_PLATES_MASK: MaskInputOptions = {
return (value ?? '').replace(/[^A-Za-z0-9;-]/g, '') mask: 'P',
tokens: { P: { pattern: /[A-Za-z0-9;-]/, multiple: true } },
preProcess: (value: string) => value.replace(/[^A-Za-z0-9;-]/g, ''),
} }