瀏覽代碼

refactors menu builders into services

Olivier Massot 3 年之前
父節點
當前提交
a11f2c5646
共有 53 個文件被更改,包括 1220 次插入1161 次删除
  1. 10 10
      components/Layout/Menu.vue
  2. 0 57
      composables/layout/Menus/accessMenu.ts
  3. 0 97
      composables/layout/Menus/accountMenu.ts
  4. 0 59
      composables/layout/Menus/admin2iosMenu.ts
  5. 0 35
      composables/layout/Menus/agendaMenu.ts
  6. 0 62
      composables/layout/Menus/baseMenu.ts
  7. 0 62
      composables/layout/Menus/billingMenu.ts
  8. 0 42
      composables/layout/Menus/communicationMenu.ts
  9. 0 86
      composables/layout/Menus/configurationMenu.ts
  10. 0 98
      composables/layout/Menus/cotisationsMenu.ts
  11. 0 26
      composables/layout/Menus/donorsMenu.ts
  12. 0 58
      composables/layout/Menus/educationalMenu.ts
  13. 0 26
      composables/layout/Menus/equipmentMenu.ts
  14. 0 26
      composables/layout/Menus/medalsMenu.ts
  15. 0 30
      composables/layout/Menus/myAccessesMenu.ts
  16. 0 42
      composables/layout/Menus/myFamilyMenu.ts
  17. 0 47
      composables/layout/Menus/parametersMenu.ts
  18. 0 46
      composables/layout/Menus/statsMenu.ts
  19. 0 52
      composables/layout/Menus/websiteMenu.ts
  20. 0 143
      composables/layout/useMenu.ts
  21. 117 0
      composables/layout/useMenuBuilder.ts
  22. 1 1
      config/abilities/pages/cotisations.yaml
  23. 7 3
      layouts/default.vue
  24. 2 2
      pages/organization.vue
  25. 2 2
      pages/organization/index.vue
  26. 102 0
      services/menuBuilder/abstractMenuBuilder.ts
  27. 56 0
      services/menuBuilder/accessMenuBuilder.ts
  28. 88 0
      services/menuBuilder/accountMenuBuilder.ts
  29. 58 0
      services/menuBuilder/admin2iosMenuBuilder.ts
  30. 38 0
      services/menuBuilder/agendaMenuBuilder.ts
  31. 62 0
      services/menuBuilder/billingMenuBuilder.ts
  32. 42 0
      services/menuBuilder/communicationMenuBuilder.ts
  33. 82 0
      services/menuBuilder/configurationMenuBuilder.ts
  34. 98 0
      services/menuBuilder/cotisationsMenuBuilder.ts
  35. 23 0
      services/menuBuilder/donorsMenuBuilder.ts
  36. 59 0
      services/menuBuilder/educationalMenuBuilder.ts
  37. 22 0
      services/menuBuilder/equipmentMenuBuilder.ts
  38. 54 0
      services/menuBuilder/mainMenuBuilder.ts
  39. 22 0
      services/menuBuilder/medalsMenuBuilder.ts
  40. 30 0
      services/menuBuilder/myAccessesMenuBuilder.ts
  41. 44 0
      services/menuBuilder/myFamilyMenuBuilder.ts
  42. 44 0
      services/menuBuilder/parametersMenuBuilder.ts
  43. 46 0
      services/menuBuilder/statsMenuBuilder.ts
  44. 24 0
      services/menuBuilder/websiteAdminMenuBuilder.ts
  45. 35 0
      services/menuBuilder/websiteListMenuBuilder.ts
  46. 1 1
      services/profile/accessProfile.ts
  47. 2 2
      services/profile/organizationProfile.ts
  48. 2 2
      services/rights/abilitiesUtils.ts
  49. 6 9
      store/profile/access.ts
  50. 1 1
      store/profile/organization.ts
  51. 1 0
      tsconfig.json
  52. 3 34
      types/interfaces.d.ts
  53. 36 0
      types/menus.d.ts

+ 10 - 10
components/Layout/Menu.vue

@@ -14,12 +14,12 @@ Prend en paramètre une liste de ItemMenu et les met en forme
     </template>
 
     <v-list class="left-menu">
-      <div v-for="(item, i) in menu" :key="i">
+      <div v-for="(item, i) in menu.children" :key="i">
         <!-- Cas 1 : l'item n'a pas d'enfants, c'est un lien -->
         <v-list-item
           v-if="!item.children"
-          :href="item.isExternalLink ? item.to : undefined"
-          :to="!item.isExternalLink ? item.to : undefined"
+          :href="isExternalLink(item) ? item.to : undefined"
+          :to="!isExternalLink(item) ? item.to : undefined"
           exact
         >
           <template v-slot:prepend>
@@ -48,9 +48,9 @@ Prend en paramètre une liste de ItemMenu et les met en forme
 
           <v-list-item
             v-for="child in item.children"
-            :key="child.title"
-            :href="child.isExternalLink ? child.to : undefined"
-            :to="!child.isExternalLink ? child.to : undefined"
+            :key="child.label"
+            :href="isExternalLink(child) ? child.to : undefined"
+            :to="!isExternalLink(child) ? child.to : undefined"
             router
             exact
           >
@@ -76,13 +76,13 @@ Prend en paramètre une liste de ItemMenu et les met en forme
 </template>
 
 <script setup lang="ts">
-import { ItemsMenu } from '~/types/interfaces'
 import {onUnmounted, watch, WatchStopHandle} from "@vue/runtime-core";
 import {ref, toRefs} from "@vue/reactivity";
