feat : refonte des blocs adresse — 4 colonnes, titre noir, separateurs (ERP-196)
This commit is contained in:
@@ -453,6 +453,7 @@
|
|||||||
"add": "Nouveau contact"
|
"add": "Nouveau contact"
|
||||||
},
|
},
|
||||||
"address": {
|
"address": {
|
||||||
|
"title": "Adresse {n}",
|
||||||
"sites": "Sites",
|
"sites": "Sites",
|
||||||
"contacts": "Contact(s) rattaché(s)",
|
"contacts": "Contact(s) rattaché(s)",
|
||||||
"country": "Pays",
|
"country": "Pays",
|
||||||
@@ -629,6 +630,7 @@
|
|||||||
"uploadFailed": "Le téléversement de la décharge a échoué."
|
"uploadFailed": "Le téléversement de la décharge a échoué."
|
||||||
},
|
},
|
||||||
"address": {
|
"address": {
|
||||||
|
"title": "Adresse",
|
||||||
"country": "Pays",
|
"country": "Pays",
|
||||||
"postalCode": "Code postal",
|
"postalCode": "Code postal",
|
||||||
"city": "Ville",
|
"city": "Ville",
|
||||||
|
|||||||
@@ -1,203 +1,211 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative grid grid-cols-4 gap-x-[44px] gap-y-4 bg-white py-4 pl-[28px] pr-[60px] shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]">
|
<!-- Bloc a plat (sans box-shadow) : un filet noir 1px le separe du suivant
|
||||||
<!-- ariaLabel via v-bind objet (prop camelCase ; aria-* serait un attribut HTML). -->
|
(pas de bordure sous le dernier bloc). -->
|
||||||
<MalioButtonIcon
|
<div class="pb-[20px]" :class="{ 'border-b border-black': !last }">
|
||||||
v-if="removable && !readonly && !disabled"
|
<!-- En-tete : titre du bloc (noir) a gauche, poubelle de suppression a droite. -->
|
||||||
icon="mdi:delete-outline"
|
<div class="flex items-center justify-between">
|
||||||
variant="ghost"
|
<h2 class="text-[20px] font-semibold text-black">{{ title }}</h2>
|
||||||
button-class="absolute top-3 right-3"
|
<!-- ariaLabel via v-bind objet (prop camelCase ; aria-* serait un attribut HTML). -->
|
||||||
v-bind="{ ariaLabel: t('commercial.clients.form.address.remove') }"
|
<MalioButtonIcon
|
||||||
@click="$emit('remove')"
|
v-if="removable && !readonly && !disabled"
|
||||||
/>
|
icon="mdi:delete-outline"
|
||||||
|
variant="ghost"
|
||||||
|
button-class="p-0"
|
||||||
|
v-bind="{ ariaLabel: t('commercial.clients.form.address.remove') }"
|
||||||
|
@click="$emit('remove')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Usage de l'adresse : Select unique (plus simple pour l'utilisateur)
|
<!-- Grille 4 colonnes des champs de l'adresse. -->
|
||||||
remplacant les 3 cases. Les options encodent les combinaisons valides
|
<div class="mt-6 grid grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
(exclusivite Prospect, RG-1.06/07/08) ; le back recoit toujours les
|
<!-- Usage de l'adresse : Select unique (plus simple pour l'utilisateur)
|
||||||
drapeaux isProspect / isDelivery / isBilling (aucune RG modifiee). -->
|
remplacant les 3 cases. Les options encodent les combinaisons valides
|
||||||
<!-- Erreur portee sur `isProspect` cote back (Callback type obligatoire +
|
(exclusivite Prospect, RG-1.06/07/08) ; le back recoit toujours les
|
||||||
exclusivite prospect) -> affichee sous le select Type d'adresse. -->
|
drapeaux isProspect / isDelivery / isBilling (aucune RG modifiee). -->
|
||||||
<MalioSelect
|
<!-- Erreur portee sur `isProspect` cote back (Callback type obligatoire +
|
||||||
:model-value="addressType"
|
exclusivite prospect) -> affichee sous le select Type d'adresse. -->
|
||||||
:options="addressTypeOptions"
|
<MalioSelect
|
||||||
:label="t('commercial.clients.form.address.addressType')"
|
:model-value="addressType"
|
||||||
:readonly="readonly"
|
:options="addressTypeOptions"
|
||||||
:disabled="disabled"
|
:label="t('commercial.clients.form.address.addressType')"
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.isProspect"
|
|
||||||
@update:model-value="onAddressTypeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-1.10). -->
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
:model-value="model.siteIris"
|
|
||||||
:options="siteOptions"
|
|
||||||
:label="t('commercial.clients.form.address.sites')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.sites"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Contacts rattaches (M2M, facultatif). Consultation : masque si aucun (ERP-193). -->
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
v-if="!hideEmpty || isFilled(model.contactIris)"
|
|
||||||
:model-value="model.contactIris"
|
|
||||||
:options="contactOptions"
|
|
||||||
:label="t('commercial.clients.form.address.contacts')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Email(s) de facturation : visible/obligatoire seulement si Facturation
|
|
||||||
(RG-1.11). Le « + » revele un 2e email optionnel (max 2, pendant du
|
|
||||||
telephone secondaire) qui coule dans la grille. Sinon un filler comble
|
|
||||||
la colonne pour que Categorie reparte au debut de la ligne suivante. -->
|
|
||||||
<MalioInputEmail
|
|
||||||
v-if="isBillingEmailRequired(model)"
|
|
||||||
:model-value="model.billingEmail"
|
|
||||||
:label="t('commercial.clients.form.address.billingEmail')"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:lowercase="true"
|
|
||||||
:error="errors?.billingEmail"
|
|
||||||
:addable="!model.hasSecondaryBillingEmail && !readonly"
|
|
||||||
:add-button-label="t('commercial.clients.form.address.addBillingEmail')"
|
|
||||||
@update:model-value="(v: string) => update('billingEmail', v)"
|
|
||||||
@add="revealSecondaryBillingEmail"
|
|
||||||
/>
|
|
||||||
<!-- Filler : aligne la suite de la grille (Categorie au debut de ligne).
|
|
||||||
Inutile en consultation masquee (la grille se recompose sans les
|
|
||||||
champs vides, ERP-193). -->
|
|
||||||
<div v-else-if="!hideEmpty" aria-hidden="true" />
|
|
||||||
|
|
||||||
<MalioInputEmail
|
|
||||||
v-if="isBillingEmailRequired(model) && model.hasSecondaryBillingEmail"
|
|
||||||
:model-value="model.billingEmailSecondary"
|
|
||||||
:label="t('commercial.clients.form.address.billingEmailSecondary')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:lowercase="true"
|
|
||||||
:error="errors?.billingEmailSecondary"
|
|
||||||
@update:model-value="(v: string) => update('billingEmailSecondary', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
:model-value="model.categoryIris"
|
|
||||||
:options="categoryOptions"
|
|
||||||
:label="t('commercial.clients.form.address.categories')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.categories"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('categoryIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioSelect
|
|
||||||
:model-value="model.country"
|
|
||||||
:options="countryOptions"
|
|
||||||
:label="t('commercial.clients.form.address.country')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioInputText
|
|
||||||
:model-value="model.postalCode"
|
|
||||||
:label="t('commercial.clients.form.address.postalCode')"
|
|
||||||
:mask="POSTAL_CODE_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.postalCode"
|
|
||||||
@update:model-value="onPostalCodeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Ville : MalioSelect alimente par le code postal (BAN). Si la BAN est
|
|
||||||
indisponible, bascule en saisie libre — recuperable : re-saisir le
|
|
||||||
code postal relance la recherche et repasse en select au succes. -->
|
|
||||||
<MalioSelect
|
|
||||||
v-if="!degraded"
|
|
||||||
:model-value="model.city"
|
|
||||||
:options="cityOptions"
|
|
||||||
:label="t('commercial.clients.form.address.city')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
empty-option-label=""
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="onCityChange"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else
|
|
||||||
:model-value="model.city"
|
|
||||||
:label="t('commercial.clients.form.address.city')"
|
|
||||||
:mask="ADDRESS_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="(v: string) => update('city', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Adresse + Adresse complementaire sur 2 colonnes : on wrappe car
|
|
||||||
MalioInputText/Autocomplete (inheritAttrs:false) renvoient `class`
|
|
||||||
sur l'input interne, pas sur la cellule de grille. Le wrapper porte
|
|
||||||
le col-span-2, le champ le remplit (w-full). -->
|
|
||||||
<div class="col-span-2">
|
|
||||||
<!-- Adresse : saisie assistee (BAN) en edition ; champ texte simple
|
|
||||||
seulement en lecture seule (MalioInputAutocomplete ne reaffiche pas
|
|
||||||
sa valeur liee, il n'afficherait rien en readonly). allow-create :
|
|
||||||
si la BAN ne propose rien (ou erreur), le texte saisi est CONSERVE au
|
|
||||||
blur/Entree (saisie manuelle) — sinon il serait efface. La ville reste
|
|
||||||
pilotee par le code postal ; choisir une suggestion remplit rue+ville+CP. -->
|
|
||||||
<MalioInputAutocomplete
|
|
||||||
v-if="!readonly && !disabled"
|
|
||||||
:model-value="model.street"
|
|
||||||
:options="addressOptions"
|
|
||||||
:loading="addressLoading"
|
|
||||||
:min-search-length="3"
|
|
||||||
:label="t('commercial.clients.form.address.street')"
|
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="!readonly && !disabled"
|
:required="!readonly && !disabled"
|
||||||
:error="errors?.street"
|
:error="errors?.isProspect"
|
||||||
:allow-create="true"
|
@update:model-value="onAddressTypeChange"
|
||||||
:no-results-text="t('commercial.clients.form.address.streetNotFound')"
|
/>
|
||||||
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
|
||||||
@search="onAddressSearch"
|
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-1.10). -->
|
||||||
@select="onAddressSelect"
|
<MalioSelectCheckbox
|
||||||
|
:model-value="model.siteIris"
|
||||||
|
:options="siteOptions"
|
||||||
|
:label="t('commercial.clients.form.address.sites')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.sites"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Contacts rattaches (M2M, facultatif). Consultation : masque si aucun (ERP-193). -->
|
||||||
|
<MalioSelectCheckbox
|
||||||
|
v-if="!hideEmpty || isFilled(model.contactIris)"
|
||||||
|
:model-value="model.contactIris"
|
||||||
|
:options="contactOptions"
|
||||||
|
:label="t('commercial.clients.form.address.contacts')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Email(s) de facturation : visible/obligatoire seulement si Facturation
|
||||||
|
(RG-1.11). Le « + » revele un 2e email optionnel (max 2, pendant du
|
||||||
|
telephone secondaire) qui coule dans la grille. Sinon un filler comble
|
||||||
|
la colonne pour que Categorie reparte au debut de la ligne suivante. -->
|
||||||
|
<MalioInputEmail
|
||||||
|
v-if="isBillingEmailRequired(model)"
|
||||||
|
:model-value="model.billingEmail"
|
||||||
|
:label="t('commercial.clients.form.address.billingEmail')"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:lowercase="true"
|
||||||
|
:error="errors?.billingEmail"
|
||||||
|
:addable="!model.hasSecondaryBillingEmail && !readonly"
|
||||||
|
:add-button-label="t('commercial.clients.form.address.addBillingEmail')"
|
||||||
|
@update:model-value="(v: string) => update('billingEmail', v)"
|
||||||
|
@add="revealSecondaryBillingEmail"
|
||||||
|
/>
|
||||||
|
<!-- Filler : aligne la suite de la grille (Categorie au debut de ligne).
|
||||||
|
Inutile en consultation masquee (la grille se recompose sans les
|
||||||
|
champs vides, ERP-193). -->
|
||||||
|
<div v-else-if="!hideEmpty" aria-hidden="true" />
|
||||||
|
|
||||||
|
<MalioInputEmail
|
||||||
|
v-if="isBillingEmailRequired(model) && model.hasSecondaryBillingEmail"
|
||||||
|
:model-value="model.billingEmailSecondary"
|
||||||
|
:label="t('commercial.clients.form.address.billingEmailSecondary')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:lowercase="true"
|
||||||
|
:error="errors?.billingEmailSecondary"
|
||||||
|
@update:model-value="(v: string) => update('billingEmailSecondary', v)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioSelectCheckbox
|
||||||
|
:model-value="model.categoryIris"
|
||||||
|
:options="categoryOptions"
|
||||||
|
:label="t('commercial.clients.form.address.categories')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.categories"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('categoryIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioSelect
|
||||||
|
:model-value="model.country"
|
||||||
|
:options="countryOptions"
|
||||||
|
:label="t('commercial.clients.form.address.country')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioInputText
|
||||||
|
:model-value="model.postalCode"
|
||||||
|
:label="t('commercial.clients.form.address.postalCode')"
|
||||||
|
:mask="POSTAL_CODE_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.postalCode"
|
||||||
|
@update:model-value="onPostalCodeChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Ville : MalioSelect alimente par le code postal (BAN). Si la BAN est
|
||||||
|
indisponible, bascule en saisie libre — recuperable : re-saisir le
|
||||||
|
code postal relance la recherche et repasse en select au succes. -->
|
||||||
|
<MalioSelect
|
||||||
|
v-if="!degraded"
|
||||||
|
:model-value="model.city"
|
||||||
|
:options="cityOptions"
|
||||||
|
:label="t('commercial.clients.form.address.city')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
empty-option-label=""
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.city"
|
||||||
|
@update:model-value="onCityChange"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
v-else
|
v-else
|
||||||
:model-value="model.street"
|
:model-value="model.city"
|
||||||
:label="t('commercial.clients.form.address.street')"
|
:label="t('commercial.clients.form.address.city')"
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.street"
|
|
||||||
@update:model-value="(v: string) => update('street', v)"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
|
||||||
<MalioInputText
|
|
||||||
:model-value="model.streetComplement"
|
|
||||||
:label="t('commercial.clients.form.address.streetComplement')"
|
|
||||||
:mask="ADDRESS_MASK"
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:required="!readonly && !disabled"
|
||||||
@update:model-value="(v: string) => update('streetComplement', v)"
|
:error="errors?.city"
|
||||||
|
@update:model-value="(v: string) => update('city', v)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<!-- Adresse + Adresse complementaire sur 2 colonnes : on wrappe car
|
||||||
|
MalioInputText/Autocomplete (inheritAttrs:false) renvoient `class`
|
||||||
|
sur l'input interne, pas sur la cellule de grille. Le wrapper porte
|
||||||
|
le col-span-2, le champ le remplit (w-full). -->
|
||||||
|
<div class="col-span-2">
|
||||||
|
<!-- Adresse : saisie assistee (BAN) en edition ; champ texte simple
|
||||||
|
seulement en lecture seule (MalioInputAutocomplete ne reaffiche pas
|
||||||
|
sa valeur liee, il n'afficherait rien en readonly). allow-create :
|
||||||
|
si la BAN ne propose rien (ou erreur), le texte saisi est CONSERVE au
|
||||||
|
blur/Entree (saisie manuelle) — sinon il serait efface. La ville reste
|
||||||
|
pilotee par le code postal ; choisir une suggestion remplit rue+ville+CP. -->
|
||||||
|
<MalioInputAutocomplete
|
||||||
|
v-if="!readonly && !disabled"
|
||||||
|
:model-value="model.street"
|
||||||
|
:options="addressOptions"
|
||||||
|
:loading="addressLoading"
|
||||||
|
:min-search-length="3"
|
||||||
|
:label="t('commercial.clients.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
:allow-create="true"
|
||||||
|
:no-results-text="t('commercial.clients.form.address.streetNotFound')"
|
||||||
|
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
||||||
|
@search="onAddressSearch"
|
||||||
|
@select="onAddressSelect"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else
|
||||||
|
:model-value="model.street"
|
||||||
|
:label="t('commercial.clients.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
@update:model-value="(v: string) => update('street', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
||||||
|
<MalioInputText
|
||||||
|
:model-value="model.streetComplement"
|
||||||
|
:label="t('commercial.clients.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:error="errors?.streetComplement"
|
||||||
|
@update:model-value="(v: string) => update('streetComplement', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -230,6 +238,8 @@ const props = defineProps<{
|
|||||||
/** Pays disponibles (France par defaut). */
|
/** Pays disponibles (France par defaut). */
|
||||||
countryOptions: RefOption[]
|
countryOptions: RefOption[]
|
||||||
removable?: boolean
|
removable?: boolean
|
||||||
|
/** Dernier bloc de la liste : supprime le filet de separation bas. */
|
||||||
|
last?: boolean
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|||||||
@@ -1,189 +1,198 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative grid grid-cols-4 gap-x-[44px] gap-y-4 bg-white py-4 pl-[28px] pr-[60px] shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]">
|
<!-- Bloc a plat (sans box-shadow) : un filet noir 1px le separe du suivant
|
||||||
<!-- Suppression : modal de confirmation cote parent. -->
|
(pas de bordure sous le dernier bloc). -->
|
||||||
<MalioButtonIcon
|
<div class="pb-[20px]" :class="{ 'border-b border-black': !last }">
|
||||||
v-if="removable && !readonly && !disabled"
|
<!-- En-tete : titre du bloc (noir) a gauche, poubelle de suppression a droite. -->
|
||||||
icon="mdi:delete-outline"
|
<div class="flex items-center justify-between">
|
||||||
variant="ghost"
|
<h2 class="text-[20px] font-semibold text-black">{{ title }}</h2>
|
||||||
button-class="absolute top-3 right-3"
|
<!-- Suppression : modal de confirmation cote parent. -->
|
||||||
v-bind="{ ariaLabel: t('commercial.suppliers.form.address.remove') }"
|
<MalioButtonIcon
|
||||||
@click="$emit('remove')"
|
v-if="removable && !readonly && !disabled"
|
||||||
/>
|
icon="mdi:delete-outline"
|
||||||
|
variant="ghost"
|
||||||
|
button-class="p-0"
|
||||||
|
v-bind="{ ariaLabel: t('commercial.suppliers.form.address.remove') }"
|
||||||
|
@click="$emit('remove')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Type d'adresse : Prospect / Depart / Rendu (RG-2.09). Select en attendant
|
<!-- Grille 4 colonnes des champs de l'adresse. -->
|
||||||
l'arbitrage metier (radio vs select) ; l'erreur 422 (propertyPath
|
<div class="mt-6 grid grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
`addressType`) s'affiche via la prop native :error de MalioSelect. -->
|
<!-- Type d'adresse : Prospect / Depart / Rendu (RG-2.09). Select en attendant
|
||||||
<MalioSelect
|
l'arbitrage metier (radio vs select) ; l'erreur 422 (propertyPath
|
||||||
:model-value="model.addressType"
|
`addressType`) s'affiche via la prop native :error de MalioSelect. -->
|
||||||
:options="addressTypeOptions"
|
<MalioSelect
|
||||||
:label="t('commercial.suppliers.form.address.addressType')"
|
:model-value="model.addressType"
|
||||||
:readonly="readonly"
|
:options="addressTypeOptions"
|
||||||
:disabled="disabled"
|
:label="t('commercial.suppliers.form.address.addressType')"
|
||||||
empty-option-label=""
|
:readonly="readonly"
|
||||||
:required="!readonly && !disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.addressType"
|
empty-option-label=""
|
||||||
@update:model-value="(v: string | number | null) => update('addressType', v === null ? null : (v as SupplierAddressType))"
|
:required="!readonly && !disabled"
|
||||||
/>
|
:error="errors?.addressType"
|
||||||
|
@update:model-value="(v: string | number | null) => update('addressType', v === null ? null : (v as SupplierAddressType))"
|
||||||
|
/>
|
||||||
|
|
||||||
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-2.06). -->
|
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-2.06). -->
|
||||||
<MalioSelectCheckbox
|
<MalioSelectCheckbox
|
||||||
:model-value="model.siteIris"
|
:model-value="model.siteIris"
|
||||||
:options="siteOptions"
|
:options="siteOptions"
|
||||||
:label="t('commercial.suppliers.form.address.sites')"
|
:label="t('commercial.suppliers.form.address.sites')"
|
||||||
:display-tag="true"
|
:display-tag="true"
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.sites"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Contacts rattaches (M2M, facultatif). -->
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
v-if="!hideEmpty || isFilled(model.contactIris)"
|
|
||||||
:model-value="model.contactIris"
|
|
||||||
:options="contactOptions"
|
|
||||||
:label="t('commercial.suppliers.form.address.contacts')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Filler : aligne le debut de ligne suivant sur la grille (le bloc client
|
|
||||||
porte ici l'email de facturation, absent cote fournisseur). Inutile en
|
|
||||||
consultation masquee (la grille se recompose sans les champs vides). -->
|
|
||||||
<div v-if="!hideEmpty" aria-hidden="true" />
|
|
||||||
|
|
||||||
<!-- Categories de type FOURNISSEUR (>= 1 obligatoire, RG-2.10). -->
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
:model-value="model.categoryIris"
|
|
||||||
:options="categoryOptions"
|
|
||||||
:label="t('commercial.suppliers.form.address.categories')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.categories"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('categoryIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioSelect
|
|
||||||
:model-value="model.country"
|
|
||||||
:options="countryOptions"
|
|
||||||
:label="t('commercial.suppliers.form.address.country')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioInputText
|
|
||||||
:model-value="model.postalCode"
|
|
||||||
:label="t('commercial.suppliers.form.address.postalCode')"
|
|
||||||
:mask="POSTAL_CODE_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.postalCode"
|
|
||||||
@update:model-value="onPostalCodeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
|
||||||
<MalioSelect
|
|
||||||
v-if="!degraded"
|
|
||||||
:model-value="model.city"
|
|
||||||
:options="cityOptions"
|
|
||||||
:label="t('commercial.suppliers.form.address.city')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
empty-option-label=""
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="onCityChange"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else
|
|
||||||
:model-value="model.city"
|
|
||||||
:label="t('commercial.suppliers.form.address.city')"
|
|
||||||
:mask="ADDRESS_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="(v: string) => update('city', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
|
||||||
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
|
||||||
<div class="col-span-2">
|
|
||||||
<MalioInputAutocomplete
|
|
||||||
v-if="!readonly && !disabled"
|
|
||||||
:model-value="model.street"
|
|
||||||
:options="addressOptions"
|
|
||||||
:loading="addressLoading"
|
|
||||||
:min-search-length="3"
|
|
||||||
:label="t('commercial.suppliers.form.address.street')"
|
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="!readonly && !disabled"
|
:required="!readonly && !disabled"
|
||||||
:error="errors?.street"
|
:error="errors?.sites"
|
||||||
:allow-create="true"
|
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
||||||
:no-results-text="t('commercial.suppliers.form.address.streetNotFound')"
|
/>
|
||||||
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
|
||||||
@search="onAddressSearch"
|
<!-- Contacts rattaches (M2M, facultatif). -->
|
||||||
@select="onAddressSelect"
|
<MalioSelectCheckbox
|
||||||
|
v-if="!hideEmpty || isFilled(model.contactIris)"
|
||||||
|
:model-value="model.contactIris"
|
||||||
|
:options="contactOptions"
|
||||||
|
:label="t('commercial.suppliers.form.address.contacts')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Filler : aligne le debut de ligne suivant sur la grille (le bloc client
|
||||||
|
porte ici l'email de facturation, absent cote fournisseur). Inutile en
|
||||||
|
consultation masquee (la grille se recompose sans les champs vides). -->
|
||||||
|
<div v-if="!hideEmpty" aria-hidden="true" />
|
||||||
|
|
||||||
|
<!-- Categories de type FOURNISSEUR (>= 1 obligatoire, RG-2.10). -->
|
||||||
|
<MalioSelectCheckbox
|
||||||
|
:model-value="model.categoryIris"
|
||||||
|
:options="categoryOptions"
|
||||||
|
:label="t('commercial.suppliers.form.address.categories')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.categories"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('categoryIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioSelect
|
||||||
|
:model-value="model.country"
|
||||||
|
:options="countryOptions"
|
||||||
|
:label="t('commercial.suppliers.form.address.country')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioInputText
|
||||||
|
:model-value="model.postalCode"
|
||||||
|
:label="t('commercial.suppliers.form.address.postalCode')"
|
||||||
|
:mask="POSTAL_CODE_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.postalCode"
|
||||||
|
@update:model-value="onPostalCodeChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
||||||
|
<MalioSelect
|
||||||
|
v-if="!degraded"
|
||||||
|
:model-value="model.city"
|
||||||
|
:options="cityOptions"
|
||||||
|
:label="t('commercial.suppliers.form.address.city')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
empty-option-label=""
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.city"
|
||||||
|
@update:model-value="onCityChange"
|
||||||
/>
|
/>
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
v-else
|
v-else
|
||||||
:model-value="model.street"
|
:model-value="model.city"
|
||||||
:label="t('commercial.suppliers.form.address.street')"
|
:label="t('commercial.suppliers.form.address.city')"
|
||||||
:mask="ADDRESS_MASK"
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:required="!readonly && !disabled"
|
:required="!readonly && !disabled"
|
||||||
:error="errors?.street"
|
:error="errors?.city"
|
||||||
@update:model-value="(v: string) => update('street', v)"
|
@update:model-value="(v: string) => update('city', v)"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
||||||
<MalioInputText
|
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
||||||
:model-value="model.streetComplement"
|
<div class="col-span-2">
|
||||||
:label="t('commercial.suppliers.form.address.streetComplement')"
|
<MalioInputAutocomplete
|
||||||
:mask="ADDRESS_MASK"
|
v-if="!readonly && !disabled"
|
||||||
|
:model-value="model.street"
|
||||||
|
:options="addressOptions"
|
||||||
|
:loading="addressLoading"
|
||||||
|
:min-search-length="3"
|
||||||
|
:label="t('commercial.suppliers.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
:allow-create="true"
|
||||||
|
:no-results-text="t('commercial.suppliers.form.address.streetNotFound')"
|
||||||
|
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
||||||
|
@search="onAddressSearch"
|
||||||
|
@select="onAddressSelect"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else
|
||||||
|
:model-value="model.street"
|
||||||
|
:label="t('commercial.suppliers.form.address.street')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
@update:model-value="(v: string) => update('street', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
||||||
|
<MalioInputText
|
||||||
|
:model-value="model.streetComplement"
|
||||||
|
:label="t('commercial.suppliers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:error="errors?.streetComplement"
|
||||||
|
@update:model-value="(v: string) => update('streetComplement', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Bennes : stepper (specifique fournisseur, defaut 0). En consultation, 0
|
||||||
|
reste affiche (valeur saisie) ; seul un champ vide serait masque. -->
|
||||||
|
<MalioInputNumber
|
||||||
|
v-if="!hideEmpty || isFilled(model.bennes)"
|
||||||
|
:model-value="model.bennes"
|
||||||
|
:label="t('commercial.suppliers.form.address.bennes')"
|
||||||
|
:min="0"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:error="errors?.bennes"
|
||||||
@update:model-value="(v: string) => update('streetComplement', v)"
|
@update:model-value="(v: string) => update('bennes', v)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Prestation de triage : booleen porte par l'adresse (specifique fournisseur).
|
||||||
|
Consultation : masquee si non cochee (ERP-193). -->
|
||||||
|
<MalioCheckbox
|
||||||
|
v-if="!hideEmpty || isFilled(model.triageProvider)"
|
||||||
|
id="address-triage-provider"
|
||||||
|
:label="t('commercial.suppliers.form.address.triageProvider')"
|
||||||
|
:model-value="model.triageProvider"
|
||||||
|
group-class="self-center"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
@update:model-value="(v: boolean) => update('triageProvider', v)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Bennes : stepper (specifique fournisseur, defaut 0). En consultation, 0
|
|
||||||
reste affiche (valeur saisie) ; seul un champ vide serait masque. -->
|
|
||||||
<MalioInputNumber
|
|
||||||
v-if="!hideEmpty || isFilled(model.bennes)"
|
|
||||||
:model-value="model.bennes"
|
|
||||||
:label="t('commercial.suppliers.form.address.bennes')"
|
|
||||||
:min="0"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:error="errors?.bennes"
|
|
||||||
@update:model-value="(v: string) => update('bennes', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Prestation de triage : booleen porte par l'adresse (specifique fournisseur).
|
|
||||||
Consultation : masquee si non cochee (ERP-193). -->
|
|
||||||
<MalioCheckbox
|
|
||||||
v-if="!hideEmpty || isFilled(model.triageProvider)"
|
|
||||||
id="address-triage-provider"
|
|
||||||
:label="t('commercial.suppliers.form.address.triageProvider')"
|
|
||||||
:model-value="model.triageProvider"
|
|
||||||
group-class="self-center"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
@update:model-value="(v: boolean) => update('triageProvider', v)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -210,6 +219,8 @@ const props = defineProps<{
|
|||||||
/** Pays disponibles (France par defaut). */
|
/** Pays disponibles (France par defaut). */
|
||||||
countryOptions: RefOption[]
|
countryOptions: RefOption[]
|
||||||
removable?: boolean
|
removable?: boolean
|
||||||
|
/** Dernier bloc de la liste : supprime le filet de separation bas. */
|
||||||
|
last?: boolean
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|||||||
@@ -211,6 +211,7 @@
|
|||||||
:key="address.id ?? `new-${index}`"
|
:key="address.id ?? `new-${index}`"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:category-options="addressCategoryOptions"
|
:category-options="addressCategoryOptions"
|
||||||
:site-options="siteOptions"
|
:site-options="siteOptions"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -171,6 +171,7 @@
|
|||||||
:key="view.draft.id ?? index"
|
:key="view.draft.id ?? index"
|
||||||
:model-value="view.draft"
|
:model-value="view.draft"
|
||||||
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addressViews.length - 1"
|
||||||
:category-options="view.categoryOptions"
|
:category-options="view.categoryOptions"
|
||||||
:site-options="allSiteOptions"
|
:site-options="allSiteOptions"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -210,6 +210,7 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
:title="t('commercial.clients.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:category-options="addressCategoryOptions"
|
:category-options="addressCategoryOptions"
|
||||||
:site-options="referentials.sites.value"
|
:site-options="referentials.sites.value"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -180,6 +180,7 @@
|
|||||||
:key="address.id ?? `new-${index}`"
|
:key="address.id ?? `new-${index}`"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:category-options="mainCategoryOptions"
|
:category-options="mainCategoryOptions"
|
||||||
:site-options="siteOptions"
|
:site-options="siteOptions"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -152,6 +152,7 @@
|
|||||||
:key="view.draft.id ?? index"
|
:key="view.draft.id ?? index"
|
||||||
:model-value="view.draft"
|
:model-value="view.draft"
|
||||||
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addressViews.length - 1"
|
||||||
:category-options="view.categoryOptions"
|
:category-options="view.categoryOptions"
|
||||||
:site-options="allSiteOptions"
|
:site-options="allSiteOptions"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -178,6 +178,7 @@
|
|||||||
:key="index"
|
:key="index"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
:title="t('commercial.suppliers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:category-options="referentials.categories.value"
|
:category-options="referentials.categories.value"
|
||||||
:site-options="referentials.sites.value"
|
:site-options="referentials.sites.value"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
|
|||||||
@@ -1,131 +1,140 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="relative grid grid-cols-4 gap-x-[44px] gap-y-4 bg-white py-4 pl-[28px] pr-[60px] shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]">
|
<!-- Bloc a plat (sans box-shadow) : un filet noir 1px le separe du suivant
|
||||||
<!-- Suppression : modal de confirmation cote parent. -->
|
(pas de bordure sous le dernier bloc). -->
|
||||||
<MalioButtonIcon
|
<div class="pb-[20px]" :class="{ 'border-b border-black': !last }">
|
||||||
v-if="removable && !readonly && !disabled"
|
<!-- En-tete : titre du bloc (noir) a gauche, poubelle de suppression a droite. -->
|
||||||
icon="mdi:delete-outline"
|
<div class="flex items-center justify-between">
|
||||||
variant="ghost"
|
<h2 class="text-[20px] font-semibold text-black">{{ title }}</h2>
|
||||||
button-class="absolute top-3 right-3"
|
<!-- Suppression : modal de confirmation cote parent. -->
|
||||||
v-bind="{ ariaLabel: t('technique.providers.form.address.remove') }"
|
<MalioButtonIcon
|
||||||
@click="$emit('remove')"
|
v-if="removable && !readonly && !disabled"
|
||||||
/>
|
icon="mdi:delete-outline"
|
||||||
|
variant="ghost"
|
||||||
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-3.05). -->
|
button-class="p-0"
|
||||||
<MalioSelectCheckbox
|
v-bind="{ ariaLabel: t('technique.providers.form.address.remove') }"
|
||||||
v-if="!hideEmpty || isFilled(model.siteIris)"
|
@click="$emit('remove')"
|
||||||
:model-value="model.siteIris"
|
|
||||||
:options="siteOptions"
|
|
||||||
:label="t('technique.providers.form.address.sites')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.sites"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Contacts rattaches (M2M, facultatif) : alimente par l'onglet Contact. -->
|
|
||||||
<MalioSelectCheckbox
|
|
||||||
v-if="!hideEmpty || isFilled(model.contactIris)"
|
|
||||||
:model-value="model.contactIris"
|
|
||||||
:options="contactOptions"
|
|
||||||
:label="t('technique.providers.form.address.contacts')"
|
|
||||||
:display-tag="true"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioSelect
|
|
||||||
v-if="!hideEmpty || isFilled(model.country)"
|
|
||||||
:model-value="model.country"
|
|
||||||
:options="countryOptions"
|
|
||||||
:label="t('technique.providers.form.address.country')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<MalioInputText
|
|
||||||
v-if="!hideEmpty || isFilled(model.postalCode)"
|
|
||||||
:model-value="model.postalCode"
|
|
||||||
:label="t('technique.providers.form.address.postalCode')"
|
|
||||||
:mask="POSTAL_CODE_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.postalCode"
|
|
||||||
@update:model-value="onPostalCodeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
|
||||||
<MalioSelect
|
|
||||||
v-if="!degraded && (!hideEmpty || isFilled(model.city))"
|
|
||||||
:model-value="model.city"
|
|
||||||
:options="cityOptions"
|
|
||||||
:label="t('technique.providers.form.address.city')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
empty-option-label=""
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="onCityChange"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else-if="degraded && (!hideEmpty || isFilled(model.city))"
|
|
||||||
:model-value="model.city"
|
|
||||||
:label="t('technique.providers.form.address.city')"
|
|
||||||
:mask="ADDRESS_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="(v: string) => update('city', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
|
||||||
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
|
||||||
<div v-if="!hideEmpty || isFilled(model.street)" class="col-span-2">
|
|
||||||
<MalioInputAutocomplete
|
|
||||||
v-if="!readonly && !disabled"
|
|
||||||
:model-value="model.street"
|
|
||||||
:options="addressOptions"
|
|
||||||
:loading="addressLoading"
|
|
||||||
:min-search-length="3"
|
|
||||||
:label="t('technique.providers.form.address.street')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.street"
|
|
||||||
:allow-create="true"
|
|
||||||
:no-results-text="t('technique.providers.form.address.streetNotFound')"
|
|
||||||
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
|
||||||
@search="onAddressSearch"
|
|
||||||
@select="onAddressSelect"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else
|
|
||||||
:model-value="model.street"
|
|
||||||
:label="t('technique.providers.form.address.street')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.street"
|
|
||||||
@update:model-value="(v: string) => update('street', v)"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
<!-- Grille 4 colonnes des champs de l'adresse. -->
|
||||||
|
<div class="mt-6 grid grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
|
<!-- Sites Starseed : multiselect a tags (>= 1 obligatoire, RG-3.05). -->
|
||||||
|
<MalioSelectCheckbox
|
||||||
|
v-if="!hideEmpty || isFilled(model.siteIris)"
|
||||||
|
:model-value="model.siteIris"
|
||||||
|
:options="siteOptions"
|
||||||
|
:label="t('technique.providers.form.address.sites')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.sites"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('siteIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Contacts rattaches (M2M, facultatif) : alimente par l'onglet Contact. -->
|
||||||
|
<MalioSelectCheckbox
|
||||||
|
v-if="!hideEmpty || isFilled(model.contactIris)"
|
||||||
|
:model-value="model.contactIris"
|
||||||
|
:options="contactOptions"
|
||||||
|
:label="t('technique.providers.form.address.contacts')"
|
||||||
|
:display-tag="true"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
@update:model-value="(v: (string | number)[]) => update('contactIris', v.map(String))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MalioSelect
|
||||||
|
v-if="!hideEmpty || isFilled(model.country)"
|
||||||
|
:model-value="model.country"
|
||||||
|
:options="countryOptions"
|
||||||
|
:label="t('technique.providers.form.address.country')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
||||||
|
/>
|
||||||
|
|
||||||
<MalioInputText
|
<MalioInputText
|
||||||
:model-value="model.streetComplement"
|
v-if="!hideEmpty || isFilled(model.postalCode)"
|
||||||
:label="t('technique.providers.form.address.streetComplement')"
|
:model-value="model.postalCode"
|
||||||
|
:label="t('technique.providers.form.address.postalCode')"
|
||||||
|
:mask="POSTAL_CODE_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.postalCode"
|
||||||
|
@update:model-value="onPostalCodeChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
||||||
|
<MalioSelect
|
||||||
|
v-if="!degraded && (!hideEmpty || isFilled(model.city))"
|
||||||
|
:model-value="model.city"
|
||||||
|
:options="cityOptions"
|
||||||
|
:label="t('technique.providers.form.address.city')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
empty-option-label=""
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.city"
|
||||||
|
@update:model-value="onCityChange"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else-if="degraded && (!hideEmpty || isFilled(model.city))"
|
||||||
|
:model-value="model.city"
|
||||||
|
:label="t('technique.providers.form.address.city')"
|
||||||
:mask="ADDRESS_MASK"
|
:mask="ADDRESS_MASK"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:disabled="disabled"
|
:disabled="disabled"
|
||||||
:error="errors?.streetComplement"
|
:required="!readonly && !disabled"
|
||||||
@update:model-value="(v: string) => update('streetComplement', v)"
|
:error="errors?.city"
|
||||||
|
@update:model-value="(v: string) => update('city', v)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
||||||
|
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
||||||
|
<div v-if="!hideEmpty || isFilled(model.street)" class="col-span-2">
|
||||||
|
<MalioInputAutocomplete
|
||||||
|
v-if="!readonly && !disabled"
|
||||||
|
:model-value="model.street"
|
||||||
|
:options="addressOptions"
|
||||||
|
:loading="addressLoading"
|
||||||
|
:min-search-length="3"
|
||||||
|
:label="t('technique.providers.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
:allow-create="true"
|
||||||
|
:no-results-text="t('technique.providers.form.address.streetNotFound')"
|
||||||
|
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
||||||
|
@search="onAddressSearch"
|
||||||
|
@select="onAddressSelect"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else
|
||||||
|
:model-value="model.street"
|
||||||
|
:label="t('technique.providers.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
@update:model-value="(v: string) => update('street', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="!hideEmpty || isFilled(model.streetComplement)" class="col-span-1">
|
||||||
|
<MalioInputText
|
||||||
|
:model-value="model.streetComplement"
|
||||||
|
:label="t('technique.providers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:error="errors?.streetComplement"
|
||||||
|
@update:model-value="(v: string) => update('streetComplement', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -143,6 +152,8 @@ const POSTAL_CODE_MASK = '#####'
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** Brouillon de l'adresse (v-model). */
|
/** Brouillon de l'adresse (v-model). */
|
||||||
modelValue: ProviderAddressFormDraft
|
modelValue: ProviderAddressFormDraft
|
||||||
|
/** Titre du bloc (ex: « Adresse 1 »). */
|
||||||
|
title: string
|
||||||
/** Sites Starseed disponibles. */
|
/** Sites Starseed disponibles. */
|
||||||
siteOptions: RefOption[]
|
siteOptions: RefOption[]
|
||||||
/** Contacts deja saisis, rattachables a l'adresse. */
|
/** Contacts deja saisis, rattachables a l'adresse. */
|
||||||
@@ -150,6 +161,8 @@ const props = defineProps<{
|
|||||||
/** Pays disponibles (France par defaut). */
|
/** Pays disponibles (France par defaut). */
|
||||||
countryOptions: RefOption[]
|
countryOptions: RefOption[]
|
||||||
removable?: boolean
|
removable?: boolean
|
||||||
|
/** Dernier bloc de la liste : supprime le filet de separation bas. */
|
||||||
|
last?: boolean
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ function mountBlock(overrides: Record<string, unknown> = {}, errors?: Record<str
|
|||||||
return mount(ProviderAddressBlock, {
|
return mount(ProviderAddressBlock, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: { ...emptyProviderAddress(), ...overrides },
|
modelValue: { ...emptyProviderAddress(), ...overrides },
|
||||||
|
title: 'Adresse 1',
|
||||||
siteOptions: [],
|
siteOptions: [],
|
||||||
contactOptions: [],
|
contactOptions: [],
|
||||||
countryOptions: [],
|
countryOptions: [],
|
||||||
|
|||||||
@@ -106,6 +106,8 @@
|
|||||||
v-for="(address, index) in addresses"
|
v-for="(address, index) in addresses"
|
||||||
:key="index"
|
:key="index"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
|
:title="t('technique.providers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:site-options="referentials.sites.value"
|
:site-options="referentials.sites.value"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
:country-options="countryOptions"
|
:country-options="countryOptions"
|
||||||
|
|||||||
@@ -96,6 +96,8 @@
|
|||||||
v-for="(view, index) in addressViews"
|
v-for="(view, index) in addressViews"
|
||||||
:key="index"
|
:key="index"
|
||||||
:model-value="view.draft"
|
:model-value="view.draft"
|
||||||
|
:title="t('technique.providers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addressViews.length - 1"
|
||||||
:site-options="view.siteOptions"
|
:site-options="view.siteOptions"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
:country-options="countryOptionsFor(view.draft.country)"
|
:country-options="countryOptionsFor(view.draft.country)"
|
||||||
|
|||||||
@@ -110,6 +110,8 @@
|
|||||||
v-for="(address, index) in addresses"
|
v-for="(address, index) in addresses"
|
||||||
:key="index"
|
:key="index"
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
|
:title="t('technique.providers.form.address.title', { n: index + 1 })"
|
||||||
|
:last="index === addresses.length - 1"
|
||||||
:site-options="referentials.sites.value"
|
:site-options="referentials.sites.value"
|
||||||
:contact-options="contactOptions"
|
:contact-options="contactOptions"
|
||||||
:country-options="countryOptions"
|
:country-options="countryOptions"
|
||||||
|
|||||||
@@ -1,103 +1,113 @@
|
|||||||
<template>
|
<template>
|
||||||
<!-- Adresse UNIQUE par transporteur (ERP-172) : un seul bloc, jamais supprimable. -->
|
<!-- Adresse UNIQUE par transporteur (ERP-172) : un seul bloc, jamais supprimable. -->
|
||||||
<div class="relative grid grid-cols-4 gap-x-[44px] gap-y-4 bg-white py-4 pl-[28px] pr-[60px] shadow-[0_4px_4px_0_rgba(0,0,0,0.25)]">
|
<!-- Bloc a plat (sans box-shadow) : un filet noir 1px le separe du suivant
|
||||||
<!-- Pays : prerempli « France » (RG-4.05). -->
|
(pas de bordure sous le dernier bloc). -->
|
||||||
<MalioSelect
|
<div class="pb-[20px]" :class="{ 'border-b border-black': !last }">
|
||||||
v-if="!hideEmpty || isFilled(model.country)"
|
<!-- En-tete : titre du bloc, en noir (adresse unique, sans suppression). -->
|
||||||
:model-value="model.country"
|
<div class="flex items-center justify-between">
|
||||||
:options="countryOptions"
|
<h2 class="text-[20px] font-semibold text-black">{{ title }}</h2>
|
||||||
:label="t('transport.carriers.form.address.country')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.country"
|
|
||||||
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Code postal (RG-4.06) : declenche l'autocomplete ville (BAN). -->
|
|
||||||
<MalioInputText
|
|
||||||
v-if="!hideEmpty || isFilled(model.postalCode)"
|
|
||||||
:model-value="model.postalCode"
|
|
||||||
:label="t('transport.carriers.form.address.postalCode')"
|
|
||||||
:mask="POSTAL_CODE_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.postalCode"
|
|
||||||
@update:model-value="onPostalCodeChange"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
|
||||||
<MalioSelect
|
|
||||||
v-if="!degraded && (!hideEmpty || isFilled(model.city))"
|
|
||||||
:model-value="model.city"
|
|
||||||
:options="cityOptions"
|
|
||||||
:label="t('transport.carriers.form.address.city')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
empty-option-label=""
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="onCityChange"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else-if="degraded && (!hideEmpty || isFilled(model.city))"
|
|
||||||
:model-value="model.city"
|
|
||||||
:label="t('transport.carriers.form.address.city')"
|
|
||||||
:mask="ADDRESS_MASK"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.city"
|
|
||||||
@update:model-value="(v: string) => update('city', v)"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!-- Filler : aligne le debut de la ligne suivante sur la grille. Inutile en
|
|
||||||
consultation masquee (la grille se recompose sans les champs vides). -->
|
|
||||||
<div v-if="!hideEmpty" aria-hidden="true" />
|
|
||||||
|
|
||||||
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
|
||||||
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
|
||||||
<div v-if="!hideEmpty || isFilled(model.street)" class="col-span-2">
|
|
||||||
<MalioInputAutocomplete
|
|
||||||
v-if="!readonly && !disabled"
|
|
||||||
:model-value="model.street"
|
|
||||||
:options="addressOptions"
|
|
||||||
:loading="addressLoading"
|
|
||||||
:min-search-length="3"
|
|
||||||
:label="t('transport.carriers.form.address.street')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.street"
|
|
||||||
:allow-create="true"
|
|
||||||
:no-results-text="t('transport.carriers.form.address.streetNotFound')"
|
|
||||||
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
|
||||||
@search="onAddressSearch"
|
|
||||||
@select="onAddressSelect"
|
|
||||||
/>
|
|
||||||
<MalioInputText
|
|
||||||
v-else
|
|
||||||
:model-value="model.street"
|
|
||||||
:label="t('transport.carriers.form.address.street')"
|
|
||||||
:readonly="readonly"
|
|
||||||
:disabled="disabled"
|
|
||||||
:required="!readonly && !disabled"
|
|
||||||
:error="errors?.street"
|
|
||||||
@update:model-value="(v: string) => update('street', v)"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MalioInputText
|
<!-- Grille 4 colonnes des champs de l'adresse. -->
|
||||||
v-if="!hideEmpty || isFilled(model.streetComplement)"
|
<div class="mt-6 grid grid-cols-4 gap-x-[44px] gap-y-4">
|
||||||
:model-value="model.streetComplement"
|
<!-- Pays : prerempli « France » (RG-4.05). -->
|
||||||
:label="t('transport.carriers.form.address.streetComplement')"
|
<MalioSelect
|
||||||
:mask="ADDRESS_MASK"
|
v-if="!hideEmpty || isFilled(model.country)"
|
||||||
:readonly="readonly"
|
:model-value="model.country"
|
||||||
:disabled="disabled"
|
:options="countryOptions"
|
||||||
:error="errors?.streetComplement"
|
:label="t('transport.carriers.form.address.country')"
|
||||||
@update:model-value="(v: string) => update('streetComplement', v)"
|
:readonly="readonly"
|
||||||
/>
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.country"
|
||||||
|
@update:model-value="(v: string | number | null) => update('country', String(v ?? 'France'))"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Code postal (RG-4.06) : declenche l'autocomplete ville (BAN). -->
|
||||||
|
<MalioInputText
|
||||||
|
v-if="!hideEmpty || isFilled(model.postalCode)"
|
||||||
|
:model-value="model.postalCode"
|
||||||
|
:label="t('transport.carriers.form.address.postalCode')"
|
||||||
|
:mask="POSTAL_CODE_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.postalCode"
|
||||||
|
@update:model-value="onPostalCodeChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Ville : MalioSelect alimente par le code postal (BAN). Saisie libre si BAN indispo. -->
|
||||||
|
<MalioSelect
|
||||||
|
v-if="!degraded && (!hideEmpty || isFilled(model.city))"
|
||||||
|
:model-value="model.city"
|
||||||
|
:options="cityOptions"
|
||||||
|
:label="t('transport.carriers.form.address.city')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
empty-option-label=""
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.city"
|
||||||
|
@update:model-value="onCityChange"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else-if="degraded && (!hideEmpty || isFilled(model.city))"
|
||||||
|
:model-value="model.city"
|
||||||
|
:label="t('transport.carriers.form.address.city')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.city"
|
||||||
|
@update:model-value="(v: string) => update('city', v)"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- Filler : aligne le debut de la ligne suivante sur la grille. Inutile en
|
||||||
|
consultation masquee (la grille se recompose sans les champs vides). -->
|
||||||
|
<div v-if="!hideEmpty" aria-hidden="true" />
|
||||||
|
|
||||||
|
<!-- Adresse (BAN) sur 2 colonnes + Adresse complementaire. allow-create : le
|
||||||
|
texte saisi est conserve si la BAN ne propose rien (saisie manuelle). -->
|
||||||
|
<div v-if="!hideEmpty || isFilled(model.street)" class="col-span-2">
|
||||||
|
<MalioInputAutocomplete
|
||||||
|
v-if="!readonly && !disabled"
|
||||||
|
:model-value="model.street"
|
||||||
|
:options="addressOptions"
|
||||||
|
:loading="addressLoading"
|
||||||
|
:min-search-length="3"
|
||||||
|
:label="t('transport.carriers.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
:allow-create="true"
|
||||||
|
:no-results-text="t('transport.carriers.form.address.streetNotFound')"
|
||||||
|
@update:model-value="(v: string | number | null) => update('street', v === null ? null : String(v))"
|
||||||
|
@search="onAddressSearch"
|
||||||
|
@select="onAddressSelect"
|
||||||
|
/>
|
||||||
|
<MalioInputText
|
||||||
|
v-else
|
||||||
|
:model-value="model.street"
|
||||||
|
:label="t('transport.carriers.form.address.street')"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:required="!readonly && !disabled"
|
||||||
|
:error="errors?.street"
|
||||||
|
@update:model-value="(v: string) => update('street', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<MalioInputText
|
||||||
|
v-if="!hideEmpty || isFilled(model.streetComplement)"
|
||||||
|
:model-value="model.streetComplement"
|
||||||
|
:label="t('transport.carriers.form.address.streetComplement')"
|
||||||
|
:mask="ADDRESS_MASK"
|
||||||
|
:readonly="readonly"
|
||||||
|
:disabled="disabled"
|
||||||
|
:error="errors?.streetComplement"
|
||||||
|
@update:model-value="(v: string) => update('streetComplement', v)"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -118,8 +128,12 @@ const POSTAL_CODE_MASK = '#####'
|
|||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** Brouillon de l'adresse (v-model). */
|
/** Brouillon de l'adresse (v-model). */
|
||||||
modelValue: CarrierAddressFormDraft
|
modelValue: CarrierAddressFormDraft
|
||||||
|
/** Titre du bloc (ex: « Adresse 1 »). */
|
||||||
|
title: string
|
||||||
/** Pays disponibles (France par defaut). */
|
/** Pays disponibles (France par defaut). */
|
||||||
countryOptions: RefOption[]
|
countryOptions: RefOption[]
|
||||||
|
/** Dernier bloc de la liste : supprime le filet de separation bas. */
|
||||||
|
last?: boolean
|
||||||
readonly?: boolean
|
readonly?: boolean
|
||||||
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
/** Bloc desactive (champs grises, consultation — distinct de readonly). */
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ function mountBlock(overrides: Record<string, unknown> = {}) {
|
|||||||
return mount(CarrierAddressBlock, {
|
return mount(CarrierAddressBlock, {
|
||||||
props: {
|
props: {
|
||||||
modelValue: { ...emptyCarrierAddress(), ...overrides },
|
modelValue: { ...emptyCarrierAddress(), ...overrides },
|
||||||
|
title: 'Adresse 1',
|
||||||
countryOptions: [{ value: 'France', label: 'France' }],
|
countryOptions: [{ value: 'France', label: 'France' }],
|
||||||
},
|
},
|
||||||
global: {
|
global: {
|
||||||
|
|||||||
@@ -143,6 +143,8 @@
|
|||||||
<!-- Adresse UNIQUE (ERP-172) : un seul bloc, sans ajouter/supprimer. -->
|
<!-- Adresse UNIQUE (ERP-172) : un seul bloc, sans ajouter/supprimer. -->
|
||||||
<CarrierAddressBlock
|
<CarrierAddressBlock
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
|
:title="t('transport.carriers.form.address.title')"
|
||||||
|
:last="true"
|
||||||
:country-options="countryOptions"
|
:country-options="countryOptions"
|
||||||
:errors="addressErrors"
|
:errors="addressErrors"
|
||||||
@update:model-value="(v) => address = v"
|
@update:model-value="(v) => address = v"
|
||||||
|
|||||||
@@ -123,6 +123,8 @@
|
|||||||
<!-- Adresse UNIQUE (ERP-172). -->
|
<!-- Adresse UNIQUE (ERP-172). -->
|
||||||
<CarrierAddressBlock
|
<CarrierAddressBlock
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
|
:title="t('transport.carriers.form.address.title')"
|
||||||
|
:last="true"
|
||||||
:country-options="countryOptionsFor(address.country)"
|
:country-options="countryOptionsFor(address.country)"
|
||||||
disabled
|
disabled
|
||||||
hide-empty
|
hide-empty
|
||||||
|
|||||||
@@ -180,6 +180,8 @@
|
|||||||
<!-- Adresse UNIQUE (ERP-172) : un seul bloc, sans ajouter/supprimer. -->
|
<!-- Adresse UNIQUE (ERP-172) : un seul bloc, sans ajouter/supprimer. -->
|
||||||
<CarrierAddressBlock
|
<CarrierAddressBlock
|
||||||
:model-value="address"
|
:model-value="address"
|
||||||
|
:title="t('transport.carriers.form.address.title')"
|
||||||
|
:last="true"
|
||||||
:country-options="countryOptions"
|
:country-options="countryOptions"
|
||||||
:disabled="isQualimat || isValidated('addresses')"
|
:disabled="isQualimat || isValidated('addresses')"
|
||||||
:errors="addressErrors"
|
:errors="addressErrors"
|
||||||
|
|||||||
Reference in New Issue
Block a user