import type { RuntimeConfig } from '@nuxt/schema' import type { AnyAbility } from '@casl/ability' import type { Router } from 'vue-router' import type { IconItem, MenuBuilder, MenuGroup, MenuItem, MenuItems, } from '~/types/layout' import { MENU_LINK_TYPE } from '~/types/enum/layout' import UrlUtils from '~/services/utils/urlUtils' import type { AccessProfile, organizationState } from '~/types/interfaces' import { LINK_TARGET } from '~/types/enum/enums' /** * 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: AccessProfile protected router: Router /** * Nom court désignant le menu que construit ce builder */ static readonly menuName: string constructor( runtimeConfig: RuntimeConfig, ability: AnyAbility, organizationProfile: organizationState, accessProfile: AccessProfile, router: Router, ) { this.runtimeConfig = runtimeConfig this.ability = ability this.organizationProfile = organizationProfile this.accessProfile = accessProfile this.router = router } /** * Permet un accès non statique à la variable menuName */ public getMenuName(): string { return Object.getPrototypeOf(this).constructor.menuName } /** * 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} children Tableau d'ItemMenu représentant les sous menu du menu principal * @param actions */ protected createGroup( label: string, icon?: IconItem, children: MenuItems = [], actions: Array = [], ): MenuGroup { return { label, icon, children, actions } } /** * Construit et retourne un MenuItem * * @param {IconItem} icon * @param {string} label Titre qui sera traduit * @param to * @param type * @param newTab * @return {MenuItem} */ protected createItem( label: string, icon?: IconItem, to: string = '', type: MENU_LINK_TYPE = MENU_LINK_TYPE.INTERNAL, newTab: boolean = false, ): MenuItem { let url: string if (type === MENU_LINK_TYPE.INTERNAL) { console.warn( "'createItem()' should not be used for internal links, use 'makeChildren()'", ) } switch (type) { case MENU_LINK_TYPE.V1: // eslint-disable-next-line no-case-declarations const v1BaseURL = this.runtimeConfig.baseUrlAdminLegacy || this.runtimeConfig.public.baseUrlAdminLegacy url = UrlUtils.join(v1BaseURL, '#', to) break case MENU_LINK_TYPE.EXTERNAL: url = UrlUtils.prependHttps(to) break default: url = to } return { icon, label, to: url, type, active: false, target: newTab ? LINK_TARGET.BLANK : LINK_TARGET.SELF, } } protected buildSubmenu(menuBuilder: typeof AbstractMenuBuilder) { // @ts-expect-error this method has to be called from non-abstract subclasses const builder = new menuBuilder( this.runtimeConfig, this.ability, this.organizationProfile, this.accessProfile, ) return builder.build() } /** * Make a list of MenuItems according to user abilities * * @param items * @protected */ protected makeChildren( items: Array<{ pageName: string; icon?: string }>, ): MenuItems { const children: MenuItems = [] items.forEach((item) => { const { pageName, icon } = item if (this.ability.can('display', pageName)) { const to = this.router.resolve({ name: pageName }) if (!to) { throw new Error('unknown page name : ' + pageName) } children.push({ icon: icon ? { name: icon } : undefined, label: pageName, to: to.href, type: MENU_LINK_TYPE.INTERNAL, active: false, }) } }) return children } } export default AbstractMenuBuilder