diff --git a/config/packages/security.yaml b/config/packages/security.yaml
index f440a21..0152ffc 100644
--- a/config/packages/security.yaml
+++ b/config/packages/security.yaml
@@ -33,9 +33,14 @@ security:
stateless: true
provider: app_user_provider
jwt: ~
+ # API JWT stateless : pas de `target` (redirection 302) — le logout
+ # renvoie 204 via ApiLogoutSuccessListener. Une redirection generait
+ # une URL absolue basee sur le Host (en dev : l'upstream proxy
+ # « nginx », non resolvable par le navigateur => ERR_NAME_NOT_RESOLVED
+ # + ~3 s de timeout DNS). Le cookie BEARER reste efface par
+ # delete_cookies.
logout:
path: /api/logout
- target: /login
enable_csrf: false
delete_cookies:
BEARER:
diff --git a/config/sidebar.php b/config/sidebar.php
index fd45eda..7fd5bea 100644
--- a/config/sidebar.php
+++ b/config/sidebar.php
@@ -184,6 +184,9 @@ return [
// Section "Mon compte" : espace personnel. Accessible a tout user authentifie
// (aucune permission RBAC requise, tous les items restent dans `core` pour
// rester toujours presents meme quand les modules metier sont desactives).
+ // La deconnexion a quitte cette section : elle vit desormais dans le footer
+ // de la sidebar (compte connecte + lien deconnexion + version, cf.
+ // frontend/app/layouts/default.vue + useLogout).
[
'label' => 'sidebar.account.section',
'icon' => 'mdi:account-circle-outline',
@@ -194,12 +197,6 @@ return [
'icon' => 'mdi:view-dashboard-outline',
'module' => 'core',
],
- [
- 'label' => 'sidebar.account.logout',
- 'to' => '/logout',
- 'icon' => 'mdi:logout',
- 'module' => 'core',
- ],
],
],
];
diff --git a/frontend/app/layouts/default.vue b/frontend/app/layouts/default.vue
index ee46cf8..e8978b6 100644
--- a/frontend/app/layouts/default.vue
+++ b/frontend/app/layouts/default.vue
@@ -21,6 +21,45 @@
+
+
+
+
+
+
+
+
+ {{ initials }}
+ {{ username }}
+
+
+
+
v {{ version }}
+
+
+
+
+
+
+
@@ -42,6 +81,18 @@ const {isModuleActive} = useModules()
const auth = useAuthStore()
const route = useRoute()
+// Footer de la sidebar : compte connecte + deconnexion inline + version.
+const {logout: onLogout} = useLogout()
+const {version, load: loadAppVersion} = useAppVersion()
+
+const username = computed(() => auth.user?.username ?? '')
+// Pastille avatar : 1re lettre du compte (meme convention que la maquette Malio).
+const initials = computed(() => username.value.charAt(0).toUpperCase() || '?')
+
+onMounted(() => {
+ void loadAppVersion()
+})
+
// Le SiteSelector est rendu si :
// - le module Sites est actif dans config/modules.php (sinon la feature
// n'a pas de sens, cf. ticket 3 spec criteres d'acceptation) ;
diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json
index 00d3da2..d17f6f8 100644
--- a/frontend/i18n/locales/fr.json
+++ b/frontend/i18n/locales/fr.json
@@ -53,7 +53,7 @@
},
"catalog": {
"categories": "Gestion des catégories",
- "products": "Produits"
+ "products": "Catalogue produits"
}
},
"dashboard": {
@@ -72,7 +72,7 @@
"companyName": "Nom",
"categories": "Catégories",
"sites": "Site",
- "lastActivity": "Dernière modification"
+ "lastActivity": "Dernière activité"
},
"filters": {
"title": "Filtres",
@@ -218,7 +218,7 @@
"companyName": "Nom",
"categories": "Catégories",
"sites": "Site",
- "lastActivity": "Dernière modification"
+ "lastActivity": "Dernière activité"
},
"filters": {
"title": "Filtres",
@@ -389,7 +389,7 @@
"companyName": "Nom",
"categories": "Catégories",
"sites": "Site",
- "lastActivity": "Dernière modification"
+ "lastActivity": "Dernière activité"
},
"filters": {
"title": "Filtres",
@@ -745,7 +745,8 @@
"weighbridge": {
"auto": "Pesée bascule",
"manual": "Pesée manuelle",
- "confirmTitle": "Êtes-vous sûr de vouloir déclencher une pesée ?",
+ "confirmTitle": "Pesée bascule",
+ "confirmMessage": "Êtes-vous sûr de vouloir déclencher une pesée ?",
"validate": "Valider",
"unavailable": "Pont bascule indisponible — passez en pesée manuelle."
},
diff --git a/frontend/modules/catalog/components/CategoryDeleteModal.vue b/frontend/modules/catalog/components/CategoryDeleteModal.vue
index 5842904..a402328 100644
--- a/frontend/modules/catalog/components/CategoryDeleteModal.vue
+++ b/frontend/modules/catalog/components/CategoryDeleteModal.vue
@@ -1,5 +1,6 @@
,
toLabel: (member: HydraMember) => string,
+ toColor?: (member: HydraMember) => string | undefined,
): Promise {
const res = await useApi().get<{ member?: HydraMember[] }>(
url,
{ pagination: 'false', ...query },
{ headers: LD_JSON_HEADERS, toast: false },
)
- return (res.member ?? []).map(m => ({ value: m['@id'], label: toLabel(m) }))
+ return (res.member ?? []).map(m => ({
+ value: m['@id'],
+ label: toLabel(m),
+ // Couleur reportee uniquement si un extracteur est fourni (ex: sites).
+ ...(toColor ? { color: toColor(m) } : {}),
+ }))
}
/** Sites de disponibilite (libelle = nom du site). */
@@ -49,7 +63,9 @@ export function useSiteOptions() {
const options = ref([])
async function load(): Promise {
- options.value = await fetchOptions('/sites', {}, s => s.name ?? '')
+ // Sites : couleur de fond depuis l'embed + texte blanc pour rester lisible.
+ const sites = await fetchOptions('/sites', {}, s => s.name ?? '', s => s.color)
+ options.value = sites.map(o => ({ ...o, textColor: '#FFFFFF' }))
}
return { options, load }
diff --git a/frontend/modules/catalog/pages/admin/products/[id]/edit.vue b/frontend/modules/catalog/pages/admin/products/[id]/edit.vue
index 6fbb767..fde56e1 100644
--- a/frontend/modules/catalog/pages/admin/products/[id]/edit.vue
+++ b/frontend/modules/catalog/pages/admin/products/[id]/edit.vue
@@ -25,6 +25,7 @@
import { useAddressAutocomplete, type AddressSuggestion } from '~/shared/composables/useAddressAutocomplete'
-import type { CategoryOption, RefOption } from '~/modules/commercial/composables/useSupplierReferentials'
+import type { CategoryOption, RefOption } from '~/modules/commercial/types/referentials'
import type { SupplierAddressFormDraft, SupplierAddressType } from '~/modules/commercial/types/supplierForm'
import { ADDRESS_MASK } from '~/shared/utils/textSanitize'
import { isFilled } from '~/shared/utils/consultationDisplay'
diff --git a/frontend/modules/commercial/composables/__tests__/useClientReferentials.spec.ts b/frontend/modules/commercial/composables/__tests__/useClientReferentials.spec.ts
index 3991e40..34e4abe 100644
--- a/frontend/modules/commercial/composables/__tests__/useClientReferentials.spec.ts
+++ b/frontend/modules/commercial/composables/__tests__/useClientReferentials.spec.ts
@@ -45,7 +45,7 @@ describe('useClientReferentials.loadCommon (resilience ERP-102)', () => {
// Resilience : les referentiels OK sont peuples malgre l'echec de /categories.
// Le libelle d'un site est son numero de departement (2 premiers chiffres du code postal).
- expect(refs.sites.value).toEqual([{ value: '/api/sites/1', label: '86' }])
+ expect(refs.sites.value).toEqual([{ value: '/api/sites/1', label: '86', textColor: '#FFFFFF' }])
expect(refs.tvaModes.value).toEqual([{ value: '/api/x/1', label: 'Libelle X' }])
expect(refs.banks.value).toEqual([{ value: '/api/x/1', label: 'Libelle X' }])
// Pays : value = nom du pays (et non l'IRI).
@@ -63,7 +63,7 @@ describe('useClientReferentials.loadCommon (resilience ERP-102)', () => {
})
}
if (url === '/sites') {
- return Promise.resolve({ member: [{ '@id': '/api/sites/1', name: 'Chatellerault', postalCode: '86100' }] })
+ return Promise.resolve({ member: [{ '@id': '/api/sites/1', name: 'Chatellerault', postalCode: '86100', color: '#FF0000' }] })
}
return Promise.resolve({ member: [] })
})
@@ -74,8 +74,9 @@ describe('useClientReferentials.loadCommon (resilience ERP-102)', () => {
expect(refs.categories.value).toEqual([
{ value: '/api/categories/1', label: 'Secteur', code: 'SECTEUR' },
])
- // Le libelle d'un site est son numero de departement (2 premiers chiffres du code postal).
- expect(refs.sites.value).toEqual([{ value: '/api/sites/1', label: '86' }])
+ // Le libelle d'un site est son numero de departement (2 premiers chiffres du
+ // code postal) ; la couleur du site est reportee (fond) avec un texte blanc.
+ expect(refs.sites.value).toEqual([{ value: '/api/sites/1', label: '86', color: '#FF0000', textColor: '#FFFFFF' }])
})
it('separe les categories CLIENT (formulaire) des categories ADRESSE (blocs adresse)', async () => {
diff --git a/frontend/modules/commercial/composables/useClientReferentials.ts b/frontend/modules/commercial/composables/useClientReferentials.ts
index 6799f79..8553eb1 100644
--- a/frontend/modules/commercial/composables/useClientReferentials.ts
+++ b/frontend/modules/commercial/composables/useClientReferentials.ts
@@ -1,4 +1,5 @@
import { ref } from 'vue'
+import type { CategoryOption, ClientOption, PaymentTypeOption, RefOption } from '~/modules/commercial/types/referentials'
/**
* Charge les referentiels (listes courtes) alimentant les selects de l'ecran
@@ -15,25 +16,6 @@ import { ref } from 'vue'
* Etat 100 % local a l'instance (refs) — aucune persistance URL.
*/
-/** Option generique au format attendu par MalioSelect / MalioSelectCheckbox ({ label, value }). */
-export interface RefOption {
- value: string
- label: string
-}
-
-/** Option de type de reglement enrichie de son code stable (RG-1.12 / RG-1.13). */
-export interface PaymentTypeOption extends RefOption {
- code: string
-}
-
-/** Option de categorie enrichie de son code stable (filtrage RG-1.29 cote adresse). */
-export interface CategoryOption extends RefOption {
- code: string
-}
-
-/** Option de client (distributeur / courtier) — value = IRI du client lie. */
-export type ClientOption = RefOption
-
interface HydraMember {
'@id': string
}
@@ -46,6 +28,7 @@ interface CategoryMember extends HydraMember {
interface SiteMember extends HydraMember {
name: string
postalCode: string
+ color?: string
}
interface ReferentialMember extends HydraMember {
@@ -119,7 +102,7 @@ export function useClientReferentials() {
// Libelle = numero de departement (2 premiers chiffres du code
// postal du site), ex: 86100 -> « 86 ». Le code postal est deja
// expose par /sites (groupe site:read) — aucune colonne a ajouter.
- .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2) })) }),
+ .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2), color: s.color, textColor: '#FFFFFF' })) }),
fetchAll('/tva_modes')
.then((tva) => { tvaModes.value = tva.map(t => ({ value: t['@id'], label: t.label })) }),
fetchAll('/payment_delays')
diff --git a/frontend/modules/commercial/composables/useSupplierReferentials.ts b/frontend/modules/commercial/composables/useSupplierReferentials.ts
index 01ef814..1445fef 100644
--- a/frontend/modules/commercial/composables/useSupplierReferentials.ts
+++ b/frontend/modules/commercial/composables/useSupplierReferentials.ts
@@ -1,4 +1,5 @@
import { ref } from 'vue'
+import type { CategoryOption, PaymentTypeOption, RefOption } from '~/modules/commercial/types/referentials'
/**
* Charge les referentiels (listes courtes) alimentant les selects de l'ecran
@@ -16,22 +17,6 @@ import { ref } from 'vue'
* Etat 100 % local a l'instance (refs) — aucune persistance URL.
*/
-/** Option generique au format attendu par MalioSelect / MalioSelectCheckbox. */
-export interface RefOption {
- value: string
- label: string
-}
-
-/** Option de type de reglement enrichie de son code stable (RG-2.07 / RG-2.08). */
-export interface PaymentTypeOption extends RefOption {
- code: string
-}
-
-/** Option de categorie enrichie de son code stable. */
-export interface CategoryOption extends RefOption {
- code: string
-}
-
interface HydraMember {
'@id': string
}
@@ -44,6 +29,7 @@ interface CategoryMember extends HydraMember {
interface SiteMember extends HydraMember {
name: string
postalCode: string
+ color?: string
}
interface ReferentialMember extends HydraMember {
@@ -106,7 +92,7 @@ export function useSupplierReferentials() {
fetchAll('/sites')
// Libelle = numero de departement (2 premiers chiffres du code
// postal du site), ex: 86100 -> « 86 ».
- .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2) })) }),
+ .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2), color: s.color, textColor: '#FFFFFF' })) }),
fetchAll('/tva_modes')
.then((tva) => { tvaModes.value = tva.map(t => ({ value: t['@id'], label: t.label })) }),
fetchAll('/payment_delays')
diff --git a/frontend/modules/commercial/pages/clients/[id]/edit.vue b/frontend/modules/commercial/pages/clients/[id]/edit.vue
index 5a67243..1ee867f 100644
--- a/frontend/modules/commercial/pages/clients/[id]/edit.vue
+++ b/frontend/modules/commercial/pages/clients/[id]/edit.vue
@@ -35,6 +35,7 @@
-
+
{{ t('commercial.clients.form.confirmDelete.title') }}
@@ -420,7 +421,8 @@
diff --git a/frontend/modules/logistique/pages/__tests__/weighingTicketEdit.spec.ts b/frontend/modules/logistique/pages/__tests__/weighingTicketEdit.spec.ts
index c688d03..0b57357 100644
--- a/frontend/modules/logistique/pages/__tests__/weighingTicketEdit.spec.ts
+++ b/frontend/modules/logistique/pages/__tests__/weighingTicketEdit.spec.ts
@@ -123,6 +123,14 @@ describe('Écran Modification ticket de pesée (page /weighing-tickets/{id}/edit
expect(wrapper.find('[data-label="logistique.weighingTickets.form.validate"]').exists()).toBe(false)
})
+ it('ticket en attente (DRAFT) : PAS de bouton « Imprimer », action principale « Valider »', async () => {
+ // Un brouillon n'a pas de numéro : le bon de pesée ne doit pas être imprimable.
+ mockFetchTicket.mockReset().mockResolvedValue({ ...DETAIL, status: 'DRAFT', number: null })
+ const wrapper = await mountPage()
+ expect(wrapper.find('[data-label="logistique.weighingTickets.form.print"]').exists()).toBe(false)
+ expect(wrapper.find('[data-label="logistique.weighingTickets.form.validate"]').exists()).toBe(true)
+ })
+
it('« Imprimer » ouvre le bon de pesée PDF servi par le back (RG-5.08)', async () => {
const wrapper = await mountPage()
await wrapper.find('[data-label="logistique.weighingTickets.form.print"]').trigger('click')
diff --git a/frontend/modules/logistique/pages/weighing-tickets/[id]/edit.vue b/frontend/modules/logistique/pages/weighing-tickets/[id]/edit.vue
index 93b7346..3f75eaa 100644
--- a/frontend/modules/logistique/pages/weighing-tickets/[id]/edit.vue
+++ b/frontend/modules/logistique/pages/weighing-tickets/[id]/edit.vue
@@ -58,6 +58,7 @@
+
-
+
{{ t('logistique.weighingTickets.form.weighbridge.confirmTitle') }}
+ {{ t('logistique.weighingTickets.form.weighbridge.confirmMessage') }}
{{ autoModal.error }}
-
+
{{ t('logistique.weighingTickets.form.weighbridge.confirmTitle') }}
+ {{ t('logistique.weighingTickets.form.weighbridge.confirmMessage') }}
{{ autoModal.error }}
diff --git a/frontend/modules/sites/composables/useCurrentSite.ts b/frontend/modules/sites/composables/useCurrentSite.ts
index da196d2..a2035fb 100644
--- a/frontend/modules/sites/composables/useCurrentSite.ts
+++ b/frontend/modules/sites/composables/useCurrentSite.ts
@@ -6,8 +6,8 @@
* rollback si la requete PATCH `/api/me/current-site` echoue.
*
* Garantie d'unicite : le flag `switching` bloque les double-clicks
- * concurrents. Le reset explicite est appele au logout
- * (voir `modules/core/pages/logout.vue`).
+ * concurrents. Le state est purge au logout via `onAuthSessionCleared`
+ * (declenche par `clearSession()`, cf. `useLogout` et l'intercepteur 401).
*
* Auto-select : aucun. Le backend (`UserRbacProcessor::ensureCurrentSiteConsistency`)
* garantit deja l'invariant "user avec sites non vide => currentSite non null"
@@ -30,8 +30,8 @@ const availableSites = ref([])
const switching = ref(false)
// Enregistrement unique au niveau module (singleton) : quand clearSession()
-// est appelee par l'intercepteur 401 de useApi, le state local est purgé
-// de la meme facon qu'au logout explicite (logout.vue).
+// est appelee (logout volontaire via useLogout, ou intercepteur 401 de useApi),
+// le state local est purgé.
onAuthSessionCleared(() => {
currentSite.value = null
availableSites.value = []
diff --git a/frontend/modules/technique/components/ProviderAddressBlock.vue b/frontend/modules/technique/components/ProviderAddressBlock.vue
index 24faeef..ac773f0 100644
--- a/frontend/modules/technique/components/ProviderAddressBlock.vue
+++ b/frontend/modules/technique/components/ProviderAddressBlock.vue
@@ -37,6 +37,7 @@
v-if="!hideEmpty || isFilled(model.contactIris)"
:model-value="model.contactIris"
:options="contactOptions"
+ :max-tags="3"
:label="t('technique.providers.form.address.contacts')"
:display-tag="true"
:readonly="readonly"
diff --git a/frontend/modules/technique/composables/useProviderReferentials.ts b/frontend/modules/technique/composables/useProviderReferentials.ts
index 3b56d8c..37ae76d 100644
--- a/frontend/modules/technique/composables/useProviderReferentials.ts
+++ b/frontend/modules/technique/composables/useProviderReferentials.ts
@@ -26,6 +26,13 @@ import { ref } from 'vue'
export interface RefOption {
value: string
label: string
+ // Couleur de fond optionnelle de l'option (hex #RRGGBB). Alimentee par le
+ // referentiel sites (couleur d'identification du site, affichee sur les tags
+ // selectionnes du multiselect).
+ color?: string
+ // Couleur de texte optionnelle (hex). Sites : blanc, pour rester lisible
+ // sur le fond colore du tag.
+ textColor?: string
}
/** Option de type de reglement enrichie de son code stable (RG-3.07 / RG-3.08). */
@@ -50,6 +57,7 @@ interface CategoryMember extends HydraMember {
interface SiteMember extends HydraMember {
name: string
postalCode: string
+ color?: string
}
interface CountryMember extends HydraMember {
@@ -94,7 +102,7 @@ export function useProviderReferentials() {
// Sites (RG-3.03) : libelle = numero de departement (2 premiers chiffres
// du code postal du site), ex: 86100 -> « 86 », 17400 -> « 17 ».
fetchAll('/sites')
- .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2) })) }),
+ .then((sitesList) => { sites.value = sitesList.map(s => ({ value: s['@id'], label: (s.postalCode ?? '').slice(0, 2), color: s.color, textColor: '#FFFFFF' })) }),
// Pays (ERP-116) : la valeur d'option est le NOM du pays (l'adresse stocke
// `country` en chaine libre, « France »...). value === label. Aligne sur
// les ecrans client/fournisseur. Sert le select Pays de l'onglet Adresse.
diff --git a/frontend/modules/technique/pages/providers/[id]/edit.vue b/frontend/modules/technique/pages/providers/[id]/edit.vue
index a7cabdd..42cd0c3 100644
--- a/frontend/modules/technique/pages/providers/[id]/edit.vue
+++ b/frontend/modules/technique/pages/providers/[id]/edit.vue
@@ -31,6 +31,7 @@
-
+
{{ t('technique.providers.form.confirmDelete.title') }}
diff --git a/frontend/modules/technique/pages/providers/[id]/index.vue b/frontend/modules/technique/pages/providers/[id]/index.vue
index 37d3692..66a373c 100644
--- a/frontend/modules/technique/pages/providers/[id]/index.vue
+++ b/frontend/modules/technique/pages/providers/[id]/index.vue
@@ -57,6 +57,7 @@
v-if="isFilled(mainCategoryIris)"
:model-value="mainCategoryIris"
:options="mainCategoryOptions"
+ :max-tags="3"
:label="t('technique.providers.form.main.categories')"
:display-tag="true"
disabled
@@ -147,7 +148,7 @@
-
+
{{ confirmArchive.title }}
diff --git a/frontend/modules/technique/pages/providers/index.vue b/frontend/modules/technique/pages/providers/index.vue
index f6110cd..946aade 100644
--- a/frontend/modules/technique/pages/providers/index.vue
+++ b/frontend/modules/technique/pages/providers/index.vue
@@ -63,10 +63,9 @@
-
-
- {{ formatLastActivity(item) }}
-
+
+
@@ -200,7 +199,6 @@ const rows = computed(() => providers.value.map(provider => ({
companyName: provider.companyName,
categories: provider.categories,
sites: provider.sites,
- updatedAt: provider.updatedAt,
})))
const columns = [
@@ -216,29 +214,6 @@ function formatCategories(item: Record): string {
return categories.map(c => c.name).join(', ')
}
-/**
- * Derniere activite : date de derniere modification de la fiche (updatedAt,
- * expose en liste via default:read). Format court francais JJ-MM-AAAA (tirets,
- * cf. spec-front M3 § Datatable).
- */
-function formatLastActivity(item: Record): string {
- const value = item.updatedAt as string | null | undefined
- if (!value) {
- return ''
- }
-
- // Garde-fou date invalide : un updatedAt mal forme donnerait « Invalid Date ».
- const date = new Date(value)
- if (Number.isNaN(date.getTime())) {
- return ''
- }
-
- const day = String(date.getDate()).padStart(2, '0')
- const month = String(date.getMonth() + 1).padStart(2, '0')
- const year = date.getFullYear()
- return `${day}-${month}-${year}`
-}
-
/** Clic sur une ligne → ecran Consultation (route a plat /providers/{id}). */
function onRowClick(item: Record): void {
router.push(`/providers/${item.id}`)
diff --git a/frontend/modules/technique/pages/providers/new.vue b/frontend/modules/technique/pages/providers/new.vue
index d3abdca..e425bd2 100644
--- a/frontend/modules/technique/pages/providers/new.vue
+++ b/frontend/modules/technique/pages/providers/new.vue
@@ -30,6 +30,7 @@
-
+
{{ t('technique.providers.form.confirmDelete.title') }}
diff --git a/frontend/modules/technique/utils/forms/__tests__/providerDetail.spec.ts b/frontend/modules/technique/utils/forms/__tests__/providerDetail.spec.ts
index 1b953e9..d2acb4a 100644
--- a/frontend/modules/technique/utils/forms/__tests__/providerDetail.spec.ts
+++ b/frontend/modules/technique/utils/forms/__tests__/providerDetail.spec.ts
@@ -122,8 +122,8 @@ describe('providerDetail helpers', () => {
it('categoryOptionsOf / siteOptionsOf / contactOptionsOf', () => {
expect(categoryOptionsOf([{ '@id': '/api/categories/7', name: 'Maintenance', code: 'MAINT' }]))
.toEqual([{ value: '/api/categories/7', label: 'Maintenance' }])
- expect(siteOptionsOf([{ '@id': '/api/sites/1', name: 'Châtellerault' }]))
- .toEqual([{ value: '/api/sites/1', label: 'Châtellerault' }])
+ expect(siteOptionsOf([{ '@id': '/api/sites/1', name: 'Châtellerault', color: '#000' }]))
+ .toEqual([{ value: '/api/sites/1', label: 'Châtellerault', color: '#000', textColor: '#FFFFFF' }])
expect(contactOptionsOf([{ '@id': '/api/provider_contacts/5', id: 5, firstName: 'Jean', lastName: 'Dupont' }]))
.toEqual([{ value: '/api/provider_contacts/5', label: 'Jean Dupont' }])
})
diff --git a/frontend/modules/technique/utils/forms/providerDetail.ts b/frontend/modules/technique/utils/forms/providerDetail.ts
index 8904a8f..44d5d14 100644
--- a/frontend/modules/technique/utils/forms/providerDetail.ts
+++ b/frontend/modules/technique/utils/forms/providerDetail.ts
@@ -187,7 +187,7 @@ export function categoryOptionsOf(categories: CategoryRead[] | undefined): RefOp
/** Options de sites (value=IRI, label=nom) construites depuis un embed. */
export function siteOptionsOf(sites: SiteRead[] | undefined): RefOption[] {
- return (sites ?? []).map(s => ({ value: s['@id'], label: s.name ?? s['@id'] }))
+ return (sites ?? []).map(s => ({ value: s['@id'], label: s.name ?? s['@id'], color: s.color, textColor: '#FFFFFF' }))
}
/** Options de contacts (value=IRI, label=nom complet ou email) depuis l'embed prestataire. */
diff --git a/frontend/modules/transport/components/CarrierQualimatTab.vue b/frontend/modules/transport/components/CarrierQualimatTab.vue
index 7cebfff..cfc148a 100644
--- a/frontend/modules/transport/components/CarrierQualimatTab.vue
+++ b/frontend/modules/transport/components/CarrierQualimatTab.vue
@@ -156,7 +156,7 @@ function confirmIntegrate(): void {
-
+
{{ t('transport.carriers.form.qualimat.confirm.title') }}
diff --git a/frontend/modules/transport/pages/carriers/[id]/edit.vue b/frontend/modules/transport/pages/carriers/[id]/edit.vue
index d3da6ab..795940a 100644
--- a/frontend/modules/transport/pages/carriers/[id]/edit.vue
+++ b/frontend/modules/transport/pages/carriers/[id]/edit.vue
@@ -202,7 +202,7 @@
-
+
{{ t('transport.carriers.form.confirmDelete.title') }}
@@ -216,7 +216,7 @@