codevdesign 1.0.65 → 1.0.67

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,367 +1,372 @@
1
- <template>
2
- <div class="pa-2 mt-2 mb-1 mr-1">
3
- <!-- Affiche la carte récap si activée et qu'il y a des unités -->
4
- <div v-if="activerDivPreferences && unites && unites.length > 0">
5
- <v-card
6
- width="375"
7
- variant="outlined"
8
- >
9
- <v-card-text class="pt-0 mt-0">
10
- <v-progress-linear
11
- v-if="chargementEnCours"
12
- indeterminate
13
- />
14
-
15
- <div
16
- v-else
17
- class="pt-2"
18
- >
19
- <div class="text-overline text-h6 mb-2">{{ texteTitre }}</div>
20
-
21
- <div>
22
- <v-list-item
23
- v-for="uniteId in Object.keys(dictChaisesReleve)"
24
- :key="uniteId"
25
- class="mb-1"
26
- >
27
- <ChaisePreferenceItem
28
- :uniteId="Number(uniteId)"
29
- :preferences="preferences"
30
- :dictChaisesReleve="dictChaisesReleve"
31
- :unites="unites"
32
- />
33
- </v-list-item>
34
- </div>
35
- </div>
36
- </v-card-text>
37
-
38
- <v-card-actions>
39
- <div class="flex-grow-1" />
40
- <v-btn
41
- v-if="!chargementEnCours"
42
- rounded
43
- variant="outlined"
44
- size="small"
45
- @click.stop="modifier"
46
- >
47
- {{ texteBoutonModifier }}
48
- </v-btn>
49
- </v-card-actions>
50
- </v-card>
51
- </div>
52
-
53
- <csqcDialogue
54
- ref="modale"
55
- :operation-en-cours="chargementEnCours"
56
- :btn-ok-desactiver="chargementEnCours"
57
- :titre="texteTitre"
58
- :btn-annuler-texte="texteBoutonAnnuler"
59
- @annuler="annuler"
60
- @ok="ok"
61
- ><template #content>
62
- <div class="pt-2"></div>
63
- <hr class="pt-0 mt-0 pl-0" />
64
- <div class="pt-4">
65
- <i>{{ info }}</i>
66
-
67
- <v-progress-linear
68
- v-if="chargementEnCours"
69
- indeterminate
70
- />
71
-
72
- <div
73
- v-else
74
- class="pt-4"
75
- >
76
- <v-row v-if="!toutesUnitesPreferencesSelectionnees">
77
- <v-col cols="12">
78
- <v-alert
79
- v-model="afficherErreur"
80
- type="error"
81
- variant="tonal"
82
- dismissible
83
- >
84
- {{ texteMessageErreur }}
85
- </v-alert>
86
- </v-col>
87
- </v-row>
88
-
89
- <v-row>
90
- <v-col
91
- v-for="uniteId in Object.keys(dictChaisesReleve)"
92
- :key="uniteId"
93
- cols="12"
94
- sm="6"
95
- md="4"
96
- >
97
- <label>{{ getUnite(uniteId)?.nom ?? '' }}</label>
98
- <br />
99
- <v-radio-group v-model="selection[uniteId]">
100
- <v-radio
101
- v-for="chaise in dictChaisesReleve[uniteId]"
102
- :key="chaise.id"
103
- :label="chaise.nom"
104
- :value="chaise.id"
105
- />
106
- </v-radio-group>
107
- </v-col>
108
- </v-row>
109
- </div>
110
- </div>
111
- </template>
112
- </csqcDialogue>
113
- </div>
114
- </template>
115
-
116
- <script setup lang="ts">
117
- import { ref, computed, onMounted, nextTick, watch, toRefs } from 'vue'
118
- import { useDisplay } from 'vuetify'
119
- import ChaisePreferenceItem from './chaiseItem.vue'
120
- import csqcDialogue from '../csqcDialogue.vue'
121
- import axios from '../../outils/appAxios'
122
- import type { Unite } from '@/codev/modeles/unite'
123
- import { useI18n } from 'vue-i18n'
124
- const modale = ref<InstanceType<typeof csqcDialogue> | null>(null)
125
-
126
- interface Chaise {
127
- id: number
128
- nom: string
129
- }
130
- interface Preference {
131
- uniteId: number
132
- chaiseId: number
133
- }
134
-
135
- /** Props */
136
- const props = defineProps<{
137
- activerDivPreferences?: boolean
138
- typeIntervenant: number
139
- demandeId: number
140
- formulaireId: number
141
- urlBase: string
142
- texteTitre?: string
143
- texteInfo?: string
144
- largeurModale?: number
145
- }>()
146
- const { typeIntervenant, demandeId, formulaireId, urlBase } = toRefs(props)
147
- /** Emits */
148
- const emit = defineEmits<{
149
- (e: 'annuler'): void
150
- (e: 'confirmer'): void
151
- }>()
152
-
153
- const { t } = useI18n({ useScope: 'global' })
154
- const { xs } = useDisplay()
155
- const isXs = computed(() => xs.value)
156
-
157
- /** State */
158
- const visible = ref(false)
159
- const chargementEnCours = ref(false)
160
- const afficherErreur = ref(false)
161
- const unites = ref<Unite[]>([])
162
- const preferences = ref<Preference[]>([])
163
- const dictChaisesReleve = ref<Record<string, Chaise[]>>({})
164
- const selection = ref<Record<string, number>>({})
165
- const modeModifier = ref(false)
166
-
167
- /** Textes */
168
- const texteMessageErreur = computed(() => t('csqc.csqcChaise.erreur'))
169
- const texteBoutonAnnuler = computed(() => t('csqc.bouton.annuler'))
170
- const texteBoutonModifier = computed(() => t('csqc.bouton.modifier'))
171
- const texteTitre = computed(() =>
172
- props.texteTitre && props.texteTitre.length > 0 ? props.texteTitre : t('csqc.csqcChaise.titre'),
173
- )
174
- const info = computed(() =>
175
- props.texteInfo && props.texteInfo.length > 0 ? props.texteInfo : t('csqc.csqcChaise.info'),
176
- )
177
-
178
- // Option A — ne recharger que si typeIntervenant change
179
-
180
- const largeur = computed(() => props.largeurModale ?? 1200)
181
- const activerDivPreferences = computed(() => props.activerDivPreferences ?? true)
182
-
183
- function getUnite(uniteId: string) {
184
- return unites.value.find((u: Unite) => String(u.id) === String(uniteId))
185
- }
186
-
187
- /*
188
- function estPrefere(uniteId: string | number, chaiseId: number) {
189
- return preferences.value.some(p => String(p.uniteId) === String(uniteId) && p.chaiseId === chaiseId)
190
- }*/
191
-
192
- /** Sélection par défaut à partir des préférences existantes */
193
- function definitionSelectionDepart() {
194
- for (const uniteId in dictChaisesReleve.value) {
195
- const chaises = dictChaisesReleve.value[uniteId] ?? []
196
- const pref = preferences.value.find(
197
- p => String(p.uniteId) === String(uniteId) && chaises.some(chaise => chaise.id === p.chaiseId),
198
- )
199
- const chaiseIdDefaut = pref?.chaiseId ?? (chaises.length === 1 ? (chaises[0]?.id ?? -1) : -1)
200
- selection.value[uniteId] = chaiseIdDefaut
201
- }
202
- }
203
-
204
- /** Toutes les unités ont-elles une préférence sélectionnée ? */
205
- const toutesUnitesPreferencesSelectionnees = computed(() => {
206
- for (const uniteId in dictChaisesReleve.value) {
207
- const chaises = dictChaisesReleve.value[uniteId] ?? []
208
- const uniteOk = chaises.some(c => preferences.value.some(p => p.chaiseId === c.id))
209
- if (!uniteOk) return false
210
- }
211
- return true
212
- })
213
-
214
- async function charger() {
215
- await chargerUnites()
216
- await chargementPreferences()
217
- }
218
-
219
- async function chargerUnites() {
220
- const url = `${props.urlBase}/api/ComposantUI/Unites`
221
- const unitesData = (await axios.getAxios().get<Unite[]>(url)) as unknown as Unite[]
222
- unites.value = unitesData ?? []
223
- }
224
-
225
- async function chargementPreferences() {
226
- chargementEnCours.value = true
227
-
228
- // Chaises par unité
229
- {
230
- const data = (await axios
231
- .getAxios()
232
- .get<
233
- Record<string, Chaise[]>
234
- >(`${props.urlBase}/api/ComposantUI/ReleveDe/${props.typeIntervenant}/Demande/${props.demandeId}`)) as unknown as Record<
235
- string,
236
- Chaise[]
237
- >
238
- dictChaisesReleve.value = data ?? {}
239
- }
240
-
241
- // Préférences de l'usager
242
- {
243
- const data = (await axios
244
- .getAxios()
245
- .get<
246
- Preference[]
247
- >(`${props.urlBase}/api/ComposantUI/Preferences/${props.formulaireId}/TypeIntervenant/${props.typeIntervenant}`)) as unknown as Preference[]
248
- preferences.value = (data ?? []).filter(Boolean)
249
- }
250
-
251
- definitionSelectionDepart()
252
-
253
- // Sauvegarde immédiate pour les choix uniques (si demandé par ton flux)
254
- await sauvegarder()
255
-
256
- chargementEnCours.value = false
257
- }
258
-
259
- async function ouvrir() {
260
- if (!activerDivPreferences.value) {
261
- await chargementPreferences()
262
- visible.value = true
263
- return
264
- }
265
-
266
- if (!toutesUnitesPreferencesSelectionnees.value) {
267
- visible.value = true
268
- return
269
- }
270
-
271
- await ok()
272
- }
273
-
274
- function annuler() {
275
- visible.value = false
276
- modeModifier.value = false
277
- emit('annuler')
278
- }
279
-
280
- function modifier() {
281
- modeModifier.value = true
282
- visible.value = true
283
- modale.value?.ouvrir()
284
- }
285
-
286
- async function sauvegarder() {
287
- for (const uniteId in dictChaisesReleve.value) {
288
- const chaiseId = selection.value[uniteId]
289
- if (!chaiseId || chaiseId <= 0) continue
290
-
291
- const chaises = dictChaisesReleve.value[uniteId] ?? []
292
- if (chaises.length === 0) continue
293
-
294
- // Préférence existante pour CETTE unité ?
295
- const prefIndexDeUnite = preferences.value.findIndex(p => chaises.some(c => c.id === p.chaiseId))
296
-
297
- try {
298
- const data = (await axios
299
- .getAxios()
300
- .put<Preference>(
301
- `${props.urlBase}/api/ComposantUI/Preferences/${props.formulaireId}` +
302
- `/Unite/${encodeURIComponent(uniteId)}` +
303
- `/Chaise/${chaiseId}` +
304
- `/TypeIntervenant/${props.typeIntervenant}`,
305
- )) as unknown as Preference
306
- const itemRecu = data as Preference
307
-
308
- if (prefIndexDeUnite >= 0) {
309
- // remplace l'élément à l'index
310
- preferences.value.splice(prefIndexDeUnite, 1, itemRecu)
311
- } else {
312
- // ajoute un nouvel élément
313
- preferences.value.push(itemRecu)
314
- }
315
- } catch (e) {
316
- console.error(e)
317
- }
318
- }
319
- }
320
- async function soumettre() {
321
- modeModifier.value = false
322
- await ouvrir()
323
- }
324
- /** Validation finale */
325
- async function ok() {
326
- await sauvegarder()
327
-
328
- if (toutesUnitesPreferencesSelectionnees.value) {
329
- if (!modeModifier.value) emit('confirmer')
330
- visible.value = false
331
- modeModifier.value = false
332
- } else {
333
- afficherErreur.value = true
334
- await nextTick()
335
- }
336
- modale.value?.fermer()
337
- }
338
-
339
- onMounted(async () => {
340
- chargementEnCours.value = true
341
- await charger()
342
- chargementEnCours.value = false
343
- })
344
-
345
- // si le type d'intervenant change
346
- watch(typeIntervenant, async (nv, ov) => {
347
- if (nv === ov) return
348
- await rechargerPourTypeIntervenant()
349
- })
350
-
351
- async function rechargerPourTypeIntervenant() {
352
- try {
353
- chargementEnCours.value = true
354
- preferences.value = []
355
- dictChaisesReleve.value = {}
356
- selection.value = {}
357
- afficherErreur.value = false
358
-
359
- // recharge les données dépendantes
360
- await chargementPreferences()
361
- } finally {
362
- chargementEnCours.value = false
363
- }
364
- }
365
-
366
- defineExpose({ charger, soumettre })
367
- </script>
1
+ <template>
2
+ <div class="pa-2 mt-2 mb-1 mr-1">
3
+ <!-- Affiche la carte récap si activée et qu'il y a des unités -->
4
+ <div v-if="activerDivPreferences && unites && unites.length > 0">
5
+ <v-card
6
+ max-width="375"
7
+ width="100%"
8
+ variant="outlined"
9
+ >
10
+ <v-card-text class="pt-0 mt-0">
11
+ <v-progress-linear
12
+ v-if="chargementEnCours"
13
+ indeterminate
14
+ />
15
+
16
+ <div
17
+ v-else
18
+ class="pt-2"
19
+ >
20
+ <div class="text-overline text-h6 mb-2">{{ texteTitre }}</div>
21
+
22
+ <div>
23
+ <v-list-item
24
+ v-for="uniteId in Object.keys(dictChaisesReleve)"
25
+ :key="uniteId"
26
+ class="mb-1"
27
+ >
28
+ <ChaisePreferenceItem
29
+ :uniteId="Number(uniteId)"
30
+ :preferences="preferences"
31
+ :dictChaisesReleve="dictChaisesReleve"
32
+ :unites="unites"
33
+ />
34
+ </v-list-item>
35
+ </div>
36
+ </div>
37
+ </v-card-text>
38
+
39
+ <v-card-actions>
40
+ <div class="flex-grow-1" />
41
+ <v-btn
42
+ v-if="!chargementEnCours"
43
+ rounded
44
+ variant="outlined"
45
+ size="small"
46
+ @click.stop="modifier"
47
+ >
48
+ {{ texteBoutonModifier }}
49
+ </v-btn>
50
+ </v-card-actions>
51
+ </v-card>
52
+ </div>
53
+
54
+ <csqcDialogue
55
+ ref="modale"
56
+ :operation-en-cours="chargementEnCours"
57
+ :btn-ok-desactiver="chargementEnCours"
58
+ :titre="texteTitre"
59
+ :btn-annuler-texte="texteBoutonAnnuler"
60
+ @annuler="annuler"
61
+ @ok="ok"
62
+ ><template #content>
63
+ <div class="pt-2"></div>
64
+ <hr class="pt-0 mt-0 pl-0" />
65
+ <div class="pt-4">
66
+ <i>{{ info }}</i>
67
+
68
+ <v-progress-linear
69
+ v-if="chargementEnCours"
70
+ indeterminate
71
+ />
72
+
73
+ <div
74
+ v-else
75
+ class="pt-4"
76
+ >
77
+ <v-row v-if="!toutesUnitesPreferencesSelectionnees">
78
+ <v-col cols="12">
79
+ <v-alert
80
+ v-model="afficherErreur"
81
+ type="error"
82
+ variant="tonal"
83
+ dismissible
84
+ >
85
+ {{ texteMessageErreur }}
86
+ </v-alert>
87
+ </v-col>
88
+ </v-row>
89
+
90
+ <v-row>
91
+ <v-col
92
+ v-for="uniteId in Object.keys(dictChaisesReleve)"
93
+ :key="uniteId"
94
+ cols="12"
95
+ sm="6"
96
+ md="4"
97
+ >
98
+ <label>{{ getUnite(uniteId)?.nom ?? '' }}</label>
99
+ <br />
100
+ <v-radio-group v-model="selection[uniteId]">
101
+ <v-radio
102
+ v-for="chaise in dictChaisesReleve[uniteId]"
103
+ :key="chaise.id"
104
+ :label="chaise.nom"
105
+ :value="chaise.id"
106
+ />
107
+ </v-radio-group>
108
+ </v-col>
109
+ </v-row>
110
+ </div>
111
+ </div>
112
+ </template>
113
+ </csqcDialogue>
114
+ </div>
115
+ </template>
116
+
117
+ <script setup lang="ts">
118
+ import { ref, computed, onMounted, nextTick, watch, toRefs } from 'vue'
119
+ import { useDisplay } from 'vuetify'
120
+ import ChaisePreferenceItem from './chaiseItem.vue'
121
+ import csqcDialogue from '../csqcDialogue.vue'
122
+ import axios from '../../outils/appAxios'
123
+ import type { Unite } from '@/codev/modeles/unite'
124
+ import { useI18n } from 'vue-i18n'
125
+ const modale = ref<InstanceType<typeof csqcDialogue> | null>(null)
126
+
127
+ interface Chaise {
128
+ id: number
129
+ nom: string
130
+ }
131
+ interface Preference {
132
+ uniteId: number
133
+ chaiseId: number
134
+ }
135
+
136
+ /** Props */
137
+ const props = defineProps<{
138
+ activerDivPreferences?: boolean
139
+ typeIntervenant: number
140
+ demandeId: number
141
+ formulaireId: number
142
+ urlBase: string
143
+ texteTitre?: string
144
+ texteInfo?: string
145
+ largeurModale?: number
146
+ }>()
147
+ const { typeIntervenant, demandeId, formulaireId, urlBase } = toRefs(props)
148
+ /** Emits */
149
+ const emit = defineEmits<{
150
+ (e: 'annuler'): void
151
+ (e: 'confirmer'): void
152
+ }>()
153
+
154
+ const { t } = useI18n({ useScope: 'global' })
155
+ const { xs } = useDisplay()
156
+ const isXs = computed(() => xs.value)
157
+
158
+ /** State */
159
+ const visible = ref(false)
160
+ const chargementEnCours = ref(false)
161
+ const afficherErreur = ref(false)
162
+ const unites = ref<Unite[]>([])
163
+ const preferences = ref<Preference[]>([])
164
+ const dictChaisesReleve = ref<Record<string, Chaise[]>>({})
165
+ const selection = ref<Record<string, number>>({})
166
+ const modeModifier = ref(false)
167
+
168
+ /** Textes */
169
+ const texteMessageErreur = computed(() => t('csqc.csqcChaise.erreur'))
170
+ const texteBoutonAnnuler = computed(() => t('csqc.bouton.annuler'))
171
+ const texteBoutonModifier = computed(() => t('csqc.bouton.modifier'))
172
+ const texteTitre = computed(() =>
173
+ props.texteTitre && props.texteTitre.length > 0 ? props.texteTitre : t('csqc.csqcChaise.titre'),
174
+ )
175
+ const info = computed(() =>
176
+ props.texteInfo && props.texteInfo.length > 0 ? props.texteInfo : t('csqc.csqcChaise.info'),
177
+ )
178
+
179
+ // Option A — ne recharger que si typeIntervenant change
180
+
181
+ const largeur = computed(() => props.largeurModale ?? 1200)
182
+ const activerDivPreferences = computed(() => props.activerDivPreferences ?? true)
183
+
184
+ function getUnite(uniteId: string) {
185
+ return unites.value.find((u: Unite) => String(u.id) === String(uniteId))
186
+ }
187
+
188
+ /*
189
+ function estPrefere(uniteId: string | number, chaiseId: number) {
190
+ return preferences.value.some(p => String(p.uniteId) === String(uniteId) && p.chaiseId === chaiseId)
191
+ }*/
192
+
193
+ /** Sélection par défaut à partir des préférences existantes */
194
+ function definitionSelectionDepart() {
195
+ for (const uniteId in dictChaisesReleve.value) {
196
+ const chaises = dictChaisesReleve.value[uniteId] ?? []
197
+ const pref = preferences.value.find(
198
+ p => String(p.uniteId) === String(uniteId) && chaises.some(chaise => chaise.id === p.chaiseId),
199
+ )
200
+ const chaiseIdDefaut = pref?.chaiseId ?? (chaises.length === 1 ? (chaises[0]?.id ?? -1) : -1)
201
+ selection.value[uniteId] = chaiseIdDefaut
202
+ }
203
+ }
204
+
205
+ /** Toutes les unités ont-elles une préférence sélectionnée ? */
206
+ const toutesUnitesPreferencesSelectionnees = computed(() => {
207
+ for (const uniteId in dictChaisesReleve.value) {
208
+ const chaises = dictChaisesReleve.value[uniteId] ?? []
209
+ const uniteOk = chaises.some(c => preferences.value.some(p => p.chaiseId === c.id))
210
+ if (!uniteOk) return false
211
+ }
212
+ return true
213
+ })
214
+
215
+ async function charger() {
216
+ await chargerUnites()
217
+ await chargementPreferences()
218
+ }
219
+
220
+ async function chargerUnites() {
221
+ const url = `${props.urlBase}/api/ComposantUI/Unites`
222
+ const unitesData = (await axios.getAxios().get<Unite[]>(url)) as unknown as Unite[]
223
+ unites.value = unitesData ?? []
224
+ }
225
+
226
+ async function chargementPreferences() {
227
+ chargementEnCours.value = true
228
+
229
+ // Chaises par unité
230
+ {
231
+ const data = (await axios
232
+ .getAxios()
233
+ .get<
234
+ Record<string, Chaise[]>
235
+ >(`${props.urlBase}/api/ComposantUI/ReleveDe/${props.typeIntervenant}/Demande/${props.demandeId}`)) as unknown as Record<
236
+ string,
237
+ Chaise[]
238
+ >
239
+ dictChaisesReleve.value = data ?? {}
240
+ }
241
+
242
+ // Préférences de l'usager
243
+ {
244
+ const data = (await axios
245
+ .getAxios()
246
+ .get<
247
+ Preference[]
248
+ >(`${props.urlBase}/api/ComposantUI/Preferences/${props.formulaireId}/TypeIntervenant/${props.typeIntervenant}`)) as unknown as Preference[]
249
+ preferences.value = (data ?? []).filter(Boolean)
250
+ }
251
+
252
+ definitionSelectionDepart()
253
+
254
+ // Sauvegarde immédiate pour les choix uniques (si demandé par ton flux)
255
+ await sauvegarder()
256
+
257
+ chargementEnCours.value = false
258
+ }
259
+
260
+ async function ouvrir() {
261
+ if (!activerDivPreferences.value) {
262
+ await chargementPreferences()
263
+ visible.value = true
264
+ await nextTick()
265
+ modale.value?.ouvrir()
266
+ return
267
+ }
268
+
269
+ if (!toutesUnitesPreferencesSelectionnees.value) {
270
+ visible.value = true
271
+ await nextTick()
272
+ modale.value?.ouvrir()
273
+ return
274
+ }
275
+
276
+ await ok()
277
+ }
278
+
279
+ function annuler() {
280
+ visible.value = false
281
+ modeModifier.value = false
282
+ emit('annuler')
283
+ }
284
+
285
+ function modifier() {
286
+ modeModifier.value = true
287
+ visible.value = true
288
+ modale.value?.ouvrir()
289
+ }
290
+
291
+ async function sauvegarder() {
292
+ for (const uniteId in dictChaisesReleve.value) {
293
+ const chaiseId = selection.value[uniteId]
294
+ if (!chaiseId || chaiseId <= 0) continue
295
+
296
+ const chaises = dictChaisesReleve.value[uniteId] ?? []
297
+ if (chaises.length === 0) continue
298
+
299
+ // Préférence existante pour CETTE unité ?
300
+ const prefIndexDeUnite = preferences.value.findIndex(p => chaises.some(c => c.id === p.chaiseId))
301
+
302
+ try {
303
+ const data = (await axios
304
+ .getAxios()
305
+ .put<Preference>(
306
+ `${props.urlBase}/api/ComposantUI/Preferences/${props.formulaireId}` +
307
+ `/Unite/${encodeURIComponent(uniteId)}` +
308
+ `/Chaise/${chaiseId}` +
309
+ `/TypeIntervenant/${props.typeIntervenant}`,
310
+ )) as unknown as Preference
311
+ const itemRecu = data as Preference
312
+
313
+ if (prefIndexDeUnite >= 0) {
314
+ // remplace l'élément à l'index
315
+ preferences.value.splice(prefIndexDeUnite, 1, itemRecu)
316
+ } else {
317
+ // ajoute un nouvel élément
318
+ preferences.value.push(itemRecu)
319
+ }
320
+ } catch (e) {
321
+ console.error(e)
322
+ }
323
+ }
324
+ }
325
+ async function soumettre() {
326
+ modeModifier.value = false
327
+ await ouvrir()
328
+ }
329
+ /** Validation finale */
330
+ async function ok() {
331
+ await sauvegarder()
332
+
333
+ if (toutesUnitesPreferencesSelectionnees.value) {
334
+ if (!modeModifier.value) emit('confirmer')
335
+ visible.value = false
336
+ modeModifier.value = false
337
+ } else {
338
+ afficherErreur.value = true
339
+ await nextTick()
340
+ }
341
+ modale.value?.fermer()
342
+ }
343
+
344
+ onMounted(async () => {
345
+ chargementEnCours.value = true
346
+ await charger()
347
+ chargementEnCours.value = false
348
+ })
349
+
350
+ // si le type d'intervenant change
351
+ watch(typeIntervenant, async (nv, ov) => {
352
+ if (nv === ov) return
353
+ await rechargerPourTypeIntervenant()
354
+ })
355
+
356
+ async function rechargerPourTypeIntervenant() {
357
+ try {
358
+ chargementEnCours.value = true
359
+ preferences.value = []
360
+ dictChaisesReleve.value = {}
361
+ selection.value = {}
362
+ afficherErreur.value = false
363
+
364
+ // recharge les données dépendantes
365
+ await chargementPreferences()
366
+ } finally {
367
+ chargementEnCours.value = false
368
+ }
369
+ }
370
+
371
+ defineExpose({ charger, soumettre })
372
+ </script>
@@ -1,187 +1,229 @@
1
- <template>
2
- <v-app-bar
3
- :color="barreCouleur"
4
- class="px-0 mx-0"
5
- :style="{
6
- position: 'sticky',
7
- // variables CSS pour les couleurs dynamiques
8
- '--entete-texte': texteCouleur,
9
- '--entete-icone': iconeCouleur,
10
- }"
11
- height="82px"
12
- >
13
- <v-row
14
- class="pt-2"
15
- @resize="controlAffichage"
16
- >
17
- <v-col
18
- :cols="titreCol"
19
- class="pr-0 mr-0 pl-5"
20
- >
21
- <v-toolbar-title class="titre">
22
- <div class="entete-ligne">
23
- <!-- GAUCHE -->
24
- <div class="entete-gauche">
25
- <!-- Barre de retour -->
26
- <slot name="retour">
27
- <v-icon
28
- v-if="retour"
29
- size="large"
30
- start
31
- :color="iconeCouleur"
32
- icon="mdi-arrow-left-thin"
33
- @click="retournerMenu"
34
- />
35
- </slot>
36
-
37
- <div class="titre-bloc">
38
- <slot name="titre">
39
- <span class="pl-3 titre-texte">{{ props.titre }}</span>
40
- </slot>
41
-
42
- <slot name="etat">
43
- <span
44
- v-if="monEtat?.afficher"
45
- class="pl-10"
46
- >
47
- <v-btn
48
- size="small"
49
- :color="monEtat.couleur"
50
- variant="tonal"
51
- >
52
- {{ monEtat.texte }}
53
- </v-btn>
54
- </span>
55
- </slot>
56
-
57
- <slot name="etatSecondaire">
58
- <span
59
- v-if="monEtatSecondaire?.afficher"
60
- class="pl-3"
61
- >
62
- <v-btn
63
- size="small"
64
- :color="monEtatSecondaire.couleur"
65
- variant="tonal"
66
- >
67
- {{ monEtatSecondaire.texte }}
68
- </v-btn>
69
- </span>
70
- </slot>
71
-
72
- <slot name="soustitre">
73
- <span class="pl-3 soustitre-texte">{{ props.soustitre }}</span>
74
- </slot>
75
- </div>
76
- </div>
77
-
78
- <!-- DROITE -->
79
- <div class="entete-droite">
80
- <slot name="droite" />
81
- </div>
82
- </div>
83
- </v-toolbar-title>
84
- </v-col>
85
- </v-row>
86
-
87
- <!-- Barre en bas -->
88
- <div style="position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background-color: #808a9d" />
89
- </v-app-bar>
90
- </template>
91
-
92
- <script setup lang="ts">
93
- import { useRouter } from 'vue-router'
94
- import { ref, computed } from 'vue'
95
-
96
- interface EnteteEtat {
97
- afficher: boolean
98
- couleur: string
99
- texte: string
100
- }
101
-
102
- interface EnteteEtatSecondaire {
103
- afficher: boolean
104
- couleur: string
105
- texte: string
106
- }
107
-
108
- const router = useRouter()
109
-
110
- const props = defineProps<{
111
- titre: string
112
- soustitre?: string
113
- retour?: string
114
- etat?: EnteteEtat
115
- couleur?: string // couleur de la barre (Vuetify color/hex)
116
- couleurTexte?: string
117
- couleurIcone?: string
118
- etatSecondaire?: EnteteEtatSecondaire
119
- }>()
120
-
121
- const titreCol = ref(12)
122
-
123
- // Fallbacks (tes valeurs actuelles)
124
- const barreCouleur = computed(() => props.couleur ?? 'white')
125
- const texteCouleur = computed(() => props.couleurTexte ?? '#223654')
126
- const iconeCouleur = computed(() => props.couleurIcone ?? 'grisMoyen') // peut être un nom de couleur Vuetify ou un hex
127
-
128
- const monEtat = computed<EnteteEtat>(() => props.etat ?? { afficher: false, couleur: 'primary', texte: 'test' })
129
- const monEtatSecondaire = computed<EnteteEtatSecondaire>(
130
- () => props.etatSecondaire ?? { afficher: false, couleur: 'primary', texte: 'test' },
131
- )
132
-
133
- function retournerMenu() {
134
- if (props.retour) router.push({ name: props.retour })
135
- }
136
- function controlAffichage() {
137
- /* logique resize */
138
- }
139
- </script>
140
-
141
- <style scoped>
142
- .titre {
143
- font-size: 1.85rem;
144
- font-weight: bold;
145
- margin-bottom: 15px;
146
- }
147
- .titre-texte {
148
- color: var(--entete-texte, #223654);
149
- }
150
- .soustitre-texte {
151
- display: block;
152
- font-size: 1rem;
153
- font-weight: normal;
154
- color: var(--entete-texte, #223654);
155
- }
156
- .titre-bloc {
157
- display: inline-block;
158
- vertical-align: middle;
159
- padding-left: 12px;
160
- }
161
- /* Couleur de l’icône (retour) + hover */
162
- .v-icon {
163
- color: var(--entete-icone, #6b7280); /* grisMoyen approx si hex */
164
- }
165
- .v-icon:hover {
166
- filter: brightness(0.85);
167
- }
168
- .entete-ligne {
169
- display: flex;
170
- align-items: center;
171
- width: 100%;
172
- }
173
-
174
- .entete-gauche {
175
- display: flex;
176
- align-items: center;
177
- min-width: 0; /* important pour éviter overflow */
178
- flex: 1;
179
- }
180
-
181
- .entete-droite {
182
- display: flex;
183
- align-items: center;
184
- gap: 12px;
185
- padding-right: 16px;
186
- }
187
- </style>
1
+ <template>
2
+ <v-app-bar
3
+ :color="barreCouleur"
4
+ class="px-0 mx-0"
5
+ :style="{
6
+ position: 'sticky',
7
+ // variables CSS pour les couleurs dynamiques
8
+ '--entete-texte': texteCouleur,
9
+ '--entete-icone': iconeCouleur,
10
+ }"
11
+ height="auto"
12
+ >
13
+ <v-row
14
+ class="pt-2"
15
+ @resize="controlAffichage"
16
+ >
17
+ <v-col
18
+ :cols="titreCol"
19
+ class="pr-0 mr-0 pl-5"
20
+ >
21
+ <v-toolbar-title class="titre">
22
+ <div class="entete-ligne">
23
+ <!-- GAUCHE -->
24
+ <div class="entete-gauche">
25
+ <!-- Barre de retour -->
26
+ <slot name="retour">
27
+ <v-icon
28
+ v-if="retour"
29
+ size="large"
30
+ start
31
+ :color="iconeCouleur"
32
+ icon="mdi-arrow-left-thin"
33
+ @click="retournerMenu"
34
+ />
35
+ </slot>
36
+
37
+ <div class="titre-bloc">
38
+ <slot name="titre">
39
+ <span class="pl-3 titre-texte">{{ props.titre }}</span>
40
+ </slot>
41
+
42
+ <slot name="etat">
43
+ <span
44
+ v-if="monEtat?.afficher"
45
+ class="pl-10"
46
+ >
47
+ <v-btn
48
+ size="small"
49
+ :color="monEtat.couleur"
50
+ variant="tonal"
51
+ >
52
+ {{ monEtat.texte }}
53
+ </v-btn>
54
+ </span>
55
+ </slot>
56
+
57
+ <slot name="etatSecondaire">
58
+ <span
59
+ v-if="monEtatSecondaire?.afficher"
60
+ class="pl-3"
61
+ >
62
+ <v-btn
63
+ size="small"
64
+ :color="monEtatSecondaire.couleur"
65
+ variant="tonal"
66
+ >
67
+ {{ monEtatSecondaire.texte }}
68
+ </v-btn>
69
+ </span>
70
+ </slot>
71
+
72
+ <slot name="soustitre">
73
+ <span class="pl-3 soustitre-texte">{{ props.soustitre }}</span>
74
+ </slot>
75
+ </div>
76
+ </div>
77
+
78
+ <!-- DROITE -->
79
+ <div class="entete-droite mr-1">
80
+ <slot name="droite" />
81
+ </div>
82
+ </div>
83
+ </v-toolbar-title>
84
+ </v-col>
85
+ </v-row>
86
+
87
+ <!-- Barre en bas -->
88
+ <div style="position: absolute; bottom: 0; left: 0; right: 0; height: 2px; background-color: #808a9d" />
89
+ </v-app-bar>
90
+ </template>
91
+
92
+ <script setup lang="ts">
93
+ import { useRouter } from 'vue-router'
94
+ import { ref, computed } from 'vue'
95
+
96
+ interface EnteteEtat {
97
+ afficher: boolean
98
+ couleur: string
99
+ texte: string
100
+ }
101
+
102
+ interface EnteteEtatSecondaire {
103
+ afficher: boolean
104
+ couleur: string
105
+ texte: string
106
+ }
107
+
108
+ const router = useRouter()
109
+
110
+ const props = defineProps<{
111
+ titre: string
112
+ soustitre?: string
113
+ retour?: string
114
+ etat?: EnteteEtat
115
+ couleur?: string // couleur de la barre (Vuetify color/hex)
116
+ couleurTexte?: string
117
+ couleurIcone?: string
118
+ etatSecondaire?: EnteteEtatSecondaire
119
+ }>()
120
+
121
+ const titreCol = ref(12)
122
+
123
+ // Fallbacks (tes valeurs actuelles)
124
+ const barreCouleur = computed(() => props.couleur ?? 'white')
125
+ const texteCouleur = computed(() => props.couleurTexte ?? '#223654')
126
+ const iconeCouleur = computed(() => props.couleurIcone ?? 'grisMoyen') // peut être un nom de couleur Vuetify ou un hex
127
+
128
+ const monEtat = computed<EnteteEtat>(() => props.etat ?? { afficher: false, couleur: 'primary', texte: 'test' })
129
+ const monEtatSecondaire = computed<EnteteEtatSecondaire>(
130
+ () => props.etatSecondaire ?? { afficher: false, couleur: 'primary', texte: 'test' },
131
+ )
132
+
133
+ function retournerMenu() {
134
+ if (props.retour) router.push({ name: props.retour })
135
+ }
136
+ function controlAffichage() {
137
+ /* logique resize */
138
+ }
139
+ </script>
140
+
141
+ <style scoped>
142
+ .titre {
143
+ font-size: 1.85rem;
144
+ font-weight: bold;
145
+ margin-bottom: 15px;
146
+ }
147
+ .titre-texte {
148
+ color: var(--entete-texte, #223654);
149
+ overflow: hidden;
150
+ text-overflow: ellipsis;
151
+ white-space: nowrap;
152
+ max-width: 100%;
153
+ }
154
+ .soustitre-texte {
155
+ display: block;
156
+ font-size: 1rem;
157
+ font-weight: normal;
158
+ color: var(--entete-texte, #223654);
159
+ }
160
+ .titre-bloc {
161
+ display: inline-block;
162
+ vertical-align: middle;
163
+ padding-left: 12px;
164
+ }
165
+ /* Couleur de l’icône (retour) + hover */
166
+ .v-icon {
167
+ color: var(--entete-icone, #6b7280); /* grisMoyen approx si hex */
168
+ }
169
+ .v-icon:hover {
170
+ filter: brightness(0.85);
171
+ }
172
+ .entete-ligne {
173
+ display: flex;
174
+ align-items: center;
175
+ flex-wrap: wrap;
176
+ width: 100%;
177
+ gap: 8px;
178
+ }
179
+
180
+ .entete-gauche {
181
+ display: flex;
182
+ align-items: center;
183
+ min-width: 0; /* important pour éviter overflow */
184
+ flex: 1;
185
+ }
186
+
187
+ .entete-droite {
188
+ display: flex;
189
+ align-items: center;
190
+ gap: 12px;
191
+ padding-right: 16px;
192
+ flex-shrink: 0;
193
+ }
194
+
195
+ @media (max-width: 600px) {
196
+ .entete-gauche {
197
+ width: 100%;
198
+ }
199
+ .entete-droite {
200
+ width: 100%;
201
+ padding-right: 8px;
202
+ padding-left: 0;
203
+ justify-content: flex-end;
204
+ }
205
+ .titre {
206
+ font-size: 1.2rem;
207
+ }
208
+ .titre-bloc {
209
+ display: flex;
210
+ flex-direction: column;
211
+ align-items: flex-start;
212
+ min-width: 0;
213
+ max-width: 100%;
214
+ padding-left: 4px;
215
+ }
216
+ .titre-texte {
217
+ white-space: nowrap;
218
+ overflow: hidden;
219
+ text-overflow: ellipsis;
220
+ max-width: 100%;
221
+ }
222
+ :deep(.v-col) {
223
+ padding-left: 8px !important;
224
+ }
225
+ :deep(.v-toolbar-title__placeholder) {
226
+ padding-inline-start: 0 !important;
227
+ }
228
+ }
229
+ </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codevdesign",
3
- "version": "1.0.65",
3
+ "version": "1.0.67",
4
4
  "description": "Composants Vuetify 3 pour les projets Codev",
5
5
  "files": [
6
6
  "./**/*.vue",