瀏覽代碼

lint, create Hookable and Datamanager base classes

Olivier Massot 4 年之前
父節點
當前提交
512484cace

+ 1 - 1
components/Layout/SubHeader/DataTimingRange.vue

@@ -65,7 +65,7 @@ export default defineComponent({
       unwatch()
     })
 
-    const updateDateTimeRange = async (dates:Array<string>):Promise<any> => {
+    const updateDateTimeRange = async (dates:Array<string>): Promise<any> => {
       setHistoricalRange(dates)
       markFormAsNotDirty()
       await updateMyProfile()

+ 1 - 1
notes_relecture.md

@@ -11,7 +11,7 @@
 
 ## Use/layout/Menus
 
-* envisageable de factoriser le code des menus dans une classe de base?
+* envisageable de factoriser le code des menus dans baseMenu?
 
 # Divers
 

+ 59 - 0
services/connection/dataManager.ts

@@ -0,0 +1,59 @@
+import { Context } from '@nuxt/types/app'
+import { UrlArgs } from '~/types/interfaces'
+import Connection from '~/services/connection/connection'
+import Hookable from '~/services/hooks/hookable'
+import { HTTP_METHOD, QUERY_TYPE } from '~/types/enums'
+
+/**
+ * Base class for data providers, persisters or deleters
+ */
+class DataManager extends Hookable {
+  protected ctx!: Context
+  protected arguments!: UrlArgs
+  protected defaultArguments: object = {
+    type: QUERY_TYPE.MODEL,
+    showProgress: true
+  }
+
+  /**
+   * Initialise le contexte (la connection en particulier)
+   * @param ctx
+   */
+  public initCtx (ctx: Context) {
+    Connection.initConnector(ctx.$axios)
+    this.ctx = ctx
+  }
+
+  /**
+   * Exécute la requête
+   * @param args
+   */
+  public async invoke (args: UrlArgs): Promise<any> {
+    args = { ...this.defaultArguments, ...args }
+    DataManager.startLoading(args)
+    await this.triggerHooks(args)
+  }
+
+  /**
+   * Signale le début du chargement à nuxt (si showProgress est true)
+   * @param args
+   */
+  private static startLoading (args: UrlArgs) {
+    if (args.showProgress) {
+      const $nuxt = window.$nuxt
+      $nuxt.$loading.start()
+    }
+  }
+
+  /**
+   * Send the request trough the Connection
+   * @param url
+   * @param method
+   * @param args
+   */
+  protected static sendRequest (url: string, method: HTTP_METHOD, args: UrlArgs): Promise<any> {
+    return Connection.invoke(method, url, args)
+  }
+}
+
+export default DataManager

+ 10 - 10
services/connection/constructUrl.ts → services/connection/urlBuilder.ts

@@ -5,10 +5,10 @@ import { repositoryHelper } from '~/services/store/repository'
 
 /**
  * @category Services/connection
- * @class ConstructUrl
+ * @class UrlBuilder
  * Classe permettant de construire une URL pour l'interrogation d'une API externe
  */
-class ConstructUrl {
+class UrlBuilder {
   static ROOT = '/api/'
 
   /**
@@ -19,13 +19,13 @@ class ConstructUrl {
   public static invoke (args: UrlArgs): string {
     switch (args.type) {
       case QUERY_TYPE.DEFAULT:
-        return ConstructUrl.getDefaultUrl(args.url)
+        return UrlBuilder.getDefaultUrl(args.url)
 
       case QUERY_TYPE.ENUM:
-        return ConstructUrl.getEnumUrl(args.enumType)
+        return UrlBuilder.getEnumUrl(args.enumType)
 
       case QUERY_TYPE.MODEL:
-        return ConstructUrl.getModelUrl(args.model, args.rootModel, args.rootId)
+        return UrlBuilder.getModelUrl(args.model, args.rootModel, args.rootId)
 
       default:
         throw new Error('url, model or enum must be defined')
@@ -41,7 +41,7 @@ class ConstructUrl {
     if (typeof url === 'undefined') {
       throw new TypeError('url must be defined')
     }
-    return String(ConstructUrl.ROOT + url).toString()
+    return String(UrlBuilder.ROOT + url).toString()
   }
 
   /**
@@ -53,7 +53,7 @@ class ConstructUrl {
     if (typeof enumType === 'undefined') {
       throw new TypeError('enumType must be defined')
     }
-    return String(ConstructUrl.ROOT + 'enum/' + enumType).toString()
+    return String(UrlBuilder.ROOT + 'enum/' + enumType).toString()
   }
 
   /**
@@ -77,12 +77,12 @@ class ConstructUrl {
         throw new TypeError('Root ID must be defined')
       }
 
-      const rootUrl = ConstructUrl.getModelUrl(rootModel) as string
+      const rootUrl = UrlBuilder.getModelUrl(rootModel) as string
       return String(`${rootUrl}/${rootId}/${entity}`).toString()
     }
 
-    return String(ConstructUrl.ROOT + entity).toString()
+    return String(UrlBuilder.ROOT + entity).toString()
   }
 }
 
-export default ConstructUrl
+export default UrlBuilder

+ 4 - 42
services/dataDeleter/dataDeleter.ts

@@ -1,34 +1,20 @@
-import { Context } from '@nuxt/types/app'
-import { hooks } from '~/services/dataDeleter/hook/_import'
 import { DataDeleterArgs } from '~/types/interfaces'
-import Connection from '~/services/connection/connection'
+import DataManager from '~/services/connection/dataManager'
 import ApiError from '~/services/utils/apiError'
 import { repositoryHelper } from '~/services/store/repository'
 
 /**
  * Le DataDeleter a pour rôle de supprimer des enregistrements via l'API Opentalent
  */
-class DataDeleter {
-  private ctx !: Context
-  private arguments!: DataDeleterArgs
-
-  /**
-   * Initialise le contexte (la connection en particulier)
-   * @param ctx
-   */
-  public initCtx (ctx:Context) {
-    Connection.initConnector(ctx.$axios)
-    this.ctx = ctx
-  }
-
+class DataDeleter extends DataManager {
   /**
    * Exécute la requête
    * @param args
    */
-  public async invoke (args:DataDeleterArgs): Promise<any> {
+  public async invoke (args: DataDeleterArgs): Promise<any> {
     this.arguments = args
     try {
-      await this.preHook()
+      await super.invoke(args)
 
       // const url = ConstructUrl.invoke(this.arguments)
       // const response = await Connection.invoke(HTTP_METHOD.DELETE, url, this.arguments)
@@ -39,30 +25,6 @@ class DataDeleter {
       throw new ApiError(error.response.status, error.response.data.detail)
     }
   }
-
-  /**
-   * Iterate over the available hooks and return
-   * the first one that support the given args
-   */
-  async preHook () {
-    for (const Hook of DataDeleter.sortedHooks()) {
-      if (Hook.support(this.arguments)) {
-        await new Hook().invoke(this.arguments)
-      }
-    }
-  }
-
-  /**
-   * Sort the available hooks by priority
-   * @private
-   */
-  private static sortedHooks (): Iterable<any> {
-    return hooks.sort(function (a, b) {
-      if (a.priority > b.priority) { return 1 }
-      if (a.priority < b.priority) { return -1 }
-      return 0
-    })
-  }
 }
 
 export default DataDeleter

+ 8 - 79
services/dataPersister/dataPersister.ts

@@ -1,54 +1,28 @@
-import { Context } from '@nuxt/types/app'
-import { hooks } from '~/services/dataPersister/hook/_import'
 import { AnyJson, DataPersisterArgs, DataProviderArgs } from '~/types/interfaces'
-import Connection from '~/services/connection/connection'
-import ConstructUrl from '~/services/connection/constructUrl'
-import { DENORMALIZER_TYPE, HTTP_METHOD, QUERY_TYPE } from '~/types/enums'
+import UrlBuilder from '~/services/connection/urlBuilder'
+import { DENORMALIZER_TYPE, HTTP_METHOD } from '~/types/enums'
 import Serializer from '~/services/serializer/serializer'
 import ApiError from '~/services/utils/apiError'
 import DataProvider from '~/services/dataProvider/dataProvider'
+import DataManager from '~/services/connection/dataManager'
 
 /**
  * Le DataPersisteer a pour rôle de mettre à jour les données via de l'API Opentalent
  */
-class DataPersister {
-  private ctx !: Context
-  private readonly defaultArguments: DataPersisterArgs
-
-  constructor () {
-    this.defaultArguments = {
-      type: QUERY_TYPE.MODEL,
-      progress: true
-    }
-  }
-
-  /**
-   * Initialise le contexte (la connection en particulier)
-   * @param ctx
-   */
-  public initCtx (ctx:Context) {
-    Connection.initConnector(ctx.$axios)
-    this.ctx = ctx
-  }
-
+class DataPersister extends DataManager {
   /**
    * Exécute la requête et retourne la réponse désérialisée
    * @param args
    */
-  public async invoke (args:DataPersisterArgs): Promise<any> {
+  public async invoke (args: DataPersisterArgs): Promise<any> {
     try {
-      // complete args with defaults
-      args = { ...this.defaultArguments, ...args }
-
-      this.startLoading(args)
-
-      await this.preHook(args)
+      await super.invoke(args)
 
       args.data = Serializer.normalize(args)
 
-      const url = ConstructUrl.invoke(args)
+      const url = UrlBuilder.invoke(args)
 
-      const response = await this.sendRequest(url, args)
+      const response = await DataPersister.sendRequest(url, args.id ? HTTP_METHOD.PUT : HTTP_METHOD.POST, args)
 
       await this.provideResponse(response, args)
     } catch (error) {
@@ -56,39 +30,6 @@ class DataPersister {
     }
   }
 
-  /**
-   * Signale le début du chargement à nuxt (si progress est true)
-   * @param args
-   */
-  startLoading (args: DataPersisterArgs) {
-    if (args.progress) {
-      const $nuxt = window.$nuxt
-      $nuxt.$loading.start()
-    }
-  }
-
-  /**
-   * Iterate over the available hooks and return
-   * the first one that support the given args
-   * @param args
-   */
-  async preHook (args: DataPersisterArgs) {
-    for (const Hook of DataPersister.sortedHooks()) {
-      if (Hook.support(args)) {
-        await new Hook().invoke(args)
-      }
-    }
-  }
-
-  /**
-   * Send the request trough the Connection
-   * @param url
-   * @param args
-   */
-  sendRequest (url: string, args: DataPersisterArgs): Promise<any> {
-    return Connection.invoke(args.id ? HTTP_METHOD.PUT : HTTP_METHOD.POST, url, args)
-  }
-
   /**
    * ?
    * @param response
@@ -112,18 +53,6 @@ class DataPersister {
 
     return await dataProvider.provide(deserializedResponse, dataProviderArgs)
   }
-
-  /**
-   * Sort the available hooks by priority
-   * @private
-   */
-  private static sortedHooks (): Iterable<any> {
-    return hooks.sort(function (a, b) {
-      if (a.priority > b.priority) { return 1 }
-      if (a.priority < b.priority) { return -1 }
-      return 0
-    })
-  }
 }
 
 export default DataPersister

+ 10 - 47
services/dataProvider/dataProvider.ts

@@ -1,33 +1,19 @@
-import { Context } from '@nuxt/types/app'
 import { AnyJson, DataProviderArgs } from '~/types/interfaces'
 import { DENORMALIZER_TYPE, HTTP_METHOD, QUERY_TYPE } from '~/types/enums'
 import { providers } from '~/services/dataProvider/provider/_import'
-import ConstructUrl from '~/services/connection/constructUrl'
-import Connection from '~/services/connection/connection'
+import UrlBuilder from '~/services/connection/urlBuilder'
 import Serializer from '~/services/serializer/serializer'
 import ApiError from '~/services/utils/apiError'
+import DataManager from '~/services/connection/dataManager'
 
 /**
  * Le DataProvider a pour rôle de fournir des données issues de l'API Opentalent
  */
-class DataProvider {
-  private ctx !: Context;
-  private readonly defaultArguments!: DataProviderArgs;
-
-  constructor () {
-    this.defaultArguments = {
-      type: QUERY_TYPE.MODEL,
-      progress: false
-    }
-  }
-
-  /**
-   * Initialise le contexte (la connection en particulier)
-   * @param ctx
-   */
-  public initCtx (ctx: Context) {
-    Connection.initConnector(ctx.$axios)
-    this.ctx = ctx
+class DataProvider extends DataManager {
+  protected progress: boolean = false
+  protected defaultArguments: object = {
+    type: QUERY_TYPE.MODEL,
+    showProgress: false
   }
 
   /**
@@ -36,14 +22,11 @@ class DataProvider {
    */
   public async invoke (args: DataProviderArgs): Promise<any> {
     try {
-      // complete args with defaults
-      args = { ...this.defaultArguments, ...args }
-
-      DataProvider.startLoading(args)
+      await super.invoke(args)
 
-      const url = ConstructUrl.invoke(args)
+      const url = UrlBuilder.invoke(args)
 
-      const response = await DataProvider.sendRequest(url, args)
+      const response = await DataProvider.sendRequest(url, HTTP_METHOD.GET, args)
 
       const deserializeResponse = await Serializer.denormalize(response, DENORMALIZER_TYPE.HYDRA)
 
@@ -53,26 +36,6 @@ class DataProvider {
     }
   }
 
-  /**
-   * Signale le début du chargement à nuxt (si progress est true)
-   * @param args
-   */
-  private static startLoading (args: DataProviderArgs) {
-    if (args.progress) {
-      const $nuxt = window.$nuxt
-      $nuxt.$loading.start()
-    }
-  }
-
-  /**
-   * Send the request trough the Connection
-   * @param url
-   * @param args
-   */
-  public static sendRequest (url: string, args: DataProviderArgs): Promise<any> {
-    return Connection.invoke(HTTP_METHOD.GET, url, args)
-  }
-
   /**
    * Iterate over the available providers and invoke
    * the first one that support the given args

+ 33 - 0
services/hooks/hookable.ts

@@ -0,0 +1,33 @@
+import { hooks } from '~/services/dataDeleter/hook/_import'
+import { UrlArgs } from '~/types/interfaces'
+
+/**
+ * Base class for an object which support hooks
+ */
+class Hookable {
+  /**
+   * Iterate over the available hooks and invoke the ones
+   * that support the given args
+   */
+  protected async triggerHooks (args: UrlArgs) {
+    for (const Hook of Hookable.sortedHooks()) {
+      if (Hook.support(args)) {
+        await new Hook().invoke(args)
+      }
+    }
+  }
+
+  /**
+   * Sort the available hooks by priority
+   * @private
+   */
+  private static sortedHooks (): Iterable<any> {
+    return hooks.sort(function (a, b) {
+      if (a.priority > b.priority) { return 1 }
+      if (a.priority < b.priority) { return -1 }
+      return 0
+    })
+  }
+}
+
+export default Hookable

+ 1 - 1
services/rights/abilitiesUtils.ts

@@ -116,7 +116,7 @@ class AbilitiesUtils {
    * @param {AnyJson} abilitiesAvailable
    * @return {AnyJson}
    */
-  abilitiesAvailableFilter (abilitiesAvailable:AnyJson):AnyJson {
+  abilitiesAvailableFilter (abilitiesAvailable:AnyJson): AnyJson {
     return _.pickBy(abilitiesAvailable, (ability:any) => {
       const services = ability.services
       return this.canHaveTheAbility(services)

+ 1 - 1
services/serializer/denormalizer/hydra.ts

@@ -90,7 +90,7 @@ class Hydra extends BaseDenormalizer {
    * Hydrate l'objet JSON de façon récursive (afin de gérer les objet nested)
    * @param {AnyJson} data
    */
-  private static populateAllData (data: AnyJson):void {
+  private static populateAllData (data: AnyJson): void {
     for (const key in data) {
       const value = data[key]
       if (value instanceof Object) {

+ 1 - 1
services/store/repository.ts

@@ -29,7 +29,7 @@ class Repository {
     return this.store.$repo(model)
   }
 
-  public createNewModelInstance (model: typeof Model):Model {
+  public createNewModelInstance (model: typeof Model): Model {
     return this.getRepository(model).make()
   }
 

+ 9 - 9
tests/unit/services/connection/constructUrl.spec.ts

@@ -1,4 +1,4 @@
-import ConstructUrl from '~/services/connection/constructUrl'
+import UrlBuilder from '~/services/connection/urlBuilder'
 import { QUERY_TYPE } from '~/types/enums'
 import User from '~/tests/unit/fixture/models/User'
 import Organization from '~/tests/unit/fixture/models/Organization'
@@ -7,13 +7,13 @@ import { repositoryHelper } from '~/services/store/repository'
 describe('invoke()', () => {
   describe('getDefaultUrl()', () => {
     it('should throw an error if URL is missing', () => {
-      expect(() => ConstructUrl.invoke({
+      expect(() => UrlBuilder.invoke({
         type: QUERY_TYPE.DEFAULT
       })).toThrow()
     })
 
     it('should return the URL concat with Root URL', () => {
-      expect(ConstructUrl.invoke({
+      expect(UrlBuilder.invoke({
         type: QUERY_TYPE.DEFAULT,
         url: 'users'
       })).toEqual('/api/users')
@@ -22,13 +22,13 @@ describe('invoke()', () => {
 
   describe('getEnumUrl()', () => {
     it('should throw an error if enumType is missing', () => {
-      expect(() => ConstructUrl.invoke({
+      expect(() => UrlBuilder.invoke({
         type: QUERY_TYPE.ENUM
       })).toThrow()
     })
 
     it('should return the Enum URL concat with Root URL', () => {
-      expect(ConstructUrl.invoke({
+      expect(UrlBuilder.invoke({
         type: QUERY_TYPE.ENUM,
         enumType: 'billing_type'
       })).toEqual('/api/enum/billing_type')
@@ -37,7 +37,7 @@ describe('invoke()', () => {
 
   describe('getModelUrl()', () => {
     it('should throw an error if model is missing', () => {
-      expect(() => ConstructUrl.invoke({
+      expect(() => UrlBuilder.invoke({
         type: QUERY_TYPE.MODEL
       })).toThrow()
     })
@@ -46,7 +46,7 @@ describe('invoke()', () => {
       const repositoryHelperMock = repositoryHelper as jest.Mocked<typeof repositoryHelper>
       repositoryHelperMock.getEntity = jest.fn().mockReturnValue('users')
 
-      expect(ConstructUrl.invoke({
+      expect(UrlBuilder.invoke({
         type: QUERY_TYPE.MODEL,
         model: User
       })).toEqual('/api/users')
@@ -56,7 +56,7 @@ describe('invoke()', () => {
       const repositoryHelperMock = repositoryHelper as jest.Mocked<typeof repositoryHelper>
       repositoryHelperMock.getEntity = jest.fn().mockReturnValue('users')
 
-      expect(() => ConstructUrl.invoke({
+      expect(() => UrlBuilder.invoke({
         type: QUERY_TYPE.MODEL,
         model: User,
         rootModel: Organization
@@ -69,7 +69,7 @@ describe('invoke()', () => {
         .mockReturnValueOnce('users')
         .mockReturnValueOnce('organizations')
 
-      expect(ConstructUrl.invoke({
+      expect(UrlBuilder.invoke({
         type: QUERY_TYPE.MODEL,
         model: User,
         rootModel: Organization,

+ 18 - 19
types/interfaces.d.ts

@@ -28,7 +28,7 @@ interface ItemMenu {
 }
 interface ItemsMenu extends Array<ItemMenu> {}
 
-interface Menu{
+interface Menu {
   getMenu : () => ItemMenu | null,
   getHeaderMenu : () => ItemMenu | null,
 }
@@ -53,7 +53,7 @@ interface formState {
   goAfterLeave: string
 }
 
-interface alert{
+interface alert {
   type: TYPE_ALERT,
   message: string
 }
@@ -78,7 +78,7 @@ interface Historical {
   dateEnd?: string
 }
 
-interface accessState extends baseAccessState{
+interface accessState extends baseAccessState {
   bearer: string,
   switchId: number,
   activityYear: number,
@@ -113,7 +113,7 @@ interface baseOrganizationState {
   subDomain?: string
 }
 
-interface organizationState extends baseOrganizationState{
+interface organizationState extends baseOrganizationState {
   id: number,
   name: string,
   product?: string,
@@ -141,44 +141,43 @@ interface UrlArgs {
   readonly rootModel?: typeof Model,
   readonly id?:any,
   readonly rootId?:number
-  readonly progress?:boolean
-}
+  readonly showProgress?:boolean
 
-interface DataPersisterArgs extends UrlArgs{
-  data?: AnyJson,
   readonly hook?: string
 }
 
-interface DataDeleterArgs extends UrlArgs{
-  readonly hook?: string
+interface DataPersisterArgs extends UrlArgs {
+  data?: AnyJson
 }
 
-interface HookPersister{
+interface HookPersister {
   invoke(args: DataPersisterArgs): Promise<any>,
 }
 
-interface HookDeleter{
+interface DataDeleterArgs extends UrlArgs {
+}
+
+interface HookDeleter {
   invoke(args: DataDeleterArgs): Promise<any>,
 }
 
-interface DataProviderArgs extends UrlArgs{
-  readonly hook?: string
+interface DataProviderArgs extends UrlArgs {
 }
 
-interface HookProvider{
+interface HookProvider {
   invoke(args: DataProviderArgs): Promise<any>,
 }
-interface Provider{
+interface Provider {
   invoke(data: AnyJson): Promise<any>,
 }
-interface Denormalizer{
+interface Denormalizer {
   denormalize(data: any): any,
 }
-interface Normalizer{
+interface Normalizer {
   normalize(args: DataPersisterArgs): any,
 }
 
-interface DataProviders{
+interface DataProviders {
   initCtx(ctx: Context): void,
   invoke(args: DataProviderArgs): Promise<any>,
 }

+ 2 - 2
use/layout/Menus/baseMenu.ts

@@ -40,11 +40,11 @@ class BaseMenu {
     }
   }
 
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     return null
   }
 
-  getHeaderMenu ():ItemMenu | null {
+  getHeaderMenu (): ItemMenu | null {
     return null
   }
 }

+ 5 - 2
use/layout/Menus/billingMenu.ts

@@ -24,7 +24,7 @@ class BillingMenu extends BaseMenu implements Menu {
    * Construit le menu Facturation ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'billing_product_page')) {
@@ -61,8 +61,11 @@ class BillingMenu extends BaseMenu implements Menu {
 
     if (children.length === 1) {
       return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('billing', 'fa-euro-sign', undefined, undefined, children)
+    } else {
+      return null
     }
-    return children.length > 0 ? this.constructMenu('billing', 'fa-euro-sign', undefined, undefined, children) : null
   }
 }
 

+ 5 - 2
use/layout/Menus/communicationMenu.ts

@@ -24,7 +24,7 @@ class CommunicationMenu extends BaseMenu implements Menu {
    * Construit le menu Communication ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'inbox_page')) {
@@ -41,8 +41,11 @@ class CommunicationMenu extends BaseMenu implements Menu {
 
     if (children.length === 1) {
       return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('communication', 'fa-comments', undefined, undefined, children)
+    } else {
+      return null
     }
-    return children.length > 0 ? this.constructMenu('communication', 'fa-comments', undefined, undefined, children) : null
   }
 }
 

+ 7 - 5
use/layout/Menus/configurationMenu.ts

@@ -26,7 +26,7 @@ class ConfigurationMenu extends BaseMenu implements Menu {
    * Construit le menu Header Configuration ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getHeaderMenu ():ItemMenu | null {
+  getHeaderMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'organization_page')) {
@@ -73,11 +73,13 @@ class ConfigurationMenu extends BaseMenu implements Menu {
       children.push(this.constructMenu('course_duplication', undefined, '/duplicate_courses/', true))
     }
 
-    if (this.$ability.can('display', 'import_page')) {
-      children.push(this.constructMenu('import', undefined, '/import/all/', true))
+    if (children.length === 1) {
+      return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('configuration', 'fa-cogs', undefined, undefined, children)
+    } else {
+      return null
     }
-
-    return children.length > 0 ? this.constructMenu('configuration', 'fa-cogs', undefined, undefined, children) : null
   }
 }
 

+ 5 - 2
use/layout/Menus/cotisationsMenu.ts

@@ -24,7 +24,7 @@ class CotisationsMenu extends BaseMenu implements Menu {
    * Construit le menu Cotisations ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'rate_cotisation_page')) {
@@ -97,8 +97,11 @@ class CotisationsMenu extends BaseMenu implements Menu {
 
     if (children.length === 1) {
       return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('cotisations', 'fa-money-bill', undefined, undefined, children)
+    } else {
+      return null
     }
-    return children.length > 0 ? this.constructMenu('cotisations', 'fa-money-bill', undefined, undefined, children) : null
   }
 }
 

+ 2 - 2
use/layout/Menus/donorsMenu.ts

@@ -6,7 +6,7 @@ import BaseMenu from '~/use/layout/Menus/baseMenu'
 /**
  * @category Use/layout/Menus
  * @class DonorsMenu
- * Classe pour la construction du Menu Doneurs
+ * Classe pour la construction du Menu Donneurs
  */
 class DonorsMenu extends BaseMenu implements Menu {
   private $ability: Ability;
@@ -24,7 +24,7 @@ class DonorsMenu extends BaseMenu implements Menu {
    * Construit le menu Partenariat et Dons ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     if (this.$ability.can('display', 'donors_page')) {
       return this.constructMenu('donors', 'far fa-handshake', '/donors/list/', true)
     }

+ 5 - 2
use/layout/Menus/educationalMenu.ts

@@ -24,7 +24,7 @@ class EducationalMenu extends BaseMenu implements Menu {
    * Construit le menu Suivi pédagogique ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'criteria_notations_page')) {
@@ -53,8 +53,11 @@ class EducationalMenu extends BaseMenu implements Menu {
 
     if (children.length === 1) {
       return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('education_state', 'fa-graduation-cap', undefined, undefined, children)
+    } else {
+      return null
     }
-    return children.length > 0 ? this.constructMenu('education_state', 'fa-graduation-cap', undefined, undefined, children) : null
   }
 }
 

+ 1 - 1
use/layout/Menus/equipmentMenu.ts

@@ -24,7 +24,7 @@ class EquipmentMenu extends BaseMenu implements Menu {
    * Construit le menu Equipement ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     if (this.$ability.can('display', 'equipment_page')) {
       return this.constructMenu('equipment', 'fa-cube', '/equipment/list', true)
     }

+ 1 - 1
use/layout/Menus/medalsMenu.ts

@@ -24,7 +24,7 @@ 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 {
+  getMenu (): ItemMenu | null {
     if (this.$ability.can('display', 'medals_page')) {
       return this.constructMenu('medals', 'fa-trophy', '/medals/list/', true)
     }

+ 1 - 1
use/layout/Menus/myAccessesMenu.ts

@@ -27,7 +27,7 @@ class MyAccessesMenu extends BaseMenu implements Menu {
    * Construit le menu Header Multi compte ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getHeaderMenu ():ItemMenu | null {
+  getHeaderMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     _.each(this.$store.state.profile.access.multiAccesses, (access) => {

+ 1 - 1
use/layout/Menus/myFamilyMenu.ts

@@ -27,7 +27,7 @@ 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 {
+  getHeaderMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     // Si Access des membres de la familles (enfants)

+ 5 - 2
use/layout/Menus/statsMenu.ts

@@ -24,7 +24,7 @@ class StatsMenu extends BaseMenu implements Menu {
    * Construit le menu Statistique et Dons ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (this.$ability.can('display', 'report_activity_page')) {
@@ -41,8 +41,11 @@ class StatsMenu extends BaseMenu implements Menu {
 
     if (children.length === 1) {
       return children[0]
+    } else if (children.length > 0) {
+      return this.constructMenu('stats', 'fa-chart-bar', undefined, undefined, children)
+    } else {
+      return null
     }
-    return children.length > 0 ? this.constructMenu('stats', 'fa-chart-bar', undefined, undefined, children) : null
   }
 }
 

+ 3 - 3
use/layout/Menus/websiteMenu.ts

@@ -27,7 +27,7 @@ class WebsiteMenu extends BaseMenu implements Menu {
    * Construit le menu Site internet ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getMenu ():ItemMenu | null {
+  getMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     if (!this.$store.state.profile.organization.website && this.$store.state.profile.access.isAdminAccess) {
@@ -42,7 +42,7 @@ class WebsiteMenu extends BaseMenu implements Menu {
    * Construit le menu Header des Sites internet ou null si aucune page accessible
    * @return {ItemMenu | null}
    */
-  getHeaderMenu ():ItemMenu | null {
+  getHeaderMenu (): ItemMenu | null {
     const children: ItemsMenu = []
 
     children.push(this.constructMenu(this.$store.state.profile.organization.name, undefined, this.getWebsite(this.$store.state.profile.organization), false, undefined, true))
@@ -54,7 +54,7 @@ class WebsiteMenu extends BaseMenu implements Menu {
     return children.length > 0 ? this.constructMenu('website', 'fa-globe-europe', undefined, undefined, children) : null
   }
 
-  getWebsite (organization: organizationState):string {
+  getWebsite (organization: organizationState): string {
     return organization.website ? organization.website : this.$config.baseURL_typo3.replace('###subDomain###', organization.subDomain)
   }
 }

+ 6 - 6
use/layout/menu.ts

@@ -42,7 +42,7 @@ class Menu {
   /**
    * Construit le menu et mets à jour le state du profile d'access
    */
-  useLateralMenuConstruct ():Ref {
+  useLateralMenuConstruct (): Ref {
     const menu:ItemsMenu = []
 
     const accessMenu:ItemMenu | null = getAccessMenu(this.$config, this.$ability, this.$store)
@@ -90,7 +90,7 @@ class Menu {
   /**
    * Construit le menu configuration et mets à jour le state du profile d'access
    */
-  useConfigurationMenuConstruct ():Ref {
+  useConfigurationMenuConstruct (): Ref {
     const menu:ItemMenu | null = getConfigurationMenu(this.$config, this.$ability, this.$store)
     // Si l'utilisateur possède au moins un menu alors le menu configuration sera accessible
     this.$store.commit('profile/access/setHasConfigurationMenu', menu != null)
@@ -100,14 +100,14 @@ class Menu {
   /**
    * Construit le menu Mon Compte
    */
-  useAccountMenuConstruct ():Ref {
+  useAccountMenuConstruct (): Ref {
     return ref(getAccountMenu(this.$config, this.$ability, this.$store))
   }
 
   /**
    * Construit le menu Mes structure et mets à jour le state du profile d'access
    */
-  useMyAccessesMenuConstruct ():Ref {
+  useMyAccessesMenuConstruct (): Ref {
     const menu:ItemMenu | null = getMyAccessesMenu(this.$config, this.$ability, this.$store)
     // Si l'utilisateur possède au moins un menu alors le menu mes structures sera accessible
     this.$store.commit('profile/access/setHasAccessesMenu', menu != null)
@@ -117,7 +117,7 @@ class Menu {
   /**
    * Construit le menu Changement d'utilisateur et mets à jour le state du profile d'access
    */
-  useMyFamilyMenuConstruct ():Ref {
+  useMyFamilyMenuConstruct (): Ref {
     const menu:ItemMenu | null = getMyFamilyMenu(this.$config, this.$ability, this.$store)
     // Si l'utilisateur possède au moins un menu alors le menu changement d'utilisateur sera accessible
     this.$store.commit('profile/access/setHasFamilyMenu', menu != null)
@@ -127,7 +127,7 @@ class Menu {
   /**
    * Construit le menu site internet du header
    */
-  useWebSiteMenuConstruct ():Ref {
+  useWebSiteMenuConstruct (): Ref {
     return ref(getWebsiteMenu(this.$config, this.$ability, this.$store).getHeaderMenu())
   }
 }