+import {MENU_LINK_TYPE, MenuGroup, MenuItem} from "~/types/menus";
 
 const props = defineProps({
   menu: {
-    type: Array as () => ItemsMenu,
+    type: Object as () => MenuGroup,
     required: true
   },
   miniVariant: {
@@ -98,7 +98,7 @@ const props = defineProps({
 const { openMenu } = toRefs(props)
 const open = ref(true)
 
-//Par défaut si l'écran est trop petit au chargement de la page, le menu doit être fermé.
+// Par défaut si l'écran est trop petit au chargement de la page, le menu doit être fermé.
 if(process.client)
   open.value = window.innerWidth >= 1264
 
@@ -109,7 +109,7 @@ const unwatch: WatchStopHandle = watch(openMenu, (newValue, oldValue) => {
   }
 })
 
-console.log(props.menu)
+const isExternalLink = (menuItem: MenuItem | MenuGroup) => 'type' in menuItem && menuItem.type === MENU_LINK_TYPE.EXTERNAL
 
 onUnmounted(() => {
   unwatch()

+ 0 - 57
composables/layout/Menus/accessMenu.ts

@@ -1,57 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import { $organizationProfile } from '~/services/profile/organizationProfile'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class AccessMenu
- * Classe pour la construction du Menu Répertoire
- */
-class AccessMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Répertoire, ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const children: ItemsMenu = []
-    const {can} = useAbility()
-
-    if (can('display', 'accesses_page')) {
-      const organization = $organizationProfile()
-      const to = organization.isSchool() ? '/students/list/' : '/adherent/list/'
-      children.push(this.constructMenuItem('person', {name: 'fa-user'}, to, true))
-    }
-
-    if (can('display', 'student_registration_page')) {
-      children.push(this.constructMenuItem('family_view', {name: 'fa-users'}, '/student_registration/new', true))
-    }
-
-    if (can('display', 'education_student_next_year_page')) {
-      children.push(this.constructMenuItem('education_student_next_year', {name: 'fa-list-alt'}, '/education_student_next_year/list/', true))
-    }
-
-    if (can('display', 'commissions_page')) {
-      children.push(this.constructMenuItem('commissions', {name: 'fa-street-view'}, '/commissions/list/', true))
-    }
-
-    if (can('display', 'network_children_page')) {
-      children.push(this.constructMenuItem('network', {name: 'fa-sitemap'}, 'networks/list/', true))
-    }
-
-    if (can('display', 'network_parents_page')) {
-      children.push(this.constructMenuItem('my_network', {name: 'fa-sitemap'}, '/network_artist_schools/list/', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('address_book', {name: 'fa-address-book'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getAccessMenu = () => new AccessMenu().getMenu()

+ 0 - 97
composables/layout/Menus/accountMenu.ts

@@ -1,97 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAccessProfileStore} from "~/store/profile/access";
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class AccountMenu
- * Classe pour la construction du Menu Mon compte
- */
-class AccountMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Header Configuration ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getHeaderMenu (): ItemMenu | null {
-    const accessProfileStore = useAccessProfileStore();
-    const {can} = useAbility()
-
-    const children: ItemsMenu = []
-
-    if (can('display', 'my_schedule_page')) {
-      children.push(this.constructMenuItem('my_schedule_page', undefined, '/my_calendar', true))
-    }
-
-    if (can('display', 'attendance_bookings_page')) {
-      children.push(this.constructMenuItem('attendance_bookings_menu', undefined, '/own_attendance', true))
-    }
-
-    if (can('display', 'my_attendance_page')) {
-      children.push(this.constructMenuItem('my_attendance', undefined, '/my_attendances/list/', true))
-    }
-
-    if (can('display', 'my_invitation_page')) {
-      children.push(this.constructMenuItem('my_invitation', undefined, '/my_invitations/list/', true))
-    }
-
-    if (can('display', 'my_students_page')) {
-      children.push(this.constructMenuItem('my_students', undefined, '/my_students/list/', true))
-    }
-
-    if (can('display', 'my_students_education_students_page')) {
-      children.push(this.constructMenuItem('my_students_education_students', undefined, '/my_students_education_students/list/', true))
-    }
-
-    if (can('display', 'criteria_notations_page') || can('display', 'criteria_notations_page_from_account_menu')) {
-      children.push(this.constructMenuItem('criteria_notations', undefined, '/criteria_notations/list/', true))
-    }
-
-    if (can('display', 'my_education_students_page')) {
-      children.push(this.constructMenuItem('my_education_students', undefined, `/main/my_profile/${accessProfileStore.id}/dashboard/my_education_students/list/`, true))
-    }
-
-    if (can('display', 'send_an_email_page')) {
-      children.push(this.constructMenuItem('send_an_email', undefined, `/list/create/emails`, true))
-    }
-
-    if (can('display', 'my_documents_page')) {
-      children.push(this.constructMenuItem('my_documents', undefined, `/main/my_profile/${accessProfileStore.id}/dashboard/show/my_access_file`, true))
-    }
-
-    if (can('display', 'my_profile_page')) {
-      children.push(this.constructMenuItem('my_profile', undefined, `/main/my_profile/${accessProfileStore.id}/dashboard`, true))
-    }
-
-    if (can('display', 'adherent_list_page')) {
-      children.push(this.constructMenuItem('adherent_list', undefined, `/adherent_contacts/list/`, true))
-    }
-
-    if (can('display', 'subscription_page')) {
-      children.push(this.constructMenuItem('my_subscription', undefined, `/subscription`))
-    }
-
-    if (can('display', 'my_bills_page')) {
-      children.push(this.constructMenuItem('my_bills', undefined, `/main/my_profile/${accessProfileStore.id}/dashboard/show/my_bills`, true))
-    }
-
-    if (can('display', 'cmf_licence_person_page')) {
-      children.push(this.constructMenuItem('print_my_licence', undefined, `/licence_cmf/user`, true))
-    }
-
-    const accountMenu = this.constructMenuItem('my_account', {
-      avatarId: accessProfileStore.avatarId,
-      avatarByDefault: accessProfileStore.gender == 'MISTER' ? 'men-1.png' : 'women-1.png'
-    }, undefined, undefined, children, false)
-
-    const actions: ItemsMenu = [];
-    actions.push(this.constructMenuItem('logout', undefined, `/logout`, true))
-
-    accountMenu.actions = actions
-
-    return accountMenu
-  }
-}
-
-export const getAccountMenu = () => new AccountMenu().getHeaderMenu()

+ 0 - 59
composables/layout/Menus/admin2iosMenu.ts

@@ -1,59 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class Admin2iosMenu
- * Classe pour la construction du Menu Admin 2IOS
- */
-class Admin2iosMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Administration 2ios ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-
-    const children: ItemsMenu = []
-
-    if (can('display', 'all_accesses_page')) {
-      children.push(this.constructMenuItem('all_accesses', {name: 'fa-users'}, '/all_accesses/list/', true))
-    }
-
-    if (can('display', 'all_organizations_page')) {
-      children.push(this.constructMenuItem('all_organizations', {name: 'fa-building'}, '/organization_params/list/', true))
-    }
-
-    if (can('display', 'tips_page')) {
-      children.push(this.constructMenuItem('tips', {name: 'fa-info-circle'}, '/tips/list/', true))
-    }
-
-    if (can('display', 'dgv_page')) {
-      children.push(this.constructMenuItem('dgv', {name: 'fa-house-damage'}, '/admin2ios/dgv', true))
-    }
-
-    if (can('display', 'cmf_cotisation_page')) {
-      children.push(this.constructMenuItem('cmf_cotisation', {name: 'fa-info-circle'}, '/admin2ios/cotisationcmf', true))
-    }
-
-    if (can('display', 'right_page')) {
-      children.push(this.constructMenuItem('right_menu', {name: 'fa-balance-scale-right'}, '/admin2ios/right', true))
-    }
-
-    if (can('display', 'tree_page')) {
-      children.push(this.constructMenuItem('tree_menu', {name: 'fa-sitemap'}, '/admin2ios/tree', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('admin2ios', {name: 'fa-sitemap'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getAdmin2iosMenu = () => new Admin2iosMenu().getMenu()

+ 0 - 35
composables/layout/Menus/agendaMenu.ts

@@ -1,35 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class AgendaMenu
- * Classe pour la construction du Menu agenda
- */
-class AgendaMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Agenda ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'agenda_page')) {
-      children.push(this.constructMenuItem('schedule', {name: 'fa-calendar-alt'}, '/calendar', true))
-    }
-
-    if (can('display', 'attendance_page')) {
-      children.push(this.constructMenuItem('attendances', {name: 'fa-calendar-check'}, '/attendances/list/', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    }
-    return children.length > 0 ? this.constructMenuItem('schedule', {name: 'fa-calendar-alt'}, undefined, undefined, children) : null
-  }
-}
-
-export const getAgendaMenu = () => new AgendaMenu().getMenu()

+ 0 - 62
composables/layout/Menus/baseMenu.ts

@@ -1,62 +0,0 @@
-import {ItemMenu, ItemsMenu, IconItem} from '~/types/interfaces'
-import {useRuntimeConfig} from "#app";
-import {RuntimeConfig} from "@nuxt/schema";
-import Url from "~/services/utils/url";
-
-/**
- * @category composables/layout/Menus
- * @class BaseMenu
- * Classe abstraite pour chacun des menus
- */
-class BaseMenu {
-  protected $config: RuntimeConfig;
-
-  /**
-   * @constructor
-   * Initialisation des services issus du context
-   */
-  constructor () {
-    this.$config = useRuntimeConfig()
-  }
-
-  /**
-   * Construit un ItemMenu
-   *
-   * @param {IconItem} icon
-   * @param {string} title titre qui sera traduit
-   * @param {string} link lien
-   * @param {boolean} isV1Link est-ce un lien renvoyant vers l'ancien admin ?
-   * @param {Array<ItemMenu>} children Tableau d'ItemMenu représentant les sous menu du menu principal
-   * @param {boolean} isExternalLink est-ce un lien renvoyant vers l'extérieur ?
-   * @return {ItemMenu}
-   */
-  constructMenuItem (title: string, icon?: IconItem, link?: string, isV1Link?: boolean, children?: Array<ItemMenu>, isExternalLink?: boolean): ItemMenu {
-    const v1BaseURL = this.$config.baseUrlAdminLegacy ?? this.$config.public.baseUrlAdminLegacy
-
-    const url = (isV1Link && !isExternalLink) ? Url.join(v1BaseURL, link ?? '') : link
-
-    return children ? {
-      title,
-      icon,
-      children
-    } : {
-      icon,
-      title,
-      to: url,
-      isExternalLink: isExternalLink || isV1Link
-    }
-  }
-
-  getMenu (): ItemMenu | null {
-    return null
-  }
-
-  getMenus (): ItemsMenu | null {
-    return null
-  }
-
-  getHeaderMenu (): ItemMenu | null {
-    return null
-  }
-}
-export default BaseMenu

+ 0 - 62
composables/layout/Menus/billingMenu.ts

@@ -1,62 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class BillingMenu
- * Classe pour la construction du Menu Facturation
- */
-class BillingMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Facturation ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'billing_product_page')) {
-      children.push(this.constructMenuItem('billing_product', {name: 'fa-cube'}, '/intangibles/list/', true))
-    }
-
-    if (can('display', 'billing_products_by_student_page')) {
-      children.push(this.constructMenuItem('billing_products_by_student', {name: 'fa-cubes'}, '/access_intangibles/list/', true))
-    }
-
-    if (can('display', 'billing_edition_page')) {
-      children.push(this.constructMenuItem('billing_edition', {name: 'fa-copy'}, '/billing_edition', true))
-    }
-
-    if (can('display', 'billing_accounting_page')) {
-      children.push(this.constructMenuItem('billing_accounting', {name: 'fa-file-alt'}, '/bill_accountings/list/', true))
-    }
-
-    if (can('display', 'billing_payment_list_page')) {
-      children.push(this.constructMenuItem('billing_payment_list', {name: 'fa-credit-card'}, '/bill_payments_list/list/', true))
-    }
-
-    if (can('display', 'pes_page')) {
-      children.push(this.constructMenuItem('pes_export', {name: 'fa-align-justify'}, '/pes/list/', true))
-    }
-
-    if (can('display', 'berger_levrault_page')) {
-      children.push(this.constructMenuItem('berger_levrault_export', {name: 'fa-align-justify'}, '/berger_levraults/list/', true))
-    }
-
-    if (can('display', 'jvs_page')) {
-      children.push(this.constructMenuItem('jvs_export', {name: 'fa-align-justify'}, '/jvs/list/', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('billing', {name: 'fa-euro-sign'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getBillingMenu = () => new BillingMenu().getMenu()

+ 0 - 42
composables/layout/Menus/communicationMenu.ts

@@ -1,42 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class CommunicationMenu
- * Classe pour la construction du Menu Communication
- */
-class CommunicationMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Communication ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'inbox_page')) {
-      children.push(this.constructMenuItem('inbox', {name: 'fa-inbox'}, '/messages/list/', true))
-    }
-
-    if (can('display', 'message_send_page')) {
-      children.push(this.constructMenuItem('message_send', {name: 'fa-paper-plane'}, '/messagessends/list/', true))
-    }
-
-    if (can('display', 'message_templates_page')) {
-      children.push(this.constructMenuItem('message_templates', {name: 'fa-edit'}, '/templates/list/', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('communication', {name: 'fa-comments'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getCommunicationMenu = () => new CommunicationMenu().getMenu()

+ 0 - 86
composables/layout/Menus/configurationMenu.ts

@@ -1,86 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useProfileOrganizationStore} from "~/store/profile/organization";
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class ConfigurationMenu
- * Classe pour la construction du Menu Paramètres
- */
-class ConfigurationMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Header Configuration ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getHeaderMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const profileOrganizationStore = useProfileOrganizationStore()
-    const children: ItemsMenu = []
-
-    if (can('display', 'organization_page')) {
-      // children.push(this.constructMenu('organization_page', undefined, `/main/organizations/${profileOrganizationStore.id}/dashboard`, true))
-      children.push(this.constructMenuItem('organization_page', undefined, `/organization`, false))
-    }
-
-    if (can('display', 'cmf_licence_page')) {
-      children.push(this.constructMenuItem('cmf_licence_generate', undefined, '/cmf_licence/organization'))
-    }
-
-    if (can('display', 'parameters_page')) {
-      // children.push(this.constructMenu('parameters', undefined,`/main/edit/parameters/${profileOrganizationStore.id}`, true))
-      children.push(this.constructMenuItem('parameters', undefined,`/parameters`, false))
-    }
-
-    if (can('display', 'place_page')) {
-      children.push(this.constructMenuItem('place', undefined, '/places/list/', true))
-    }
-
-    if (can('display', 'education_page')) {
-      children.push(this.constructMenuItem('education', undefined, '/educations/list/', true))
-    }
-
-    if (can('display', 'tag_page')) {
-      children.push(this.constructMenuItem('tag', undefined, '/taggs/list/', true))
-    }
-
-    if (can('display', 'activities_page')) {
-      children.push(this.constructMenuItem('activities', undefined, '/activities/list/', true))
-    }
-
-    if (can('display', 'template_systems_page')) {
-      children.push(this.constructMenuItem('template_systems', undefined,'/template_systems/list/', true))
-    }
-
-    if (can('display', 'billing_settings_page')) {
-      children.push(this.constructMenuItem('billing_settings', undefined, '/main/edit/billing_settings/' + profileOrganizationStore.id, true))
-    }
-
-    if (can('display', 'online_registration_settings_page')) {
-      children.push(this.constructMenuItem('online_registration_settings', undefined, '/main/edit/online_registration_settings/' + profileOrganizationStore.id, true))
-    }
-
-    if (can('display', 'transition_next_year_page')) {
-      children.push(this.constructMenuItem('transition_next_year', undefined, '/transition_next_year', true))
-    }
-
-    if (can('display', 'course_duplication_page')) {
-      children.push(this.constructMenuItem('course_duplication', undefined, '/duplicate_courses', true))
-    }
-
-    if (can('display', 'import_page')) {
-      children.push(this.constructMenuItem('import', undefined, '/import/all', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('configuration', {name: 'fa-cogs'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getConfigurationMenu = () => new ConfigurationMenu().getHeaderMenu()

+ 0 - 98
composables/layout/Menus/cotisationsMenu.ts

@@ -1,98 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class CotisationsMenu
- * Classe pour la construction du Menu Cotisation (CMF)
- */
-class CotisationsMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Cotisations ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'rate_cotisation_page')) {
-      children.push(this.constructMenuItem('rate_cotisation', {name: 'fa-euro-sign'}, '/cotisation/rate', true))
-    }
-
-    if (can('display', 'parameters_cotisation_page')) {
-      children.push(this.constructMenuItem('parameters_cotisation', {name: 'fa-euro-sign'}, '/cotisation/parameter', true))
-    }
-
-    if (can('display', 'send_cotisation_page')) {
-      children.push(this.constructMenuItem('send_cotisation', {name: 'fa-euro-sign'}, '/cotisation/send', true))
-    }
-
-    if (can('display', 'state_cotisation_page')) {
-      children.push(this.constructMenuItem('state_cotisation', {name: 'fa-euro-sign'}, '/cotisation/state', true))
-    }
-
-    if (can('display', 'pay_cotisation_page')) {
-      children.push(this.constructMenuItem('pay_cotisation', {name: 'fa-euro-sign'}, '/cotisation/pay', true))
-    }
-
-    if (can('display', 'check_cotisation_page')) {
-      children.push(this.constructMenuItem('check_cotisation', {name: 'fa-euro-sign'}, '/cotisation/check', true))
-    }
-
-    if (can('display', 'ledger_cotisation_page')) {
-      children.push(this.constructMenuItem('ledger_cotisation', {name: 'fa-euro-sign'}, '/cotisation/ledger', true))
-    }
-
-    if (can('display', 'magazine_cotisation_page')) {
-      children.push(this.constructMenuItem('magazine_cotisation', {name: 'fa-euro-sign'}, '/cotisation/magazine', true))
-    }
-
-    if (can('display', 'ventilated_cotisation_page')) {
-      children.push(this.constructMenuItem('ventilated_cotisation', {name: 'fa-euro-sign'}, '/cotisation/ventilated', true))
-    }
-
-    if (can('display', 'pay_erase_cotisation_page')) {
-      children.push(this.constructMenuItem('pay_erase_cotisation', {name: 'fa-euro-sign'}, '/cotisation/payerase', true))
-    }
-
-    if (can('display', 'resume_cotisation_page')) {
-      children.push(this.constructMenuItem('resume_cotisation', {name: 'fa-euro-sign'}, '/cotisation/resume', true))
-    }
-
-    if (can('display', 'history_cotisation_page')) {
-      children.push(this.constructMenuItem('history_cotisation', {name: 'fa-euro-sign'}, '/cotisation/history', true))
-    }
-
-    if (can('display', 'call_cotisation_page')) {
-      children.push(this.constructMenuItem('call_cotisation', {name: 'fa-euro-sign'}, '/cotisation/call', true))
-    }
-
-    if (can('display', 'history_struture_cotisation_page')) {
-      children.push(this.constructMenuItem('history_struture_cotisation', {name: 'fa-euro-sign'}, '/cotisation/historystructure', true))
-    }
-
-    if (can('display', 'insurance_cotisation_page')) {
-      children.push(this.constructMenuItem('insurance_cotisation', {name: 'fa-euro-sign'}, '/cotisation/insurance', true))
-    }
-
-    if (can('display', 'resume_all_cotisation_page')) {
-      children.push(this.constructMenuItem('resume_all_cotisation', {name: 'fa-euro-sign'}, '/cotisation/resumeall', true))
-    }
-
-    if (can('display', 'resume_pay_cotisation_page')) {
-      children.push(this.constructMenuItem('resume_pay_cotisation', {name: 'fa-euro-sign'}, '/cotisation/resumepay', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('cotisations', {name: 'fa-money-bill'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getCotisationsMenu = () => new CotisationsMenu().getMenu()

+ 0 - 26
composables/layout/Menus/donorsMenu.ts

@@ -1,26 +0,0 @@
-import { ItemMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class DonorsMenu
- * Classe pour la construction du Menu Donneurs
- */
-class DonorsMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Partenariat et Dons ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-
-    if (can('display', 'donors_page')) {
-      return this.constructMenuItem('donors', {name: 'far fa-handshake'}, '/donors/list/', true)
-    }
-    return null
-  }
-}
-
-export const getDonorsMenu = () => new DonorsMenu().getMenu()

+ 0 - 58
composables/layout/Menus/educationalMenu.ts

@@ -1,58 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class EducationalMenu
- * Classe pour la construction du Menu Suivi pédagogique
- */
-class EducationalMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Suivi pédagogique ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'criteria_notations_page')) {
-      children.push(this.constructMenuItem('criteria_notations', {name: 'fa-bars'}, '/criteria_notations/list/', true))
-    }
-
-    if (can('display', 'education_notation_config_page')) {
-      children.push(this.constructMenuItem('education_notation_configs', {name: 'fa-bars'},  '/education_notation_configs/list/', true))
-    }
-
-    if (can('display', 'seizure_period_page')) {
-      children.push(this.constructMenuItem('seizure_period', {name: 'fa-calendar-alt'}, '/education_teachers/list/', true))
-    }
-
-    if (can('display', 'test_seizure_page')) {
-      children.push(this.constructMenuItem('test_seizure', {name: 'fa-pencil-alt'}, '/education_input/list/', true))
-    }
-
-    if (can('display', 'test_validation_page')) {
-      children.push(this.constructMenuItem('test_validation', {name: 'fa-check'}, '/education_notations/list/', true))
-    }
-
-    if (can('display', 'examen_results_page')) {
-      children.push(this.constructMenuItem('examen_results', {name: 'fa-graduation-cap'}, '/examen_convocations/list/', true))
-    }
-
-    if (can('display', 'education_by_student_validation_page')) {
-      children.push(this.constructMenuItem('education_by_student_validation', {name: 'fa-check-square'}, '/education_by_student/list/', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('education_state', {name: 'fa-graduation-cap'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getEducationalMenu = () => new EducationalMenu().getMenu()

+ 0 - 26
composables/layout/Menus/equipmentMenu.ts

@@ -1,26 +0,0 @@
-import { ItemMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class EquipmentMenu
- * Classe pour la construction du Menu Matériel
- */
-class EquipmentMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Equipement ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-
-    if (can('display', 'equipment_page')) {
-      return this.constructMenuItem('equipment', {name: 'fa-cube'}, '/equipment/list/', true)
-    }
-    return null
-  }
-}
-
-export const getEquipmentMenu = () => new EquipmentMenu().getMenu()

+ 0 - 26
composables/layout/Menus/medalsMenu.ts

@@ -1,26 +0,0 @@
-import { ItemMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class MedalsMenu
- * Classe pour la construction du Menu Médailles
- */
-class MedalsMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Médails et Dons ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-
-    if (can('display', 'medals_page')) {
-      return this.constructMenuItem('medals', {name: 'fa-trophy'}, '/medals/list/', true)
-    }
-    return null
-  }
-}
-
-export const getMedalsMenu = () => new MedalsMenu().getMenu()

+ 0 - 30
composables/layout/Menus/myAccessesMenu.ts

@@ -1,30 +0,0 @@
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import {useAccessProfileStore} from "~/store/profile/access";
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class MyAccessesMenu
- * Classe pour la construction du Menu Mon profile
- */
-class MyAccessesMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Header Multi compte ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getHeaderMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    const accessProfileStore = useAccessProfileStore();
-    useEach(accessProfileStore.multiAccesses, (access) => {
-      children.push(this.constructMenuItem(access.name as string, undefined, '/switch/' + access.id, true))
-    })
-
-    return children.length > 0 ? this.constructMenuItem('multiAccesses', {name: 'fa-building'}, undefined, undefined, children) : null
-  }
-}
-
-export const getMyAccessesMenu = () => new MyAccessesMenu().getHeaderMenu()

+ 0 - 42
composables/layout/Menus/myFamilyMenu.ts

@@ -1,42 +0,0 @@
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import {useAccessProfileStore} from "~/store/profile/access";
-import {useProfileOrganizationStore} from "~/store/profile/organization";
-
-/**
- * @category composables/layout/Menus
- * @class MyFamilyMenu
- * Classe pour la construction du Menu Famille
- */
-class MyFamilyMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Header Changement d'utilisateur ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getHeaderMenu (): ItemMenu | null {
-    const children: ItemsMenu = []
-
-    // Si Access des membres de la familles (enfants)
-    const accessProfileStore = useAccessProfileStore()
-    const profileOrganizationStore = useProfileOrganizationStore()
-    useEach(accessProfileStore.familyAccesses, (access) => {
-      const url = `/switch_user/${profileOrganizationStore.id}/${accessProfileStore.id}/${access.id}`
-      children.push(this.constructMenuItem(`${access.givenName} ${access.name}`, {
-        avatarId: access.avatarId,
-        avatarByDefault: access.gender == 'MISTER' ? 'men-1.png' : 'women-1.png'
-      }, url, true))
-    })
-
-    // Si on est en compte swtich, on doit pouvoir retourner au compte d'origine
-    const originalAccess = accessProfileStore.originalAccess
-    if (originalAccess && !originalAccess.isSuperAdminAccess) {
-      const url = `/switch_user/${originalAccess.organization.id}/${originalAccess.id}/exit`
-      children.push(this.constructMenuItem(`${originalAccess.givenName} ${originalAccess.name}`, undefined, url, true))
-    }
-
-    return children.length > 0 ? this.constructMenuItem('familyAccesses', {name: 'fa-users'}, undefined, undefined, children) : null
-  }
-}
-
-export const getMyFamilyMenu = () => new MyFamilyMenu().getHeaderMenu()

+ 0 - 47
composables/layout/Menus/parametersMenu.ts

@@ -1,47 +0,0 @@
-import {ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class ConfigurationMenu
- * Classe pour la construction du Menu Paramètres
- */
-class ParametersMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Header Configuration ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenus (): ItemsMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'parameters_page')) {
-      children.push(this.constructMenuItem('general_params', {name: 'fa-cogs'},`/parameters`, false))
-    }
-    if (can('display', 'parameters_communication_page')) {
-      children.push(this.constructMenuItem('communication_params', {name: 'fa-comments'},`/parameters/communication`, false))
-    }
-    if (can('display', 'parameters_student_page')) {
-      children.push(this.constructMenuItem('students_params', {name: 'fa-users'},`/parameters/student`, false))
-    }
-    if (can('display', 'parameters_education_page')) {
-      children.push(this.constructMenuItem('education_params', {name: 'fa-graduation-cap'},`/parameters/education`, false))
-    }
-    if (can('display', 'parameters_bills_page')) {
-      children.push(this.constructMenuItem('bills_params', {name: 'fa-euro-sign'},`/parameters/billing`, false))
-    }
-    if (can('display', 'parameters_secure_page')) {
-      children.push(this.constructMenuItem('secure_params', {name: 'fa-lock'},`/parameters/secure`, false))
-    }
-
-    if (children.length > 0) {
-      return children
-    } else {
-      return null
-    }
-  }
-}
-
-export const getParametersMenu = () => new ParametersMenu().getMenus()

+ 0 - 46
composables/layout/Menus/statsMenu.ts

@@ -1,46 +0,0 @@
-import { ItemMenu, ItemsMenu, Menu } from '~/types/interfaces'
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import {useAbility} from "@casl/vue";
-
-/**
- * @category composables/layout/Menus
- * @class StatsMenu
- * Classe pour la construction du Menu Statistiques
- */
-class StatsMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Statistique et Dons ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const {can} = useAbility()
-    const children: ItemsMenu = []
-
-    if (can('display', 'report_activity_page')) {
-      children.push(this.constructMenuItem('report_activity', {name: 'fa-chart-bar'}, '/report_activity', true))
-    }
-
-    if (can('display', 'education_quotas_page')) {
-      children.push(this.constructMenuItem('educations_quotas_by_education', {name: 'fa-user-circle'},  '/educations_quotas_by_education_year/list/', true))
-    }
-
-    if (can('display', 'fede_stats_page')) {
-      children.push(this.constructMenuItem('fede_stats', {name: 'fa-chart-bar'}, '/statistic/membersfedeonly', true))
-    }
-
-    if (can('display', 'structure_stats_page')) {
-      children.push(this.constructMenuItem('structure_stats', {name: 'fa-chart-bar'}, '/statistic/membersfedeassos', true))
-    }
-
-    if (children.length === 1) {
-      return children[0]
-    } else if (children.length > 0) {
-      return this.constructMenuItem('stats', {name: 'fa-chart-bar'}, undefined, undefined, children)
-    } else {
-      return null
-    }
-  }
-}
-
-export const getStatsMenu = () => new StatsMenu().getMenu()

+ 0 - 52
composables/layout/Menus/websiteMenu.ts

@@ -1,52 +0,0 @@
-import BaseMenu from '~/composables/layout/Menus/baseMenu'
-import { ItemMenu, ItemsMenu, Menu, organizationState } from '~/types/interfaces'
-import {useProfileOrganizationStore} from "~/store/profile/organization";
-import {useAccessProfileStore} from "~/store/profile/access";
-
-/**
- * @category composables/layout/Menus
- * @class WebsiteMenu
- * Classe pour la construction du Menu Sites internet
- */
-class WebsiteMenu extends BaseMenu implements Menu {
-
-  /**
-   * Construit le menu Site internet ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getMenu (): ItemMenu | null {
-    const profileOrganizationStore = useProfileOrganizationStore()
-    const accessProfileStore = useAccessProfileStore()
-
-    if (!profileOrganizationStore.website && accessProfileStore.isAdminAccess) {
-      return this.constructMenuItem('advanced_modification', {name: 'fa-globe-americas'}, this.getWebsite(profileOrganizationStore) + '/typo3', false, undefined, true)
-    }
-    return null
-  }
-
-  /**
-   * Construit le menu Header des Sites internet ou null si aucune page accessible
-   * @return {ItemMenu | null}
-   */
-  getHeaderMenu (): ItemMenu | null {
-    const profileOrganizationStore = useProfileOrganizationStore()
-
-    const children: ItemsMenu = []
-
-    children.push(this.constructMenuItem(profileOrganizationStore.name as string, undefined, this.getWebsite(profileOrganizationStore), false, undefined, true))
-
-    useEach(profileOrganizationStore.parents, (parent:any) => {
-      if(parent.id != process.env.OPENTALENT_MANAGER_ID){
-        children.push(this.constructMenuItem(parent.name, undefined, this.getWebsite(parent), false, undefined, true))
-      }
-    })
-
-    return children.length > 0 ? this.constructMenuItem('website', {name: 'fa-globe-americas'}, undefined, undefined, children) : null
-  }
-
-  getWebsite (organization: organizationState): string {
-    return organization.website ?? '';
-  }
-}
-
-export const getWebsiteMenu = () => new WebsiteMenu()

+ 0 - 143
composables/layout/useMenu.ts

@@ -1,143 +0,0 @@
-import { ItemMenu, ItemsMenu } from '~/types/interfaces'
-import { getAccessMenu } from '~/composables/layout/Menus/accessMenu'
-import { getAgendaMenu } from '~/composables/layout/Menus/agendaMenu'
-import { getEquipmentMenu } from '~/composables/layout/Menus/equipmentMenu'
-import { getEducationalMenu } from '~/composables/layout/Menus/educationalMenu'
-import { getBillingMenu } from '~/composables/layout/Menus/billingMenu'
-import { getCommunicationMenu } from '~/composables/layout/Menus/communicationMenu'
-import { getDonorsMenu } from '~/composables/layout/Menus/donorsMenu'
-import { getMedalsMenu } from '~/composables/layout/Menus/medalsMenu'
-import { getStatsMenu } from '~/composables/layout/Menus/statsMenu'
-import { getCotisationsMenu } from '~/composables/layout/Menus/cotisationsMenu'
-import { getAdmin2iosMenu } from '~/composables/layout/Menus/admin2iosMenu'
-import { getWebsiteMenu } from '~/composables/layout/Menus/websiteMenu'
-import { getConfigurationMenu } from '~/composables/layout/Menus/configurationMenu'
-import { getMyFamilyMenu } from '~/composables/layout/Menus/myFamilyMenu'
-import { getMyAccessesMenu } from '~/composables/layout/Menus/myAccessesMenu'
-import { getAccountMenu } from '~/composables/layout/Menus/accountMenu'
-import {getParametersMenu} from "~/composables/layout/Menus/parametersMenu";
-import {Ref} from "@vue/reactivity";
-import {useAccessProfileStore} from "~/store/profile/access";
-
-/**
- * @category composables/layout
- * @class Menu
- * Use Classe pour la construction du Menu
- */
-class Menu {
-
-  /**
-   * Construit le menu et mets à jour le state du profile d'access
-   */
-  getLateralMenu (): Ref {
-    const menu: ItemsMenu = []
-
-    const accessMenu: ItemMenu | null = getAccessMenu()
-    if (accessMenu) { menu.push(accessMenu) }
-
-    const agendaMenu: ItemMenu | null = getAgendaMenu()
-    if (agendaMenu) { menu.push(agendaMenu) }
-
-    const equipmentMenu: ItemMenu | null = getEquipmentMenu()
-    if (equipmentMenu) { menu.push(equipmentMenu) }
-
-    const educationalMenu: ItemMenu | null = getEducationalMenu()
-    if (educationalMenu) { menu.push(educationalMenu) }
-
-    const billingMenu: ItemMenu | null = getBillingMenu()
-    if (billingMenu) { menu.push(billingMenu) }
-
-    const communicationMenu: ItemMenu | null = getCommunicationMenu()
-    if (communicationMenu) { menu.push(communicationMenu) }
-
-    const donorsMenu: ItemMenu | null = getDonorsMenu()
-    if (donorsMenu) { menu.push(donorsMenu) }
-
-    const medalsMenu: ItemMenu | null = getMedalsMenu()
-    if (medalsMenu) { menu.push(medalsMenu) }
-
-    const websiteMenu: ItemMenu | null = getWebsiteMenu().getMenu()
-    if (websiteMenu) { menu.push(websiteMenu) }
-
-    const cotisationsMenu: ItemMenu | null = getCotisationsMenu()
-    if (cotisationsMenu) { menu.push(cotisationsMenu) }
-
-    const statsMenu: ItemMenu | null = getStatsMenu()
-    if (statsMenu) { menu.push(statsMenu) }
-
-    const admin2iosMenu: ItemMenu | null = getAdmin2iosMenu()
-    if (admin2iosMenu) { menu.push(admin2iosMenu) }
-
-    // Si l'utilisateur possède au moins un menu alors le menu latéral sera accessible
-    const accessProfileStore = useAccessProfileStore()
-    accessProfileStore.hasLateralMenu = menu.length > 0
-
-    return ref(menu)
-  }
-
-  /**
-   * Construit le menu configuration et met à jour le state du profile d'access
-   */
-  getConfigurationMenu (): Ref {
-    const menu: ItemMenu | null = getConfigurationMenu()
-
-    // Si l'utilisateur possède au moins un menu alors le menu configuration sera accessible
-    const accessProfileStore = useAccessProfileStore()
-    accessProfileStore.hasConfigurationMenu = menu != null
-
-    return ref(menu)
-  }
-
-  /**
-   * Construit le menu Mon Compte
-   */
-  getAccountMenu (): Ref {
-    return ref(getAccountMenu())
-  }
-
-  /**
-   * Construit le menu Mes structure et mets à jour le state du profile d'access
-   */
-  getMyAccessesMenu (): Ref {
-    const menu: ItemMenu | null = getMyAccessesMenu()
-    // Si l'utilisateur possède au moins un menu alors le menu mes structures sera accessible
-    const accessProfileStore = useAccessProfileStore()
-    accessProfileStore.hasAccessesMenu = menu != null
-    return ref(menu)
-  }
-
-  /**
-   * Construit le menu Changement d'utilisateur et mets à jour le state du profile d'access
-   */
-  getMyFamilyMenu (): Ref {
-    const menu: ItemMenu | null = getMyFamilyMenu()
-    // Si l'utilisateur possède au moins un menu alors le menu changement d'utilisateur sera accessible
-    const accessProfileStore = useAccessProfileStore()
-    accessProfileStore.hasFamilyMenu = menu != null
-    return ref(menu)
-  }
-
-  /**
-   * Construit le menu site internet du header
-   */
-  getWebSiteMenu (): Ref {
-    return ref(getWebsiteMenu().getHeaderMenu())
-  }
-
-  /**
-   * Construit le menu Paramètres
-   */
-  getParametersMenu (): Ref {
-    const menu: ItemsMenu | null = getParametersMenu()
-
-    // Si l'utilisateur possède au moins un menu alors le menu latéral sera accessible
-    const accessProfileStore = useAccessProfileStore()
-    accessProfileStore.hasLateralMenu = true
-
-    return ref(menu)
-  }
-}
-
-export function UseMenu() {
-  return new Menu()
-}

+ 117 - 0
composables/layout/useMenuBuilder.ts

@@ -0,0 +1,117 @@
+import {Ref, ref} from "@vue/reactivity";
+import {useAccessProfileStore} from "~/store/profile/access";
+import {useRuntimeConfig} from "#app";
+import {useAbility} from "@casl/vue";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
+import AbstractMenuBuilder from "~/services/menuBuilder/abstractMenuBuilder";
+import MainMenuBuilder from "~/services/menuBuilder/mainMenuBuilder";
+import ConfigurationMenuBuilder from "~/services/menuBuilder/configurationMenuBuilder";
+import AccountMenuBuilder from "~/services/menuBuilder/accountMenuBuilder";
+import MyAccessesMenuBuilder from "~/services/menuBuilder/myAccessesMenuBuilder";
+import MyFamilyMenuBuilder from "~/services/menuBuilder/myFamilyMenuBuilder";
+import WebsiteListMenuBuilder from "~/services/menuBuilder/websiteListMenuBuilder";
+import ParametersMenuBuilder from "~/services/menuBuilder/parametersMenuBuilder";
+
+/**
+ * Renvoie certaines méthodes pour interagir avec les menus
+ *
+ * La méthode `buildMenu` permet de construire un menu ou un sous-menu.
+ * Elle prend en paramètre n'importe quelle subclass de AbstractMenuBuilder
+ *
+ * La methode `hasMenu` permet d'interroger le store pour savoir si un menu portant ce nom a été
+ * construit pour l'utilisateur courant, et si ce menu n'est pas vide.
+ *
+ * Fournit aussi des méthodes pour créer directement les principaux types de menus :
+ *
+ *   - buildMainMenu
+ *   - buildConfigurationMenu
+ *   - buildAccountMenu
+ *   - buildMyAccessesMenu
+ *   - buildMyFamilyMenu
+ *   - buildWebsiteListMenu
+ *   - buildParametersMenu
+ *
+ *
+ * Exemple d'usage :
+ *
+ *     const { buildMenu, hasMenu } = useMenuBuilder()
+ *     const menu = buildMenu(MainMenuBuilder)
+ *
+ *     console.log(hasMenu('Main')) // true
+ */
+export const useMenuBuilder = () => {
+  const runtimeConfig = useRuntimeConfig()
+  const ability = useAbility()
+  const organizationProfile = useOrganizationProfileStore()
+  const accessProfile = useAccessProfileStore()
+
+  /**
+   * Construit un menu selon le builder passé en paramètre
+   * Dans certains cas, met à jour le profil de l'utilisateur dans le store pour garder une trace de l'état du menu
+   *
+   * @param menuBuilder
+   */
+  const buildMenu = (menuBuilder: typeof AbstractMenuBuilder) => {
+    // @ts-ignore
+    const builder = new menuBuilder(runtimeConfig, ability, organizationProfile, accessProfile)
+    const menu = builder.build()
+
+    // On enregistre l'état du menu dans le store
+    accessProfile.hasMenu[menu.name()] = menu.length > 0
+
+    return ref(menu)
+  }
+
+  const hasMenu = (name: string): Ref<boolean> => {
+    // TODO: revoir pour la réactivité
+    return accessProfile.hasMenu[name] ?? ref(false)
+  }
+
+  /**
+   * Construit le menu principal
+   */
+
+  const buildMainMenu = () => buildMenu(MainMenuBuilder)
+
+  /**
+   * Construit le menu configuration
+   */
+  const buildConfigurationMenu = () => buildMenu(ConfigurationMenuBuilder)
+
+  /**
+   * Construit le menu Mon Compte
+   */
+  const buildAccountMenu = () => buildMenu(AccountMenuBuilder)
+
+  /**
+   * Construit le menu Mes structure
+   */
+  const buildMyAccessesMenu = () => buildMenu(MyAccessesMenuBuilder)
+
+  /**
+   * Construit le menu Ma famille (changement d'utilisateur)
+   */
+  const buildMyFamilyMenu = () => buildMenu(MyFamilyMenuBuilder)
+
+  /**
+   * Construit le menu de liste des sites internet
+   */
+  const buildWebsiteListMenu = () => buildMenu(WebsiteListMenuBuilder)
+
+  /**
+   * Construit le menu de liste des sites internet
+   */
+  const buildParametersMenu = () => buildMenu(ParametersMenuBuilder)
+
+  return {
+    buildMenu,
+    hasMenu,
+    buildMainMenu,
+    buildConfigurationMenu,
+    buildAccountMenu,
+    buildMyAccessesMenu,
+    buildMyFamilyMenu,
+    buildWebsiteListMenu,
+    buildParametersMenu,
+  }
+}

+ 1 - 1
config/abilities/pages/cotisations.yaml

@@ -102,7 +102,7 @@
       organization:
         - {function: hasModule, parameters: ['CotisationStructure']}
 
-  history_struture_cotisation_page:
+  history_structure_cotisation_page:
     action: 'display'
     services:
       access:

+ 7 - 3
layouts/default.vue

@@ -24,13 +24,17 @@
 
 <script setup lang="ts">
 import {useAccessProfileStore} from "~/store/profile/access";
-import {UseMenu} from "~/composables/layout/useMenu";
+import {useMenuBuilder} from "~/composables/layout/useMenuBuilder";
+import {computed} from "@vue/reactivity";
 
-const menu = UseMenu().getLateralMenu()
+
+const { buildMainMenu, hasMenu } = useMenuBuilder()
+
+const menu = buildMainMenu()
 
 const accessProfileStore = useAccessProfileStore()
 
-const displayMenu = computed(() => accessProfileStore.hasLateralMenu)
+const displayMenu = computed(() => hasMenu('Main'))
 
 const properties = reactive({
   clipped: false,

+ 2 - 2
pages/organization.vue

@@ -30,7 +30,7 @@
 import {useEntityManager} from "~/composables/data/useEntityManager";
 import {useEntityFetch} from "~/composables/data/useEntityFetch";
 import {computed, ComputedRef, Ref} from "@vue/reactivity";
-import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
 import {Organization} from "~/models/Organization/Organization";
 import {ContactPoint} from "~/models/Core/ContactPoint";
 import {BankAccount} from "~/models/Core/BankAccount";
@@ -45,7 +45,7 @@ import {File} from "~/models/Core/File";
 
 const { em, pending: emPending } = useEntityManager()
 
-const id: number | null = useProfileOrganizationStore().id
+const id: number | null = useOrganizationProfileStore().id
 if (id === null) {
   throw new Error('Missing organization id')
 }

+ 2 - 2
pages/organization/index.vue

@@ -449,7 +449,7 @@ import {useEntityFetch} from "~/composables/data/useEntityFetch";
 import {TypeOfPractice} from "~/models/Organization/TypeOfPractice";
 import {computed, ComputedRef, reactive, ref} from "@vue/reactivity";
 import {$organizationProfile} from "~/services/profile/organizationProfile";
-import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
 import {useExtensionPanel} from "~/composables/layout/useExtensionPanel";
 import {useRoute} from "#app";
 import { useValidation } from "~/composables/form/useValidation";
@@ -465,7 +465,7 @@ import {NetworkOrganization} from "~/models/Network/NetworkOrganization";
 import {OrganizationArticle} from "~/models/Organization/OrganizationArticle";
 import {useI18n} from "vue-i18n";
 
-const id: number | null = useProfileOrganizationStore().id
+const id: number | null = useOrganizationProfileStore().id
 if (id === null) {
   throw new Error('Missing organization id')
 }

+ 102 - 0
services/menuBuilder/abstractMenuBuilder.ts

@@ -0,0 +1,102 @@
+import {MenuItem, MenuItems, IconItem, MENU_LINK_TYPE, MenuGroup, MenuBuilder} from '~/types/menus'
+import {RuntimeConfig} from "@nuxt/schema";
+import Url from "~/services/utils/url";
+import {AnyAbility} from "@casl/ability";
+import {accessState, organizationState} from "~/types/interfaces";
+
+/**
+ * Classe de base des menus et sous-menus.
+ *
+ * La méthode principale est la méthode build
+ */
+abstract class AbstractMenuBuilder implements MenuBuilder{
+  protected runtimeConfig: RuntimeConfig;
+  protected ability: AnyAbility;
+  protected organizationProfile: organizationState;
+  protected accessProfile: accessState;
+
+  constructor (
+      runtimeConfig: RuntimeConfig,
+      ability: AnyAbility,
+      organizationProfile: organizationState,
+      accessProfile: accessState
+  ) {
+    this.runtimeConfig = runtimeConfig
+    this.ability = ability
+    this.organizationProfile = organizationProfile
+    this.accessProfile = accessProfile
+  }
+
+  /**
+   * Retourne un nom court pour désigner le menu que construit ce builder
+   */
+  abstract name(): string
+
+  /**
+   * Construit et retourne un menu ou sous-menu selon le profil de l'utilisateur, le profil de son organisation,
+   * et les droits de l'utilisateur.
+   *
+   * Si le menu comporte plusieurs éléments, retourne une instance de MenuGroup
+   * Si le menu ne comporte qu'un seul élément, retourne une instance de MenuItem
+   * Si le menu ne comporte aucun élément, retourne null
+   */
+  abstract build (): MenuItem | MenuGroup | null
+
+  /**
+   * Construit et retourne un MenuGroup
+   *
+   * @param label
+   * @param icon
+   * @param {Array<MenuItem>} children Tableau d'ItemMenu représentant les sous menu du menu principal
+   * @param active
+   */
+  protected createGroup(
+      label: string,
+      icon?: IconItem,
+      children: MenuItems = [],
+      active: boolean = true
+  ): MenuGroup {
+    return { label, icon, children, active }
+  }
+
+  /**
+   * Construit et retourne un MenuItem
+   *
+   * @param {IconItem} icon
+   * @param {string} label Titre qui sera traduit
+   * @param to
+   * @param type
+   * @param isAction
+   * @param active
+   * @return {MenuItem}
+   */
+  protected createItem (
+      label: string,
+      icon?: IconItem,
+      to: string = '',
+      type: MENU_LINK_TYPE = MENU_LINK_TYPE.INTERNAL,
+      isAction = false,
+      active: boolean = true
+  ): MenuItem {
+    let url: string
+
+    switch(type) {
+      case MENU_LINK_TYPE.V1:
+        const v1BaseURL = this.runtimeConfig.baseUrlAdminLegacy ?? this.runtimeConfig.public.baseUrlAdminLegacy
+        url = Url.join(v1BaseURL, to ?? '')
+        break;
+      default:
+        url = to
+    }
+
+    return { icon, label, to: url, type, active, isAction }
+  }
+
+  protected buildSubmenu(menuBuilder: typeof AbstractMenuBuilder) {
+    // @ts-ignore
+    const builder = new menuBuilder(this.runtimeConfig, this.ability, this.organizationProfile, this.accessProfile)
+    return builder.build()
+
+  }
+}
+export default AbstractMenuBuilder

+ 56 - 0
services/menuBuilder/accessMenuBuilder.ts

@@ -0,0 +1,56 @@
+import {$organizationProfile} from '~/services/profile/organizationProfile'
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Répertoire
+ */
+export default class AccessMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Access'
+  }
+
+  /**
+   * Construit le menu Répertoire, ou null si aucune page accessible
+   */
+  build (): MenuGroup | MenuItem | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'accesses_page')) {
+      const organization = $organizationProfile()
+      const to = organization.isSchool() ? '/students/list/' : '/adherent/list/'
+      children.push(this.createItem('person', {name: 'fas fa-user'}, to, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'student_registration_page')) {
+      children.push(this.createItem('family_view', {name: 'fas fa-users'}, '/student_registration/new', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'education_student_next_year_page')) {
+      children.push(this.createItem('education_student_next_year', {name: 'fas fa-list-alt'}, '/education_student_next_year/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'commissions_page')) {
+      children.push(this.createItem('commissions', {name: 'fas fa-street-view'}, '/commissions/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'network_children_page')) {
+      children.push(this.createItem('network', {name: 'fas fa-sitemap'}, 'networks/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'network_parents_page')) {
+      children.push(this.createItem('my_network', {name: 'fas fa-sitemap'}, '/network_artist_schools/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('address_book', {name: 'fas fa-address-book'}, children)
+    } else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 88 - 0
services/menuBuilder/accountMenuBuilder.ts

@@ -0,0 +1,88 @@
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from '~/types/menus'
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+
+/**
+ * Menu Mon compte
+ */
+export default class AccountMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Account'
+  }
+
+  /**
+   * Construit le menu Header Configuration ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'my_schedule_page')) {
+      children.push(this.createItem('my_schedule_page', undefined, '/my_calendar', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'attendance_bookings_page')) {
+      children.push(this.createItem('attendance_bookings_menu', undefined, '/own_attendance', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_attendance_page')) {
+      children.push(this.createItem('my_attendance', undefined, '/my_attendances/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_invitation_page')) {
+      children.push(this.createItem('my_invitation', undefined, '/my_invitations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_students_page')) {
+      children.push(this.createItem('my_students', undefined, '/my_students/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_students_education_students_page')) {
+      children.push(this.createItem('my_students_education_students', undefined, '/my_students_education_students/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'criteria_notations_page') || this.ability.can('display', 'criteria_notations_page_from_account_menu')) {
+      children.push(this.createItem('criteria_notations', undefined, '/criteria_notations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_education_students_page')) {
+      children.push(this.createItem('my_education_students', undefined, `/main/my_profile/${this.accessProfile.id}/dashboard/my_education_students/list/`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'send_an_email_page')) {
+      children.push(this.createItem('send_an_email', undefined, `/list/create/emails`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_documents_page')) {
+      children.push(this.createItem('my_documents', undefined, `/main/my_profile/${this.accessProfile.id}/dashboard/show/my_access_file`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'my_profile_page')) {
+      children.push(this.createItem('my_profile', undefined, `/main/my_profile/${this.accessProfile.id}/dashboard`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'adherent_list_page')) {
+      children.push(this.createItem('adherent_list', undefined, `/adherent_contacts/list/`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'subscription_page')) {
+      children.push(this.createItem('my_subscription', undefined, `/subscription`))
+    }
+
+    if (this.ability.can('display', 'my_bills_page')) {
+      children.push(this.createItem('my_bills', undefined, `/main/my_profile/${this.accessProfile.id}/dashboard/show/my_bills`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'cmf_licence_person_page')) {
+      children.push(this.createItem('print_my_licence', undefined, `/licence_cmf/user`, MENU_LINK_TYPE.V1))
+    }
+
+    children.push(this.createItem('logout', undefined, `/logout`, MENU_LINK_TYPE.V1, true))
+
+    const icon = {
+      avatarId: this.accessProfile.avatarId,
+      avatarByDefault: this.accessProfile.gender == 'MISTER' ? 'men-1.png' : 'women-1.png'
+    }
+
+    return this.createGroup('my_account', icon, children)
+  }
+}

+ 58 - 0
services/menuBuilder/admin2iosMenuBuilder.ts

@@ -0,0 +1,58 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Admin 2IOS
+ */
+export default class Admin2iosMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Admin2ios'
+  }
+
+  /**
+   * Construit le menu Administration 2ios ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'all_accesses_page')) {
+      children.push(this.createItem('all_accesses', {name: 'fas fa-users'}, '/all_accesses/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'all_organizations_page')) {
+      children.push(this.createItem('all_organizations', {name: 'fas fa-building'}, '/organization_params/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'tips_page')) {
+      children.push(this.createItem('tips', {name: 'fas fa-info-circle'}, '/tips/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'dgv_page')) {
+      children.push(this.createItem('dgv', {name: 'fas fa-house-damage'}, '/admin2ios/dgv', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'cmf_cotisation_page')) {
+      children.push(this.createItem('cmf_cotisation', {name: 'fas fa-info-circle'}, '/admin2ios/cotisationcmf', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'right_page')) {
+      children.push(this.createItem('right_menu', {name: 'fas fa-balance-scale-right'}, '/admin2ios/right', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'tree_page')) {
+      children.push(this.createItem('tree_menu', {name: 'fas fa-sitemap'}, '/admin2ios/tree', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('admin2ios', {name: 'fas fa-sitemap'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 38 - 0
services/menuBuilder/agendaMenuBuilder.ts

@@ -0,0 +1,38 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu agenda
+ */
+export default class AgendaMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Agenda'
+  }
+
+  /**
+   * Construit le menu Agenda ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'agenda_page')) {
+      children.push(this.createItem('schedule', {name: 'fas fa-calendar-alt'}, '/calendar', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'attendance_page')) {
+      children.push(this.createItem('attendances', {name: 'fas fa-calendar-check'}, '/attendances/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      this.createGroup('schedule', {name: 'fas fa-calendar-alt'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 62 - 0
services/menuBuilder/billingMenuBuilder.ts

@@ -0,0 +1,62 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Facturation
+ */
+export default class BillingMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Billing'
+  }
+
+  /**
+   * Construit le menu Facturation ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'billing_product_page')) {
+      children.push(this.createItem('billing_product', {name: 'fas fa-cube'}, '/intangibles/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'billing_products_by_student_page')) {
+      children.push(this.createItem('billing_products_by_student', {name: 'fas fa-cubes'}, '/access_intangibles/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'billing_edition_page')) {
+      children.push(this.createItem('billing_edition', {name: 'fas fa-copy'}, '/billing_edition', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'billing_accounting_page')) {
+      children.push(this.createItem('billing_accounting', {name: 'fas fa-file-alt'}, '/bill_accountings/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'billing_payment_list_page')) {
+      children.push(this.createItem('billing_payment_list', {name: 'fas fa-credit-card'}, '/bill_payments_list/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'pes_page')) {
+      children.push(this.createItem('pes_export', {name: 'fas fa-align-justify'}, '/pes/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'berger_levrault_page')) {
+      children.push(this.createItem('berger_levrault_export', {name: 'fas fa-align-justify'}, '/berger_levraults/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'jvs_page')) {
+      children.push(this.createItem('jvs_export', {name: 'fas fa-align-justify'}, '/jvs/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('billing', {name: 'fas fa-euro-sign'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 42 - 0
services/menuBuilder/communicationMenuBuilder.ts

@@ -0,0 +1,42 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Communication
+ */
+export default class CommunicationMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Communication'
+  }
+
+  /**
+   * Construit le menu Communication ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'inbox_page')) {
+      children.push(this.createItem('inbox', {name: 'fas fa-inbox'}, '/messages/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'message_send_page')) {
+      children.push(this.createItem('message_send', {name: 'fas fa-paper-plane'}, '/messagessends/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'message_templates_page')) {
+      children.push(this.createItem('message_templates', {name: 'fas fa-edit'}, '/templates/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('communication', {name: 'fas fa-comments'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 82 - 0
services/menuBuilder/configurationMenuBuilder.ts

@@ -0,0 +1,82 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Classe pour la construction du Menu Paramètres
+ */
+export default class ConfigurationMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Configuration'
+  }
+
+  /**
+   * Construit le menu Header Configuration ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'organization_page')) {
+      children.push(this.createItem('organization_page', undefined, `/organization`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'cmf_licence_page')) {
+      children.push(this.createItem('cmf_licence_generate', undefined, '/cmf_licence/organization', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'parameters_page')) {
+      children.push(this.createItem('parameters', undefined,`/parameters`, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'place_page')) {
+      children.push(this.createItem('place', undefined, '/places/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'education_page')) {
+      children.push(this.createItem('education', undefined, '/educations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'tag_page')) {
+      children.push(this.createItem('tag', undefined, '/taggs/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'activities_page')) {
+      children.push(this.createItem('activities', undefined, '/activities/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'template_systems_page')) {
+      children.push(this.createItem('template_systems', undefined,'/template_systems/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'billing_settings_page')) {
+      children.push(this.createItem('billing_settings', undefined, '/main/edit/billing_settings/' + this.organizationProfile.id, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'online_registration_settings_page')) {
+      children.push(this.createItem('online_registration_settings', undefined, '/main/edit/online_registration_settings/' + this.organizationProfile.id, MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'transition_next_year_page')) {
+      children.push(this.createItem('transition_next_year', undefined, '/transition_next_year', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'course_duplication_page')) {
+      children.push(this.createItem('course_duplication', undefined, '/duplicate_courses', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'import_page')) {
+      children.push(this.createItem('import', undefined, '/import/all', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('configuration', {name: 'fas fa-cogs'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 98 - 0
services/menuBuilder/cotisationsMenuBuilder.ts

@@ -0,0 +1,98 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MenuItems, MENU_LINK_TYPE, MenuGroup, MenuItem} from "~/types/menus";
+
+/**
+ * Menu Cotisation (CMF)
+ */
+export default class CotisationsMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Cotisation'
+  }
+
+  /**
+   * Construit le menu Cotisations ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'rate_cotisation_page')) {
+      children.push(this.createItem('rate_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/rate', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'parameters_cotisation_page')) {
+      children.push(this.createItem('parameters_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/parameter', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'send_cotisation_page')) {
+      children.push(this.createItem('send_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/send', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'state_cotisation_page')) {
+      children.push(this.createItem('state_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/state', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'pay_cotisation_page')) {
+      children.push(this.createItem('pay_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/pay', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'check_cotisation_page')) {
+      children.push(this.createItem('check_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/check', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'ledger_cotisation_page')) {
+      children.push(this.createItem('ledger_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/ledger', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'magazine_cotisation_page')) {
+      children.push(this.createItem('magazine_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/magazine', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'ventilated_cotisation_page')) {
+      children.push(this.createItem('ventilated_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/ventilated', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'pay_erase_cotisation_page')) {
+      children.push(this.createItem('pay_erase_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/payerase', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'resume_cotisation_page')) {
+      children.push(this.createItem('resume_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/resume', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'history_cotisation_page')) {
+      children.push(this.createItem('history_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/history', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'call_cotisation_page')) {
+      children.push(this.createItem('call_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/call', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'history_structure_cotisation_page')) {
+      children.push(this.createItem('history_structure_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/historystructure', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'insurance_cotisation_page')) {
+      children.push(this.createItem('insurance_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/insurance', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'resume_all_cotisation_page')) {
+      children.push(this.createItem('resume_all_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/resumeall', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'resume_pay_cotisation_page')) {
+      children.push(this.createItem('resume_pay_cotisation', {name: 'fas fa-euro-sign'}, '/cotisation/resumepay', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('cotisations', {name: 'fas fa-money-bill'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 23 - 0
services/menuBuilder/donorsMenuBuilder.ts

@@ -0,0 +1,23 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuItem} from "~/types/menus";
+
+/**
+ * Menu Donneurs
+ */
+export default class DonorsMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Donors'
+  }
+
+  /**
+   * Construit le menu Partenariat et Dons, ou null si aucune page accessible
+   */
+  build (): MenuItem | null {
+    if (this.ability.can('display', 'donors_page')) {
+      return this.createItem('donors', {name: 'fas fa-handshake'}, '/donors/list/', MENU_LINK_TYPE.V1)
+    }
+
+    return null
+  }
+}

+ 59 - 0
services/menuBuilder/educationalMenuBuilder.ts

@@ -0,0 +1,59 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Suivi pédagogique
+ */
+export default class EducationalMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Educational'
+  }
+
+  /**
+   * Construit le menu Suivi pédagogique ou null si aucune page accessible
+   */
+  build (): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'criteria_notations_page')) {
+      children.push(this.createItem('criteria_notations', {name: 'fas fa-bars'}, '/criteria_notations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'education_notation_config_page')) {
+      children.push(this.createItem('education_notation_configs', {name: 'fas fa-bars'},  '/education_notation_configs/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'seizure_period_page')) {
+      children.push(this.createItem('seizure_period', {name: 'fas fa-calendar-alt'}, '/education_teachers/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'test_seizure_page')) {
+      children.push(this.createItem('test_seizure', {name: 'fas fa-pencil-alt'}, '/education_input/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'test_validation_page')) {
+      children.push(this.createItem('test_validation', {name: 'fas fa-check'}, '/education_notations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'examen_results_page')) {
+      children.push(this.createItem('examen_results', {name: 'fas fa-graduation-cap'}, '/examen_convocations/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'education_by_student_validation_page')) {
+      children.push(this.createItem('education_by_student_validation', {name: 'fas fa-check-square'}, '/education_by_student/list/', MENU_LINK_TYPE.V1))
+    }
+
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('education_state', {name: 'fas fa-graduation-cap'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 22 - 0
services/menuBuilder/equipmentMenuBuilder.ts

@@ -0,0 +1,22 @@
+import {MENU_LINK_TYPE, MenuItem} from "~/types/menus";
+import AbstractMenuBuilder from "~/services/menuBuilder/abstractMenuBuilder";
+
+/**
+ * Menu Matériel
+ */
+export default class EquipmentMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Equipment'
+  }
+
+  /**
+   * Construit le menu Equipement ou null si aucune page accessible
+   */
+  build (): MenuItem | null  {
+    if (this.ability.can('display', 'equipment_page')) {
+      return this.createItem('equipment', {name: 'fas fa-cube'}, '/equipment/list/', MENU_LINK_TYPE.V1)
+    }
+    return null
+  }
+}

+ 54 - 0
services/menuBuilder/mainMenuBuilder.ts

@@ -0,0 +1,54 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+import AccessMenuBuilder from "~/services/menuBuilder/accessMenuBuilder";
+import AgendaMenuBuilder from "~/services/menuBuilder/agendaMenuBuilder";
+import EquipmentMenuBuilder from "~/services/menuBuilder/equipmentMenuBuilder";
+import EducationalMenuBuilder from "~/services/menuBuilder/educationalMenuBuilder";
+import BillingMenuBuilder from "~/services/menuBuilder/billingMenuBuilder";
+import CommunicationMenuBuilder from "~/services/menuBuilder/communicationMenuBuilder";
+import MedalsMenuBuilder from "~/services/menuBuilder/medalsMenuBuilder";
+import DonorsMenuBuilder from "~/services/menuBuilder/donorsMenuBuilder";
+import WebsiteAdminMenuBuilder from "~/services/menuBuilder/websiteAdminMenuBuilder";
+import CotisationsMenuBuilder from "~/services/menuBuilder/cotisationsMenuBuilder";
+import StatsMenuBuilder from "~/services/menuBuilder/statsMenuBuilder";
+import Admin2iosMenuBuilder from "~/services/menuBuilder/admin2iosMenuBuilder";
+
+/**
+ * Menu principal (ou menu lateral)
+ */
+export default class MainMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Main'
+  }
+
+  /**
+   * Construit le menu principal, ou null si aucune page accessible
+   */
+  build (): MenuGroup | MenuItem | null {
+    const children: MenuItems = [
+      this.buildSubmenu(AccessMenuBuilder),
+      this.buildSubmenu(AgendaMenuBuilder),
+      this.buildSubmenu(EquipmentMenuBuilder),
+      this.buildSubmenu(EducationalMenuBuilder),
+      this.buildSubmenu(BillingMenuBuilder),
+      this.buildSubmenu(CommunicationMenuBuilder),
+      this.buildSubmenu(DonorsMenuBuilder),
+      this.buildSubmenu(MedalsMenuBuilder),
+      this.buildSubmenu(WebsiteAdminMenuBuilder),
+      this.buildSubmenu(CotisationsMenuBuilder),
+      this.buildSubmenu(StatsMenuBuilder),
+      this.buildSubmenu(Admin2iosMenuBuilder)
+    ].filter((m: MenuItem | MenuGroup | null) => m !== null)
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('address_book', {name: 'fas fa-address-book'}, children)
+    } else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 22 - 0
services/menuBuilder/medalsMenuBuilder.ts

@@ -0,0 +1,22 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuItem} from "~/types/menus";
+
+/**
+ * Menu Médailles
+ */
+export default class MedalsMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Medals'
+  }
+
+  /**
+   * Construit le menu Médailles et Dons, ou null si aucune page accessible
+   */
+  build (): MenuItem | null {
+    if (this.ability.can('display', 'medals_page')) {
+      return this.createItem('medals', {name: 'fas fa-trophy'}, '/medals/list/', MENU_LINK_TYPE.V1)
+    }
+    return null
+  }
+}

+ 30 - 0
services/menuBuilder/myAccessesMenuBuilder.ts

@@ -0,0 +1,30 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItems} from "~/types/menus";
+import {useEach} from "#imports";
+
+/**
+ * Menu Mon Profil
+ */
+export default class MyAccessesMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'MyAccesses'
+  }
+
+  /**
+   * Construit le menu Header Multi compte, ou null si aucune page accessible
+   */
+  build(): MenuGroup | null {
+    const children: MenuItems = []
+
+    useEach(this.accessProfile.multiAccesses, (access) => {
+      children.push(this.createItem(access.name as string, undefined, '/switch/' + access.id, MENU_LINK_TYPE.V1))
+    })
+
+    if (children.length > 0) {
+      return this.createGroup('multiAccesses', {name: 'fas fa-building'}, children)
+    }
+
+    return null
+  }
+}

+ 44 - 0
services/menuBuilder/myFamilyMenuBuilder.ts

@@ -0,0 +1,44 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItems} from "~/types/menus";
+import {useEach} from "#imports";
+
+/**
+ * Menu Famille
+ */
+export default class MyFamilyMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'MyFamily'
+  }
+
+  /**
+   * Construit le menu Header Changement d'utilisateur ou null si aucune page accessible
+   */
+  build(): MenuGroup | null {
+    const children: MenuItems = []
+
+    // Si Access des membres de la familles (enfants)
+    useEach(this.accessProfile.familyAccesses, (access) => {
+      const url = `/switch_user/${this.organizationProfile.id}/${this.accessProfile.id}/${access.id}`
+      const icon = {
+        avatarId: access.avatarId,
+        avatarByDefault: access.gender == 'MISTER' ? 'men-1.png' : 'women-1.png'
+      }
+
+      children.push(this.createItem(`${access.givenName} ${access.name}`, icon, url, MENU_LINK_TYPE.V1))
+    })
+
+    // Si on est en compte switch, on doit pouvoir retourner au compte d'origine
+    const originalAccess = this.accessProfile.originalAccess
+    if (originalAccess && !originalAccess.isSuperAdminAccess) {
+      const url = `/switch_user/${originalAccess.organization.id}/${originalAccess.id}/exit`
+      children.push(this.createItem(`${originalAccess.givenName} ${originalAccess.name}`, undefined, url, MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 0) {
+      this.createGroup('familyAccesses', {name: 'fas fa-users'}, children)
+    }
+
+    return null
+  }
+}

+ 44 - 0
services/menuBuilder/parametersMenuBuilder.ts

@@ -0,0 +1,44 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Paramètres
+ */
+export default class ParametersMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Parameters'
+  }
+
+  /**
+   * Construit le menu Header Configuration, ou null si aucune page accessible
+   */
+  build(): MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'parameters_page')) {
+      children.push(this.createItem('general_params', {name: 'fas fa-cogs'},`/parameters`, MENU_LINK_TYPE.V1))
+    }
+    if (this.ability.can('display', 'parameters_communication_page')) {
+      children.push(this.createItem('communication_params', {name: 'fas fa-comments'},`/parameters/communication`, MENU_LINK_TYPE.V1))
+    }
+    if (this.ability.can('display', 'parameters_student_page')) {
+      children.push(this.createItem('students_params', {name: 'fas fa-users'},`/parameters/student`, MENU_LINK_TYPE.V1))
+    }
+    if (this.ability.can('display', 'parameters_education_page')) {
+      children.push(this.createItem('education_params', {name: 'fas fa-graduation-cap'},`/parameters/education`, MENU_LINK_TYPE.V1))
+    }
+    if (this.ability.can('display', 'parameters_bills_page')) {
+      children.push(this.createItem('bills_params', {name: 'fas fa-euro-sign'},`/parameters/billing`, MENU_LINK_TYPE.V1))
+    }
+    if (this.ability.can('display', 'parameters_secure_page')) {
+      children.push(this.createItem('secure_params', {name: 'fas fa-lock'},`/parameters/secure`, MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 0) {
+      return this.createGroup('parameters', undefined, children)
+    }
+
+    return null
+  }
+}

+ 46 - 0
services/menuBuilder/statsMenuBuilder.ts

@@ -0,0 +1,46 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItem, MenuItems} from "~/types/menus";
+
+/**
+ * Menu Statistiques
+ */
+export default class StatsMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'Stats'
+  }
+
+  /**
+   * Construit le menu Statistique et Dons ou null si aucune page accessible
+   */
+  build(): MenuItem | MenuGroup | null {
+    const children: MenuItems = []
+
+    if (this.ability.can('display', 'report_activity_page')) {
+      children.push(this.createItem('report_activity', {name: 'fas fa-chart-bar'}, '/report_activity', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'education_quotas_page')) {
+      children.push(this.createItem('educations_quotas_by_education', {name: 'fas fa-user-circle'},  '/educations_quotas_by_education_year/list/', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'fede_stats_page')) {
+      children.push(this.createItem('fede_stats', {name: 'fas fa-chart-bar'}, '/statistic/membersfedeonly', MENU_LINK_TYPE.V1))
+    }
+
+    if (this.ability.can('display', 'structure_stats_page')) {
+      children.push(this.createItem('structure_stats', {name: 'fas fa-chart-bar'}, '/statistic/membersfedeassos', MENU_LINK_TYPE.V1))
+    }
+
+    if (children.length > 1) {
+      // Plusieurs éléments, on retourne un groupe
+      return this.createGroup('stats', {name: 'fas fa-chart-bar'}, children)
+    }
+    else if (children.length === 1) {
+      // Un seul élément, on retourne cet élément seul
+      return children[0]
+    }
+
+    return null
+  }
+}

+ 24 - 0
services/menuBuilder/websiteAdminMenuBuilder.ts

@@ -0,0 +1,24 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuItem} from "~/types/menus";
+
+/**
+ * Menu Site internet
+ */
+export default class WebsiteAdminMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'WebsiteAdmin'
+  }
+
+  /**
+   * Construit le menu Site internet, ou null si aucune page accessible
+   */
+  build(): MenuItem | null {
+    if (this.organizationProfile.website && this.accessProfile.isAdminAccess) {
+      const url = this.organizationProfile.website + '/typo3'
+      return this.createItem('advanced_modification', {name: 'fas fa-globe-americas'}, url, MENU_LINK_TYPE.V1)
+    }
+
+    return null
+  }
+}

+ 35 - 0
services/menuBuilder/websiteListMenuBuilder.ts

@@ -0,0 +1,35 @@
+import AbstractMenuBuilder from '~/services/menuBuilder/abstractMenuBuilder'
+import {MENU_LINK_TYPE, MenuGroup, MenuItems} from "~/types/menus";
+import {useEach} from "#imports";
+
+/**
+ * Menu : Liste des sites internet de la structure et de ses structures parentes
+ */
+export default class WebsiteListMenuBuilder extends AbstractMenuBuilder {
+
+  name() {
+    return 'WebsiteList'
+  }
+
+  /**
+   * Construit le menu Site internet, ou null si aucune page accessible
+   */
+  build(): MenuGroup | null {
+    const children: MenuItems = []
+
+    const url = this.organizationProfile.website + '/typo3'
+    children.push(this.createItem(this.organizationProfile.name as string, undefined, url, MENU_LINK_TYPE.V1))
+
+    useEach(this.organizationProfile.parents, (parent:any) => {
+      if(parent.id != process.env.OPENTALENT_MANAGER_ID){
+        children.push(this.createItem(parent.name, undefined, parent.website, MENU_LINK_TYPE.V1))
+      }
+    })
+
+    if (children.length > 0) {
+      return this.createGroup('website', {name: 'fas fa-globe-americas'},  children)
+    }
+
+    return null
+  }
+}

+ 1 - 1
services/profile/accessProfile.ts

@@ -99,7 +99,7 @@ export class AccessProfile {
       'member': this.accessProfile.isMember,
       'other': this.accessProfile.isOther,
       'guardian': this.accessProfile.isGuardian,
-      'payor': this.accessProfile.isPayor,
+      'payor': this.accessProfile.isPayer,
     }
 
     if(profile in factory)

+ 2 - 2
services/profile/organizationProfile.ts

@@ -1,5 +1,5 @@
 import { organizationState } from '~/types/interfaces'
-import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
 import {AnyJson} from "~/types/data";
 /**
  * L'OrganizationProfile permet de manipuler l'OrganizationState auquel
@@ -14,7 +14,7 @@ class OrganizationProfile {
    * @constructor
    */
   constructor () {
-    this.organizationProfile = useProfileOrganizationStore()
+    this.organizationProfile = useOrganizationProfileStore()
   }
 
   /**

+ 2 - 2
services/rights/abilitiesUtils.ts

@@ -3,7 +3,7 @@ import {$organizationProfile} from '~/services/profile/organizationProfile'
 import {$roleUtils} from '~/services/rights/roleUtils'
 import {AbilitiesType} from '~/types/interfaces'
 import {useAccessProfileStore} from "~/store/profile/access";
-import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
 import YamlDenormalizer from "~/services/data/serializer/denormalizer/yamlDenormalizer";
 import {MongoAbility} from "@casl/ability/dist/types/Ability";
 import {AnyJson} from "~/types/data";
@@ -55,7 +55,7 @@ class AbilitiesUtils {
      */
     setAbilities() {
         const accessProfileStore = useAccessProfileStore()
-        const organizationProfileStore = useProfileOrganizationStore()
+        const organizationProfileStore = useOrganizationProfileStore()
 
         // Nécessaire pour que l'update des habilités soit correcte après la phase SSR
         this.$ability.update(accessProfileStore.abilities)

+ 6 - 9
store/profile/access.ts

@@ -1,7 +1,7 @@
 import { defineStore } from 'pinia'
 import {$roleUtils} from "~/services/rights/roleUtils";
 import {accessState, baseAccessState, baseOrganizationState, OrignalAccessState} from "~/types/interfaces";
-import {useProfileOrganizationStore} from "~/store/profile/organization";
+import {useOrganizationProfileStore} from "~/store/profile/organization";
 
 import {MyProfile} from "~/models/Access/MyProfile";
 import {useRepo} from "pinia-orm";
@@ -32,11 +32,8 @@ export const useAccessProfileStore = defineStore('accessProfile', {
       isMember: false,
       isOther: false,
       isGuardian: false,
-      isPayor: false,
-      hasLateralMenu: false,
-      hasConfigurationMenu: false,
-      hasAccessesMenu: false,
-      hasFamilyMenu: false,
+      isPayer: false,
+      hasMenu: [],
       multiAccesses: [],
       familyAccesses: [],
       originalAccess: null
@@ -64,7 +61,7 @@ export const useAccessProfileStore = defineStore('accessProfile', {
       this.isMember = $roleUtils.isA('MEMBER', roles)
       this.isOther = $roleUtils.isA('OTHER', roles)
       this.isGuardian = profile.isGuardian
-      this.isPayor = profile.isPayor
+      this.isPayer = profile.isPayor
       this.roles = $roleUtils.filterFunctionRoles(roles)
 
       // Time to add the original Access (switch User case)
@@ -77,7 +74,7 @@ export const useAccessProfileStore = defineStore('accessProfile', {
       this.setFamilyAccesses(profile.familyAccesses)
 
       // Time to set Organization Profile
-      const organizationProfileStore = useProfileOrganizationStore()
+      const organizationProfileStore = useOrganizationProfileStore()
       organizationProfileStore.setProfile(profile.organization)
 
       useRepo(MyProfile).save(profile)
@@ -89,7 +86,7 @@ export const useAccessProfileStore = defineStore('accessProfile', {
       this.avatarId = profile.avatarId
       this.activityYear = profile.activityYear
 
-      const organizationProfileStore = useProfileOrganizationStore()
+      const organizationProfileStore = useOrganizationProfileStore()
       organizationProfileStore.refreshProfile(profile.organization)
     },
     setMultiAccesses(organizations: any) {

+ 1 - 1
store/profile/organization.ts

@@ -1,7 +1,7 @@
 import { baseOrganizationState, organizationState } from '~/types/interfaces'
 import { defineStore } from "pinia";
 
-export const useProfileOrganizationStore = defineStore('profileOrganization', {
+export const useOrganizationProfileStore = defineStore('organizationProfile', {
   state: (): organizationState => {
         return {
           id: null,

+ 1 - 0
tsconfig.json

@@ -20,6 +20,7 @@
       "./types/interfaces.d.ts",
       "./types/enums.d.ts",
       "./types/data.d.ts",
+      "./types/menus.d.ts"
     ]
   }
 }

+ 3 - 34
types/interfaces.d.ts

@@ -1,4 +1,3 @@
-import { Model, Query } from 'pinia-orm'
 import { Ability } from '@casl/ability'
 import { Store } from 'pinia'
 import DataPersister from '~/services/data/dataPersister'
@@ -8,12 +7,11 @@ import {
   ABILITIES,
   FORM_FUNCTION,
   GENDER,
-  METADATA_TYPE,
-  QUERY_TYPE,
   TYPE_ALERT,
 } from '~/types/enums'
 import ApiResource from "~/models/ApiResource";
 import {AnyJson} from "~/types/data";
+import {Record} from "immutable";
 
 /**
  * Upgrade du @nuxt/types pour TypeScript
@@ -33,32 +31,6 @@ declare module '@vuex-orm/core' {
   }
 }
 
-interface IconItem {
-  name?: string
-  avatarId?: number | null
-  avatarByDefault?: string
-}
-
-interface ItemMenu {
-  title: string
-  icon?: IconItem
-  avatar?: number
-  to?: string
-  // eslint-disable-next-line no-use-before-define
-  children?: ItemsMenu
-  isExternalLink?: boolean
-  // eslint-disable-next-line no-use-before-define
-  actions?: ItemsMenu
-  active?: boolean
-}
-
-type ItemsMenu = Array<ItemMenu>
-
-interface Menu {
-  getMenu: () => ItemMenu | null
-  getHeaderMenu: () => ItemMenu | null
-}
-
 interface AbilitiesType {
   action: ABILITIES
 
@@ -141,11 +113,8 @@ interface accessState extends baseAccessState {
   isMember: boolean | null
   isOther: boolean | null
   isGuardian: boolean | null
-  isPayor: boolean | null
-  hasLateralMenu: boolean | null
-  hasConfigurationMenu: boolean | null
-  hasAccessesMenu: boolean | null
-  hasFamilyMenu: boolean | null
+  isPayer: boolean | null
+  hasMenu: Record<string, boolean>
   multiAccesses: Array<baseOrganizationState>
   familyAccesses: Array<baseAccessState>
   originalAccess: OrignalAccessState | null

+ 36 - 0
types/menus.d.ts

@@ -0,0 +1,36 @@
+interface IconItem {
+    name?: string
+    avatarId?: number | null
+    avatarByDefault?: string
+}
+
+export const enum MENU_LINK_TYPE {
+    INTERNAL = 1,
+    V1 = 2,
+    EXTERNAL = 3,
+}
+
+interface MenuItem {
+    label: string
+    icon?: IconItem
+    to?: string
+    type: MENU_LINK_TYPE
+    active: boolean
+    isAction: boolean
+    avatar?: number
+}
+
+interface MenuGroup {
+    label: string
+    icon?: IconItem
+    children?: MenuItems
+    active: boolean
+}
+
+type MenuItems = Array<MenuItem | MenuGroup>
+
+interface MenuBuilder {
+    build: () => MenuItem | MenuGroup | null
+}
+
+