Skip to content

Commit 110dfb1

Browse files
committed
feat: standardize menu part naming and improve menu item handling
- Updated the menu item structure to include a `name` property for better consistency and normalization. - Refactored the menu rendering logic to utilize the `name` property instead of `label` for improved filtering and display. - Introduced a normalization function to standardize menu part names across the application, enhancing menu configuration and visibility. - Adjusted the default menu entries to align with the new naming conventions, ensuring consistency in menu part references.
1 parent 079e639 commit 110dfb1

File tree

6 files changed

+59
-36
lines changed

6 files changed

+59
-36
lines changed

apps/web/src/components/layouts/default/drawer.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ div
1818
q-icon(name="mdi-open-in-new" color="primary")
1919
q-item-section Ouvrir dans un nouvel onglet
2020
q-separator
21-
q-list(v-for="(part, i) in visibleMenuParts" :key="part.label")
21+
q-list(v-for="(part, i) in visibleMenuParts" :key="part.name")
2222
div(v-for="menu in part.menus" :key="menu.path")
2323
q-item(
2424
clickable
@@ -99,7 +99,7 @@ export default defineNuxtComponent({
9999
const visibleMenuParts = computed(() => menuParts.value
100100
.map(part => ({
101101
...part,
102-
menus: getMenuByPart(part.label).filter(menu => menu.hideInMenuBar !== true),
102+
menus: getMenuByPart(part.name!).filter(menu => menu.hideInMenuBar !== true),
103103
}))
104104
.filter(part => part.menus.length > 0))
105105

apps/web/src/composables/useMenu.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type useMenuReturnType = {
1717

1818
type MenuPartItem = {
1919
label: string
20+
name?: string
2021
position: number
2122
}
2223

@@ -54,14 +55,23 @@ function useMenu(identityStateStore: ReturnType<typeof useIdentityStateStore>):
5455
const overrideLabel = typeof overridePart?.label === 'string' ? overridePart.label : ''
5556
if (!overrideLabel) continue
5657

57-
const matchIndex = mergedParts.findIndex((part) => part.label === overrideLabel)
58+
const overrideRawName = typeof overridePart?.name === 'string' ? overridePart.name : ''
59+
const overrideName = overrideRawName.trim() ? normalizeNameFromLabel(overrideRawName) : normalizeNameFromLabel(overrideLabel)
60+
61+
const matchIndex = mergedParts.findIndex((part) => normalizeNameFromLabel(part.name || part.label) === overrideName)
5862
if (matchIndex >= 0) {
5963
mergedParts[matchIndex] = {
6064
...mergedParts[matchIndex],
6165
...(overridePart as any),
66+
label: overrideLabel,
67+
name: overrideName,
6268
}
6369
} else {
64-
mergedParts.push(overridePart as any)
70+
mergedParts.push({
71+
...(overridePart as any),
72+
label: overrideLabel,
73+
name: overrideName,
74+
})
6575
}
6676
}
6777

@@ -190,7 +200,8 @@ function useMenu(identityStateStore: ReturnType<typeof useIdentityStateStore>):
190200
}
191201

192202
function getMenuByPart(part: string): MenuItem[] {
193-
return getMenu().filter((menu) => menu.part === part)
203+
const normalizedPart = normalizeNameFromLabel(part)
204+
return getMenu().filter((menu) => normalizeNameFromLabel(menu.part) === normalizedPart)
194205
}
195206

196207
async function initialize(): Promise<void> {

apps/web/src/constants/defaultMenuEntries.ts

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import { IdentityState } from '~/constants/enums'
66
export function getDefaultMenuEntries(): MenuItem[] {
77
const { getStateBadge } = useIdentityStates()
88

9+
function normalizeNameFromLabel(label: string): string {
10+
return label.toLowerCase().trim().replace(/\s+/g, '_')
11+
}
12+
913
return [
1014
{
1115
icon: 'mdi-account',
@@ -14,7 +18,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
1418
path: '/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0',
1519
color: 'primary',
1620
badge: { color: 'primary' },
17-
part: MenuPart.DONNEES,
21+
part: normalizeNameFromLabel(MenuPart.DONNEES),
1822
hideInMenuBar: false,
1923
acl: ['/management/identities'],
2024
},
@@ -24,7 +28,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
2428
name: 'exporter',
2529
path: '/identities/export',
2630
color: 'accent',
27-
part: MenuPart.DONNEES,
31+
part: normalizeNameFromLabel(MenuPart.DONNEES),
2832
hideInMenuBar: true,
2933
acl: ['/management/identities'],
3034
},
@@ -34,7 +38,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
3438
name: 'journal_des_jobs',
3539
path: '/jobs/table?filters[:state]=-1',
3640
color: 'info',
37-
part: MenuPart.DONNEES,
41+
part: normalizeNameFromLabel(MenuPart.DONNEES),
3842
hideInMenuBar: false,
3943
acl: ['/core/jobs'],
4044
},
@@ -44,7 +48,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
4448
name: 'cycle_de_vie_des_identités',
4549
path: '/lifecycles/table',
4650
color: 'info',
47-
part: MenuPart.DONNEES,
51+
part: normalizeNameFromLabel(MenuPart.DONNEES),
4852
hideInMenuBar: false,
4953
acl: ['/management/lifecycle'],
5054
},
@@ -55,7 +59,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
5559
path: '/audits/table',
5660
color: 'lime-8',
5761
hideInMenuBar: false,
58-
part: MenuPart.DONNEES,
62+
part: normalizeNameFromLabel(MenuPart.DONNEES),
5963
acl: ['/core/audits'],
6064
},
6165
{
@@ -64,7 +68,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
6468
name: 'detection_des_doublons',
6569
path: '/identities/fusion',
6670
color: 'positive',
67-
part: MenuPart.DONNEES,
71+
part: normalizeNameFromLabel(MenuPart.DONNEES),
6872
hideInMenuBar: true,
6973
acl: ['/management/identities'],
7074
},
@@ -75,7 +79,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
7579
path: '/identities/trash',
7680
color: 'grey-10',
7781
hideInMenuBar: true,
78-
part: MenuPart.DONNEES,
82+
part: normalizeNameFromLabel(MenuPart.DONNEES),
7983
acl: ['/management/identities'],
8084
},
8185
{
@@ -86,7 +90,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
8690
color: 'warning',
8791
textColor: 'black',
8892
badge: getStateBadge(IdentityState.TO_VALIDATE),
89-
part: MenuPart.ETATS,
93+
part: normalizeNameFromLabel(MenuPart.ETATS),
9094
hideInMenuBar: false,
9195
acl: ['/management/identities'],
9296
},
@@ -98,7 +102,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
98102
color: 'secondary',
99103
textColor: 'black',
100104
badge: getStateBadge(IdentityState.TO_COMPLETE),
101-
part: MenuPart.ETATS,
105+
part: normalizeNameFromLabel(MenuPart.ETATS),
102106
hideInMenuBar: false,
103107
acl: ['/management/identities'],
104108
},
@@ -109,7 +113,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
109113
path: `/identities/table?readonly=1&sort[metadata.lastUpdatedAt]=desc&skip=0&filters[%23state]=${IdentityState.TO_SYNC}`,
110114
color: 'orange-8',
111115
badge: getStateBadge(IdentityState.TO_SYNC),
112-
part: MenuPart.ETATS,
116+
part: normalizeNameFromLabel(MenuPart.ETATS),
113117
hideInMenuBar: false,
114118
acl: ['/management/identities'],
115119
},
@@ -120,7 +124,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
120124
path: `/identities/table?readonly=1&sort[metadata.lastUpdatedAt]=desc&skip=0&filters[%23state]=${IdentityState.PROCESSING}`,
121125
color: 'grey-8',
122126
badge: getStateBadge(IdentityState.PROCESSING),
123-
part: MenuPart.ETATS,
127+
part: normalizeNameFromLabel(MenuPart.ETATS),
124128
hideInMenuBar: false,
125129
acl: ['/management/identities'],
126130
},
@@ -131,7 +135,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
131135
path: `/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0&filters[%23state]=${IdentityState.SYNCED}`,
132136
badge: getStateBadge(IdentityState.SYNCED),
133137
color: 'positive',
134-
part: MenuPart.ETATS,
138+
part: normalizeNameFromLabel(MenuPart.ETATS),
135139
hideInMenuBar: false,
136140
acl: ['/management/identities'],
137141
},
@@ -146,7 +150,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
146150
color: 'linear-gradient(135deg, #7C3AED 0%, #EC4899 50%, #F97316 100%)',
147151
textColor: 'white',
148152
},
149-
part: MenuPart.ETATS,
153+
part: normalizeNameFromLabel(MenuPart.ETATS),
150154
hideInMenuBar: false,
151155
acl: ['/management/identities'],
152156
},
@@ -157,7 +161,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
157161
path: `/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0&filters[%23state]=${IdentityState.ON_ERROR}`,
158162
color: 'negative',
159163
badge: getStateBadge(IdentityState.ON_ERROR),
160-
part: MenuPart.ETATS,
164+
part: normalizeNameFromLabel(MenuPart.ETATS),
161165
hideInMenuBar: false,
162166
acl: ['/management/identities'],
163167
},
@@ -168,7 +172,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
168172
path: `/identities/table?sort[metadata.lastUpdatedAt]=desc&skip=0&filters[%23state]=${IdentityState.DONT_SYNC}`,
169173
color: 'black',
170174
badge: getStateBadge(IdentityState.DONT_SYNC),
171-
part: MenuPart.ETATS,
175+
part: normalizeNameFromLabel(MenuPart.ETATS),
172176
hideInMenuBar: false,
173177
acl: ['/management/identities'],
174178
},
@@ -178,7 +182,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
178182
name: 'invitations_non_envoyées',
179183
path: '/identities/table?limit=10&skip=0&filters[%23initState]=0&sort[metadata.lastUpdatedAt]=desc',
180184
color: 'negative',
181-
part: MenuPart.ACTIVATION,
185+
part: normalizeNameFromLabel(MenuPart.ACTIVATION),
182186
badge: { color: 'negative' },
183187
hideInMenuBar: false,
184188
acl: ['/management/identities'],
@@ -190,7 +194,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
190194
path: '/identities/table?limit=10&skip=0&filters[%23initState]=1&sort[metadata.lastUpdatedAt]=desc',
191195
color: 'warning',
192196
textColor: 'black',
193-
part: MenuPart.ACTIVATION,
197+
part: normalizeNameFromLabel(MenuPart.ACTIVATION),
194198
badge: { color: 'warning', textColor: 'black' },
195199
hideInMenuBar: false,
196200
acl: ['/management/identities'],
@@ -202,7 +206,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
202206
path: '/identities/table?limit=10&skip=0&filters[%23initState]=2&sort[metadata.lastUpdatedAt]=desc',
203207
color: 'positive',
204208
textColor: 'white',
205-
part: MenuPart.ACTIVATION,
209+
part: normalizeNameFromLabel(MenuPart.ACTIVATION),
206210
badge: { color: 'positive' },
207211
hideInMenuBar: false,
208212
acl: ['/management/identities'],
@@ -213,7 +217,7 @@ export function getDefaultMenuEntries(): MenuItem[] {
213217
name: 'invitations_périmées',
214218
path: '/identities/outdated',
215219
color: 'accent',
216-
part: MenuPart.ACTIVATION,
220+
part: normalizeNameFromLabel(MenuPart.ACTIVATION),
217221
hideInMenuBar: false,
218222
acl: ['/management/identities'],
219223
},

