abstractMenuBuilder.ts 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import type { RuntimeConfig } from '@nuxt/schema'
  2. import type { AnyAbility } from '@casl/ability'
  3. import type { Router } from 'vue-router'
  4. import type {
  5. IconItem,
  6. MenuBuilder,
  7. MenuGroup,
  8. MenuItem,
  9. MenuItems,
  10. } from '~/types/layout'
  11. import { MENU_LINK_TYPE } from '~/types/enum/layout'
  12. import UrlUtils from '~/services/utils/urlUtils'
  13. import type { AccessProfile, organizationState } from '~/types/interfaces'
  14. import { LINK_TARGET } from '~/types/enum/enums'
  15. /**
  16. * Classe de base des menus et sous-menus.
  17. *
  18. * La méthode principale est la méthode build
  19. */
  20. abstract class AbstractMenuBuilder implements MenuBuilder {
  21. protected runtimeConfig: RuntimeConfig
  22. protected ability: AnyAbility
  23. protected organizationProfile: organizationState
  24. protected accessProfile: AccessProfile
  25. protected router: Router
  26. /**
  27. * Nom court désignant le menu que construit ce builder
  28. */
  29. static readonly menuName: string
  30. constructor(
  31. runtimeConfig: RuntimeConfig,
  32. ability: AnyAbility,
  33. organizationProfile: organizationState,
  34. accessProfile: AccessProfile,
  35. router: Router,
  36. ) {
  37. this.runtimeConfig = runtimeConfig
  38. this.ability = ability
  39. this.organizationProfile = organizationProfile
  40. this.accessProfile = accessProfile
  41. this.router = router
  42. }
  43. /**
  44. * Permet un accès non statique à la variable menuName
  45. */
  46. public getMenuName(): string {
  47. return Object.getPrototypeOf(this).constructor.menuName
  48. }
  49. /**
  50. * Construit et retourne un menu ou sous-menu selon le profil de l'utilisateur, le profil de son organisation
  51. * et les droits de l'utilisateur.
  52. *
  53. * Si le menu comporte plusieurs éléments, retourne une instance de MenuGroup
  54. * Si le menu ne comporte qu'un seul élément, retourne une instance de MenuItem
  55. * Si le menu ne comporte aucun élément, retourne null.
  56. */
  57. abstract build(): MenuItem | MenuGroup | null
  58. /**
  59. * Construit et retourne un MenuGroup
  60. *
  61. * @param label
  62. * @param icon
  63. * @param {Array<MenuItem>} children Tableau d'ItemMenu représentant les sous menu du menu principal
  64. * @param actions
  65. */
  66. protected createGroup(
  67. label: string,
  68. icon?: IconItem,
  69. children: MenuItems = [],
  70. actions: Array<MenuItem> = [],
  71. ): MenuGroup {
  72. return { label, icon, children, actions }
  73. }
  74. /**
  75. * Construit et retourne un MenuItem
  76. *
  77. * @param {IconItem} icon
  78. * @param {string} label Titre qui sera traduit
  79. * @param to
  80. * @param type
  81. * @param newTab
  82. * @param endOfSubsection
  83. * @return {MenuItem}
  84. */
  85. protected createItem(
  86. label: string,
  87. icon?: IconItem,
  88. to: string = '',
  89. type: MENU_LINK_TYPE = MENU_LINK_TYPE.INTERNAL,
  90. newTab: boolean = false,
  91. endOfSubsection: boolean = false,
  92. ): MenuItem {
  93. let url: string
  94. if (type === MENU_LINK_TYPE.INTERNAL) {
  95. console.warn(
  96. "'createItem()' should not be used for internal links, use 'makeChildren()'",
  97. )
  98. }
  99. switch (type) {
  100. case MENU_LINK_TYPE.V1:
  101. // eslint-disable-next-line no-case-declarations
  102. const v1BaseURL =
  103. this.runtimeConfig.baseUrlAdminLegacy ||
  104. this.runtimeConfig.public.baseUrlAdminLegacy
  105. url = UrlUtils.join(v1BaseURL, '#', to)
  106. break
  107. case MENU_LINK_TYPE.EXTERNAL:
  108. url = UrlUtils.prependHttps(to)
  109. break
  110. default:
  111. url = to
  112. }
  113. return {
  114. icon,
  115. label,
  116. to: url,
  117. type,
  118. active: false,
  119. target: newTab ? LINK_TARGET.BLANK : LINK_TARGET.SELF,
  120. endOfSubsection,
  121. }
  122. }
  123. protected buildSubmenu(menuBuilder: typeof AbstractMenuBuilder) {
  124. // @ts-expect-error this method has to be called from non-abstract subclasses
  125. const builder = new menuBuilder(
  126. this.runtimeConfig,
  127. this.ability,
  128. this.organizationProfile,
  129. this.accessProfile,
  130. )
  131. return builder.build()
  132. }
  133. /**
  134. * Make a list of MenuItems according to user abilities
  135. *
  136. * @param items
  137. * @protected
  138. */
  139. protected makeChildren(
  140. items: Array<{
  141. pageName: string
  142. icon?: string
  143. endOfSubsection?: boolean
  144. }>,
  145. ): MenuItems {
  146. const children: MenuItems = []
  147. items.forEach((item) => {
  148. const { pageName, icon, endOfSubsection = false } = item
  149. if (this.ability.can('display', pageName)) {
  150. const to = this.router.resolve({ name: pageName })
  151. if (!to) {
  152. throw new Error('unknown page name : ' + pageName)
  153. }
  154. children.push({
  155. icon: icon ? { name: icon } : undefined,
  156. label: pageName,
  157. to: to.href,
  158. type: MENU_LINK_TYPE.INTERNAL,
  159. active: false,
  160. endOfSubsection,
  161. })
  162. }
  163. })
  164. return children
  165. }
  166. }
  167. export default AbstractMenuBuilder