apps/web/src/constants/variables.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,32 +3,37 @@ export const MaxMenuBadgeCount = 99999
33
export const DefaultMenuParts = [
44
{
55
label: 'Données',
6+
name: 'données',
67
position: 10,
78
},
89
{
910
label: 'Listes',
11+
name: 'listes',
1012
position: 20,
1113
},
1214
{
1315
label: 'Affectations',
16+
name: 'affectations',
1417
position: 30,
1518
},
1619
{
1720
label: 'Etats',
21+
name: 'etats',
1822
position: 40,
1923
},
2024
{
2125
label: 'Activation',
26+
name: 'activation',
2227
position: 50,
2328
},
2429
]
2530

2631
export enum MenuPart {
27-
DONNEES = 'Données',
28-
LISTES = 'Listes',
29-
AFFECTATIONS = 'Affectations',
30-
ETATS = 'Etats',
31-
ACTIVATION = 'Activation',
32+
DONNEES = 'données',
33+
LISTES = 'listes',
34+
AFFECTATIONS = 'affectations',
35+
ETATS = 'etats',
36+
ACTIVATION = 'activation',
3237
}
3338

3439
export const NewTargetId = '000000000000000000000000'

apps/web/src/pages/index.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ q-page.container(padding)
33
.column.no-wrap
44
div(v-for="(part, i) in menuParts", :key="i")
55
q-bar.q-pa-lg.q-mb-sm.transparent(
6-
v-show='getMenuByPart(part.label).filter(i => i.hideInDashboard !== true).length' dense
6+
v-show='getMenuByPart(part.name).filter(i => i.hideInDashboard !== true).length' dense
77
)
88
.text-h5 {{ part.label }}
99

1010
.row.q-col-gutter-md
1111
.col.col-12.col-sm-6.col-md-4.col-lg-3(
12-
v-for="item in getMenuByPart(part.label).filter(i => i.hideInDashboard !== true)" :key="item.label"
12+
v-for="item in getMenuByPart(part.name).filter(i => i.hideInDashboard !== true)" :key="item.label"
1313
)
1414
q-btn.q-py-md.fit(
1515
:style='getButtonStyle(item)'
@@ -47,7 +47,7 @@ q-page.container(padding)
4747
q-item-label Debug
4848
q-separator.q-mt-md.q-mb-sm(
4949
v-if="i < menuParts.length - 1"
50-
v-show='getMenuByPart(part.label).filter(i => i.hideInDashboard !== true).length'
50+
v-show='getMenuByPart(part.name).filter(i => i.hideInDashboard !== true).length'
5151
)
5252
sesame-core-debug-menu-entry-dialog(
5353
v-model="debugDialogOpen"

apps/web/src/server/routes/config/ui.get.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'
22
import * as path from 'node:path'
33
import { defineEventHandler } from 'h3'
44
import { parse } from 'yaml'
5+
import { DefaultMenuParts } from '../../../constants/variables'
56
import { getDefaultMenuEntries } from '../../../constants/defaultMenuEntries'
67

78
type MenuEntry = Record<string, unknown>
@@ -44,17 +45,18 @@ async function readYamlFile(path: string): Promise<Record<string, unknown>> {
4445

4546
let defaultMenuEntriesJsonGenerated = false
4647

47-
async function ensureDefaultMenuEntriesJson(): Promise<void> {
48+
async function ensureDefaultMenuDataJson(): Promise<void> {
4849
if (defaultMenuEntriesJsonGenerated) return
4950
defaultMenuEntriesJsonGenerated = true
5051

5152
const configDir = path.resolve(process.cwd(), 'config')
52-
const outputPath = path.join(configDir, 'default-menu-entries.json')
53+
const outputPath = path.join(configDir, 'default-menu-data.json')
5354

5455
try {
5556
await mkdir(configDir, { recursive: true })
5657

5758
const entries = getDefaultMenuEntries()
59+
const parts = DefaultMenuParts
5860

5961
const payload = {
6062
_meta: {
@@ -63,17 +65,18 @@ async function ensureDefaultMenuEntriesJson(): Promise<void> {
6365
generatedBy: 'sesame-orchestrator',
6466
note: 'FICHIER AUTO-GÉNÉRÉ AU DÉMARRAGE. NE PAS MODIFIER.',
6567
},
68+
parts,
6669
entries,
6770
}
6871

6972
await writeFile(outputPath, JSON.stringify(payload, null, 2), 'utf8')
7073
} catch (error) {
71-
console.debug('[config/ui] Unable to write default menu entries json', error)
74+
console.debug('[config/ui] Unable to write default menu data json', error)
7275
}
7376
}
7477

7578
export default defineEventHandler(async () => {
76-
await ensureDefaultMenuEntriesJson()
79+
await ensureDefaultMenuDataJson()
7780

7881
const menusFile = await readYamlFile('./config/menus.yml')
7982
const identitiesColumnsFile = await readYamlFile('./config/identities-columns.yml')

0 commit comments

Comments
 (0)