Vincent GUFFON 4 年之前
父节点
当前提交
089f544968

+ 1 - 1
components/Layout/Header.vue

@@ -45,7 +45,7 @@
 
     <LayoutHeaderMenu :menu="myFamilyMenu" v-if="hasFamilyMenu"></LayoutHeaderMenu>
 
-    <LayoutNotification></LayoutNotification>
+    <LayoutHeaderNotification></LayoutHeaderNotification>
 
     <LayoutHeaderMenu :menu="configurationMenu" v-if="hasConfigurationMenu"></LayoutHeaderMenu>
 

+ 0 - 0
components/Layout/HeaderMenu.vue → components/Layout/Header/Menu.vue


+ 0 - 0
components/Layout/Notification.vue → components/Layout/Header/Notification.vue


+ 0 - 0
components/Layout/ActivityYear.vue → components/Layout/SubHeader/ActivityYear.vue


+ 0 - 0
components/Layout/Breadcrumbs.vue → components/Layout/SubHeader/Breadcrumbs.vue


+ 34 - 14
components/Layout/DataTiming.vue → components/Layout/SubHeader/DataTiming.vue

@@ -4,7 +4,7 @@
     <span class="mr-2 ot_dark_grey--text font-weight-bold">{{$t('display_data')}} : </span>
 
     <v-btn-toggle
-      v-model="timeChoice"
+      v-model="historicalBtn"
       dense
       class="ot_light_grey toggle-btn"
       active-class="ot_green ot_white--text"
@@ -39,19 +39,10 @@
       const {markFormAsNotDirty} = $useDirtyForm(store)
       const {updateMyProfile, setHistorical, historical} = $useMyProfileUpdater(store, $dataPersister)
 
-      const timeChoice:Ref<Array<number>> = ref(Array<number>())
+      const historicalBtn:Ref<Array<number>> = initHistoricalBtn(historical.value)
 
-      const historicalArray:Array<string> = ['past', 'present', 'future']
-      for(const key in historicalArray){
-        if (historical.value[historicalArray[key]])
-          timeChoice.value.push(parseInt(key))
-      }
-
-      const unwatch:WatchStopHandle = watch(timeChoice, async (newValue) => {
-        const historicalChoice:Array<string> = []
-        for(const key in newValue){
-          historicalChoice.push(historicalArray[newValue[key]])
-        }
+      const unwatch:WatchStopHandle = watch(historicalBtn, async (newValue) => {
+        const historicalChoice:Array<string> = initHistoricalChoice(newValue)
 
         setHistorical(historicalChoice)
         markFormAsNotDirty()
@@ -64,10 +55,39 @@
       })
 
       return {
-        timeChoice
+        historicalBtn
       }
     }
   })
+
+  /**
+   * Prépare le tableau de valeur numéraire devant être passé au component v-btn-toggle
+   * @param historical
+   */
+  function initHistoricalBtn(historical:Array<any>){
+    const timeChoice:Ref<Array<number>> = ref(Array<number>())
+    const historicalArray:Array<any> = ['past', 'present', 'future']
+
+    for(const key in historicalArray){
+      if (historical[historicalArray[key]])
+        timeChoice.value.push(parseInt(key))
+    }
+    return timeChoice
+  }
+
+  /**
+   * Transforme le résultat renvoyé par le component v-btn-toggle pour l'enregistrer coté AccessProfile
+   * @param historical
+   */
+  function initHistoricalChoice(historical:Array<any>){
+    const historicalArray:Array<any> = ['past', 'present', 'future']
+
+    const historicalChoice:Array<string> = []
+    for(const key in historical){
+      historicalChoice.push(historicalArray[historical[key]])
+    }
+    return historicalChoice
+  }
 </script>
 
 <style scoped lang="scss">

+ 0 - 0
components/Layout/DataTimingRange.vue → components/Layout/SubHeader/DataTimingRange.vue


+ 118 - 0
components/Layout/SubHeader/PersonnalizedList.vue

@@ -0,0 +1,118 @@
+<template>
+  <main>
+    <v-menu
+      bottom
+      left
+      transition="slide-y-transition"
+      :close-on-content-click="false"
+      min-width="500"
+    >
+      <template v-slot:activator="{ on, attrs }">
+        <span
+          v-bind="attrs"
+          v-on="on"
+          class="ot_green--text"
+        >
+          {{$t('my_list')}}
+        </span>
+      </template>
+      <v-list>
+        <v-list-item>
+          <v-list-item-title>
+            <UiInputAutocomplete
+              :label="$t('searchList')"
+              :isLoading="isLoading"
+              :items="items"
+              :itemText="['label']"
+              v-on:update="goOn"
+              :returnObject="true"
+            >
+            </UiInputAutocomplete>
+          </v-list-item-title>
+        </v-list-item>
+      </v-list>
+    </v-menu>
+  </main>
+</template>
+
+<script lang="ts">
+  import {computed, defineComponent, useContext, useFetch, ref, Ref, ComputedRef} from '@nuxtjs/composition-api'
+  import {QUERY_TYPE} from "~/types/enums";
+  import {PersonalizedList} from "~/models/Access/PersonalizedList";
+  import {repositoryHelper} from "~/services/store/repository";
+  import {Collection} from "@vuex-orm/core";
+  import {AnyJson} from "~/types/interfaces";
+  import {$objectProperties} from "~/services/utils/objectProperties";
+
+  export default defineComponent({
+    fetchOnServer: false,
+    setup() {
+      const {$dataProvider, $config} = useContext()
+      const isLoading:Ref<boolean> = ref(true)
+      const homeUrl:string = $config.baseURL_adminLegacy
+
+      useFetch(async ()=>{
+        await $dataProvider.invoke({
+          type: QUERY_TYPE.MODEL,
+          model: PersonalizedList
+        })
+        isLoading.value = false
+      })
+
+      const items:ComputedRef<Array<AnyJson>>  = computed(() => {
+        const lists = repositoryHelper.findCollectionFromModel(PersonalizedList) as Collection<PersonalizedList>
+
+        let listsGroupByKeyMenu:Array<AnyJson> = groupListByKey(lists)
+
+        listsGroupByKeyMenu = $objectProperties.sortObjectByKey(listsGroupByKeyMenu)
+
+        return constructAutoCompleteItems(listsGroupByKeyMenu)
+      })
+
+      const goOn = (list:PersonalizedList) => {
+        window.location.href = `${homeUrl}/${list.entity}/list/${list.id}`
+      }
+
+      return {
+        items,
+        isLoading,
+        goOn
+      }
+    }
+  })
+
+  /**
+   * On regroupe la list par clé afin de constituer les groups
+   * @param lists
+   */
+  function groupListByKey(lists:Collection<PersonalizedList>): Array<AnyJson>{
+    const {app:{i18n}} = useContext()
+    let listsGroupByKeyMenu:any = {}
+    for(const list of lists){
+      const key = i18n.t(list.menuKey) as string
+      listsGroupByKeyMenu[key] = listsGroupByKeyMenu[key] ?? []
+      listsGroupByKeyMenu[key].push(list)
+    }
+    return listsGroupByKeyMenu
+  }
+
+  /**
+   * On construit la list d'Item, constituée de Header et d'Item "sélectionnable"
+   * @param listsGroupByKeyMenu
+   */
+  function constructAutoCompleteItems(listsGroupByKeyMenu:Array<any>){
+    const items: any = []
+    for(const key in listsGroupByKeyMenu){
+      items.push({ header: key })
+      for(const item of listsGroupByKeyMenu[key]){
+        items.push(item)
+      }
+    }
+    return items
+  }
+</script>
+
+
+<style scoped>
+
+</style>

+ 8 - 10
components/Layout/Subheader.vue

@@ -1,35 +1,33 @@
 <template>
   <main>
     <v-card
-      v-if="displayedSubHeader"
-      class="d-none d-sm-none d-md-flex ot_super_light_grey text-body-2"
+      class="d-none d-sm-none d-md-flex ot_light_grey text-body-2"
       flat
       tile
     >
-      <LayoutBreadcrumbs class="mr-auto"></LayoutBreadcrumbs>
+      <LayoutSubHeaderBreadcrumbs class="mr-auto"></LayoutSubHeaderBreadcrumbs>
+
       <v-card
         class="d-md-flex ot_super_light_grey pt-2 mr-6  align-baseline"
         flat
         tile
       >
-        <LayoutActivityYear v-if="!showDateTimeRange"></LayoutActivityYear>
-        <LayoutDataTiming v-if="!showDateTimeRange" class="ml-2"></LayoutDataTiming>
-        <LayoutDataTimingRange class="ml-n1" v-on:showDateTimeRange="showDateTimeRange=$event"></LayoutDataTimingRange>
+        <LayoutSubHeaderActivityYear class="activity-year" v-if="!showDateTimeRange"></LayoutSubHeaderActivityYear>
+        <LayoutSubHeaderDataTiming v-if="!showDateTimeRange" class="data-timing ml-2"></LayoutSubHeaderDataTiming>
+        <LayoutSubHeaderDataTimingRange class="data-timing-range ml-n1" v-on:showDateTimeRange="showDateTimeRange=$event"></LayoutSubHeaderDataTimingRange>
+        <LayoutSubHeaderPersonnalizedList class="personalized-list ml-2"></LayoutSubHeaderPersonnalizedList>
       </v-card>
     </v-card>
   </main>
 </template>
 
 <script lang="ts">
-  import {computed, defineComponent, useContext, ref, Ref, ComputedRef} from '@nuxtjs/composition-api'
+  import {defineComponent, ref, Ref} from '@nuxtjs/composition-api'
 
   export default defineComponent({
     setup() {
-      const {store} = useContext()
       const showDateTimeRange:Ref<boolean> = ref(false)
-      const displayedSubHeader:ComputedRef<boolean> = computed(()=>store.state.profile.access.hasLateralMenu || store.state.profile.access.isTeacher)
       return {
-        displayedSubHeader,
         showDateTimeRange
       }
     }

+ 37 - 61
components/Ui/Input/Autocomplete.vue

@@ -1,36 +1,30 @@
 <template>
   <main>
     <v-autocomplete
-      v-model="model"
-      :value="data"
-      :items="items"
-      :loading="isLoading"
-      :search-input.sync="search"
-      hide-no-data
-      hide-selected
+      @change="$emit('update', $event, field)"
+      :items="itemsToDisplayed"
+      :label="$t(label)"
       item-text="textDisplay"
       :item-value="itemValue"
-      :label="$t(label_field)"
-      :placeholder="$t('start_your_research')"
-      prepend-icon="mdi-magnify"
+      :multiple="multiple"
+      :loading="isLoading"
       :return-object="returnObject"
-    ></v-autocomplete>
-
+    >
+    </v-autocomplete>
   </main>
 </template>
 
 <script lang="ts">
-  import {defineComponent, computed, watch, ref, useContext, onUnmounted, Ref} from '@nuxtjs/composition-api'
-  import {QUERY_TYPE} from "~/types/enums";
-  import * as _ from "lodash";
+  import {computed, defineComponent, ComputedRef} from '@nuxtjs/composition-api'
+  import {AnyJson} from "~/types/interfaces";
 
   export default defineComponent({
     props: {
-      label:{
+      label: {
         type: String,
         required: false
       },
-      field:{
+      field: {
         type: String,
         required: false
       },
@@ -38,69 +32,51 @@
         type: String,
         required: false
       },
+      items: {
+        type: Array,
+        required: false,
+        default: []
+      },
       readOnly: {
         type: Boolean,
         required: false
       },
-      itemValue:{
+      itemValue: {
         type: String,
         default: 'id'
       },
-      itemText:{
+      itemText: {
         type: Array,
         required: true
       },
-      returnObject:{
+      returnObject: {
+        type: Boolean,
+        default: false
+      },
+      multiple: {
+        type: Boolean,
+        default: false
+      },
+      isLoading: {
         type: Boolean,
         default: false
       }
     },
-    setup(props){
-      const {$dataProvider} = useContext();
-
-      const search:Ref<string|null> = ref(null);
-      const model = ref(null);
-      const count = ref(0);
-      const entries = ref([]);
-      const isLoading = ref(false);
-
-      const items = computed(() => {
-        return entries.value.map(entry => {
-          const textDisplay:Array<string> = []
-          for (const text of props.itemText){
-            textDisplay.push(entry[text as string])
+    setup(props) {
+      //On reconstruit les items à afficher car le text de l'Item doit être construit par rapport au itemText passé en props
+      const itemsToDisplayed: ComputedRef<Array<AnyJson>> = computed(() => {
+        return props.items.map((item: any) => {
+          const textDisplay: Array<string> = []
+          for (const text of props.itemText) {
+            textDisplay.push(item[text as string])
           }
-          return Object.assign({}, entry, { textDisplay: textDisplay.join(' ') })
-        })
-      })
-
-      const unwatch = watch(search, _.debounce(async (research) => {
-        // Items have already been requested
-        if (isLoading.value) return
-
-        isLoading.value = true
-
-        let response = await $dataProvider.invoke({
-          type: QUERY_TYPE.DEFAULT,
-          url: `gps-coordinate-searching?city=${research}`
+          return Object.assign({}, item, {textDisplay: textDisplay.join(' ')})
         })
-
-        count.value = response.length
-        entries.value = response
-        isLoading.value = false
-      }, 500))
-
-      onUnmounted(()=>{
-        unwatch()
       })
 
       return {
-        label_field : props.label ?? props.field,
-        count,
-        isLoading,
-        items,
-        search,
-        model
+        label_field: props.label ?? props.field,
+        itemsToDisplayed
       }
 
     }

+ 112 - 0
components/Ui/Input/AutocompleteWithAPI.vue

@@ -0,0 +1,112 @@
+<template>
+  <main>
+    <v-autocomplete
+      v-model="model"
+      :value="data"
+      :items="items"
+      :loading="isLoading"
+      :search-input.sync="search"
+      hide-no-data
+      hide-selected
+      item-text="textDisplay"
+      :item-value="itemValue"
+      :label="$t(label_field)"
+      :placeholder="$t('start_your_research')"
+      prepend-icon="mdi-magnify"
+      :return-object="returnObject"
+    ></v-autocomplete>
+
+  </main>
+</template>
+
+<script lang="ts">
+  import {defineComponent, computed, watch, ref, useContext, onUnmounted, Ref} from '@nuxtjs/composition-api'
+  import {QUERY_TYPE} from "~/types/enums";
+  import * as _ from "lodash";
+
+  export default defineComponent({
+    props: {
+      label:{
+        type: String,
+        required: false
+      },
+      field:{
+        type: String,
+        required: false
+      },
+      data: {
+        type: String,
+        required: false
+      },
+      readOnly: {
+        type: Boolean,
+        required: false
+      },
+      itemValue:{
+        type: String,
+        default: 'id'
+      },
+      itemText:{
+        type: Array,
+        required: true
+      },
+      returnObject:{
+        type: Boolean,
+        default: false
+      }
+    },
+    setup(props){
+      const {$dataProvider} = useContext();
+
+      const search:Ref<string|null> = ref(null);
+      const model = ref(null);
+      const count = ref(0);
+      const entries = ref([]);
+      const isLoading = ref(false);
+
+      const items = computed(() => {
+        return entries.value.map(entry => {
+          const textDisplay:Array<string> = []
+          for (const text of props.itemText){
+            textDisplay.push(entry[text as string])
+          }
+          return Object.assign({}, entry, { textDisplay: textDisplay.join(' ') })
+        })
+      })
+
+      const unwatch = watch(search, _.debounce(async (research) => {
+        // Items have already been requested
+        if (isLoading.value) return
+
+        isLoading.value = true
+
+        let response = await $dataProvider.invoke({
+          type: QUERY_TYPE.DEFAULT,
+          url: `gps-coordinate-searching?city=${research}`
+        })
+
+        count.value = response.length
+        entries.value = response
+        isLoading.value = false
+      }, 500))
+
+      onUnmounted(()=>{
+        unwatch()
+      })
+
+      return {
+        label_field : props.label ?? props.field,
+        count,
+        isLoading,
+        items,
+        search,
+        model
+      }
+
+    }
+  })
+</script>
+
+<style scoped>
+
+</style>

+ 4 - 3
components/Ui/SubList.vue

@@ -13,10 +13,11 @@
 </template>
 
 <script lang="ts">
-  import {defineComponent, ref, computed, useContext, useFetch, toRefs, onUnmounted, ToRefs} from '@nuxtjs/composition-api'
-  import {Model, Query} from "@vuex-orm/core";
+  import {defineComponent, ref, computed, useContext, useFetch, toRefs, onUnmounted, ToRefs, ComputedRef} from '@nuxtjs/composition-api'
+  import {Query} from "@vuex-orm/core";
   import {queryHelper} from "~/services/store/query";
   import {QUERY_TYPE} from "~/types/enums";
+  import {Collection} from "@vuex-orm/core/dist/src/data/Data";
 
   export default defineComponent({
     props: {
@@ -49,7 +50,7 @@
         })
       })
 
-      const items  = computed(() => queryHelper.getCollection(query.value))
+      const items:ComputedRef<Collection>  = computed(() => queryHelper.getCollection(query.value))
       // onUnmounted( useRepositoryHelper.cleanRepository(repository.value) )
 
       return {

+ 2 - 0
lang/fr-FR.js

@@ -4,6 +4,7 @@ import fields from '@/lang/field/fr-FR'
 import rulesAndErrors from '@/lang/rulesAndErrors/fr-FR'
 import form from '@/lang/form/fr-FR'
 import breadcrumbs from '@/lang/breadcrumbs/fr-FR'
+import menuKey from '@/lang/menuKey/fr-FR'
 
 export default (context, locale) => {
 
@@ -14,5 +15,6 @@ export default (context, locale) => {
     ...rulesAndErrors(context, locale),
     ...form(context, locale),
     ...breadcrumbs(context, locale),
+    ...menuKey(context, locale),
   }
 }

+ 2 - 0
lang/layout/fr-FR.js

@@ -100,5 +100,7 @@ export default (context, locale) => {
     history_help: 'Personnaliser la pédiode d\'affichage',
     period_choose: 'Période à afficher',
     date_choose: 'Choix de la période',
+    my_list: 'Mes listes',
+    searchList: 'Rechercher parmis mes listes personnalisées',
   })
 }

+ 7 - 0
lang/menuKey/fr-FR.js

@@ -0,0 +1,7 @@
+export default (context, locale) => {
+  return ({
+    attendanceListMenuKey: 'Absences',
+    billAndBillCreditListMenuKey: "Facturation",
+    personRepertoryListMenuKey: "Répertoire",
+  })
+}

+ 6 - 3
layouts/default.vue

@@ -10,7 +10,7 @@
       <LayoutHeader v-on:handle-open-menu-click="handleOpenMenu"></LayoutHeader>
 
       <v-main class="ot_content_color">
-        <LayoutSubheader></LayoutSubheader>
+        <LayoutSubheader v-if="displayedSubHeader"></LayoutSubheader>
         <nuxt/>
       </v-main>
 
@@ -20,7 +20,7 @@
 </template>
 
 <script lang="ts">
-  import {computed, defineComponent, reactive, useContext} from '@nuxtjs/composition-api'
+  import {computed, ComputedRef, defineComponent, reactive, useContext} from '@nuxtjs/composition-api'
   import {$useMenu} from '@/use/layout/menu'
 
   export default defineComponent({
@@ -33,7 +33,9 @@
         clipped: false,
         miniVariant: false
       })
-      const displayedMenu = computed(()=>store.state.profile.access.hasLateralMenu)
+      const displayedMenu:ComputedRef<boolean> = computed(()=>store.state.profile.access.hasLateralMenu)
+
+      const displayedSubHeader:ComputedRef<boolean> = computed(()=>store.state.profile.access.hasLateralMenu || store.state.profile.access.isTeacher)
 
       const handleOpenMenu = (miniVariant:boolean) => {
         properties.miniVariant = miniVariant
@@ -43,6 +45,7 @@
         properties,
         menu,
         displayedMenu,
+        displayedSubHeader,
         handleOpenMenu
       }
     },

+ 17 - 0
models/Access/PersonalizedList.ts

@@ -0,0 +1,17 @@
+import {Attr, Model, Str} from '@vuex-orm/core'
+
+export class PersonalizedList extends Model{
+  static entity = 'personalized_lists'
+
+  @Attr(null)
+  id!: number | null
+
+  @Str('')
+  label!:string|null
+
+  @Str('')
+  entity!:string|null
+
+  @Str('')
+  menuKey!:string
+}

+ 18 - 0
services/utils/objectProperties.ts

@@ -89,6 +89,24 @@ class ObjectProperties {
       return values;
     }, {});
   }
+
+  /**
+   * Tri un objet par rapport à ses clés (par ordre alpha)
+   * @example sortObjectByKey({b:1, d:2, c:3, a:4}) => {a:4, b:1, c:3, d:2}
+   * @param toSort
+   */
+  sortObjectByKey(toSort:any): any {
+    if (typeof toSort !== 'object') {
+      throw new Error('Expecting an object parameter');
+    }
+    return Object.keys(toSort).sort().reduce(
+      (obj:any, key:string) => {
+        obj[key] = toSort[key];
+        return obj;
+      },
+      {}
+    )
+  }
 }
 
 export const $objectProperties = new ObjectProperties()

+ 12 - 0
store/profile/access.ts

@@ -2,6 +2,8 @@ import {$roleUtils} from '~/services/rights/roleUtils'
 import {AbilitiesType, accessState, AnyJson, baseAccessState, baseOrganizationState, Historical} from "~/types/interfaces";
 import * as _ from "lodash";
 import {GENDER} from "~/types/enums";
+import {MyProfile} from "~/models/Access/MyProfile";
+import {repositoryHelper} from "~/services/store/repository";
 
 export const state = () => ({
   bearer: null,
@@ -154,6 +156,8 @@ export const actions = {
 
     //Time to set Oganization Profile
     context.dispatch('profile/organization/setProfile', profile.organization, {root:true})
+
+    context.dispatch('createNewMyProfileVUexOrmInstance', profile)
   },
   setMultiAccesses(context:any, accesses:any){
     _.each(accesses, organization => {
@@ -187,5 +191,13 @@ export const actions = {
       }
       context.commit('setOriginalAccess', originalAccess)
     }
+  },
+  /**
+   * Créer une nouvelle instance MyProfile dans Vuex ORM
+   * @param context
+   * @param access
+   */
+  createNewMyProfileVUexOrmInstance(context:any, access: any): void{
+    repositoryHelper.persist(MyProfile, access)
   }
 }

+ 47 - 0
tests/unit/component/Layout/SubHeader.spec.js

@@ -0,0 +1,47 @@
+import SubHeader from "~/components/Layout/SubHeader";
+import LayoutSubHeaderBreadcrumbs from "~/components/Layout/SubHeader/Breadcrumbs";
+
+import {shallowMount} from "@vue/test-utils"
+import Vuetify from 'vuetify'
+
+var wrapper
+var vuetify
+beforeEach(()=>{
+  vuetify = new Vuetify()
+  wrapper = shallowMount(SubHeader, {
+    stubs:[
+      'LayoutSubHeaderBreadcrumbs',
+      'LayoutSubHeaderActivityYear',
+      'LayoutSubHeaderDataTiming',
+      'LayoutSubHeaderDataTimingRange',
+      'LayoutSubHeaderPersonnalizedList'
+    ],
+    vuetify
+  })
+})
+
+describe("LayoutSubHeaderActivityYear", () => {
+
+  it("should display by default", async () => {
+    expect(wrapper.find(".activity-year").exists()).toBeTruthy()
+  });
+
+  it("should hide if showDateTimeRange is truthy", async () => {
+    await wrapper.setData({ showDateTimeRange: true })
+    expect(wrapper.find(".activity-year").exists()).toBeFalsy()
+  });
+})
+
+describe("LayoutSubHeaderDataTiming", () => {
+
+  it("should display by default", async () => {
+    expect(wrapper.find(".data-timing").exists()).toBeTruthy()
+  });
+
+  it("should hide if showDateTimeRange is truthy", async () => {
+    await wrapper.setData({ showDateTimeRange: true })
+    expect(wrapper.find(".data-timing").exists()).toBeFalsy()
+  });
+})
+
+

+ 1 - 1
tests/unit/component/Layout/Breadcrumbs.spec.js → tests/unit/component/Layout/SubHeader/Breadcrumbs.spec.js

@@ -1,4 +1,4 @@
-import Breadcrumbs from "~/components/Layout/Breadcrumbs";
+import Breadcrumbs from "~/components/Layout/SubHeader/Breadcrumbs";
 import {shallowMount, RouterLinkStub} from "@vue/test-utils"
 
 describe("Breadcrumbs", () => {

+ 8 - 0
tests/unit/services/utils/objectProperties.spec.ts

@@ -63,3 +63,11 @@ describe('cloneAndNest()', () => {
   );
 });
 
+describe('sortObjectByKey()', () => {
+  it('should throw an error if args is not an array', () =>
+    expect(() => $objectProperties.sortObjectByKey('foo')).toThrow()
+  );
+  it('should sort an array by his keys', () =>
+    expect($objectProperties.sortObjectByKey({b:1, d:2, c:3, a:4})).toStrictEqual({a:4, b:1, c:3, d:2})
+  );
+})

+ 4 - 11
tests/unit/use/updater/useMyProfileUpdater.spec.ts

@@ -48,17 +48,10 @@ describe('getHistoricalRangeEntry()', () => {
   })
 })
 
-describe('createNewMyProfileInstance()', () => {
-  it('should create an My Profile instance', () => {
-    const state_my_profile = {
-      id: 1,
-      activityYear: 2020,
-      historical: {past: false, present: true, future: false}
-    } as accessState
-    const myProfile = useMyProfileUpdater.createNewMyProfileInstance(state_my_profile)
-    expect(myProfile.id).toStrictEqual(1)
-    expect(myProfile.activityYear).toStrictEqual(2020)
-    expect(myProfile.historical).toStrictEqual({past: false, present: true, future: false})
+describe('getMyProfileInstance()', () => {
+  it('should call findItemFromModel', () => {
+    repositoryHelper.findItemFromModel  =  jest.fn()
+    expect(repositoryHelper).toHaveBeenCalled
   })
 })
 

+ 6 - 10
use/updater/useMyProfileUpdater.ts

@@ -4,6 +4,7 @@ import {computed, ComputedRef} from '@nuxtjs/composition-api'
 import {accessState, AnyJson, AnyStore, Historical} from "~/types/interfaces";
 import DataPersister from "~/services/dataPersister/dataPersister";
 import {MyProfile} from "~/models/Access/MyProfile";
+import {Item, Model} from "@vuex-orm/core";
 
 /**
  * @category Use/updater
@@ -23,7 +24,7 @@ export class UseMyProfileUpdater{
    * Composition function
    */
   public invoke(): AnyJson{
-    this.myProfile = this.createNewMyProfileInstance(this.store.state.profile.access)
+    this.myProfile = this.getMyProfileInstance(this.store.state.profile.access.id) as MyProfile
     const activityYear:ComputedRef<number> = computed(()=>this.myProfile.activityYear)
     const historical:ComputedRef<Historical> = computed(()=>this.myProfile.historical)
 
@@ -38,16 +39,11 @@ export class UseMyProfileUpdater{
   }
 
   /**
-   * Créer une nouvelle instance MyProfile
-   * @param myProfile
+   * récupère l'instance MyProfile
+   * @param myProfileId
    */
-  private createNewMyProfileInstance(myProfile:accessState): MyProfile{
-    const myProfileInstance = repositoryHelper.createNewModelInstance(MyProfile) as MyProfile
-    myProfileInstance.id = myProfile.id
-    myProfileInstance.activityYear = myProfile.activityYear
-    myProfileInstance.historical = myProfile.historical
-    repositoryHelper.persist(MyProfile, myProfileInstance)
-    return myProfileInstance
+  private getMyProfileInstance(myProfileId:any): Item<Model>{
+    return repositoryHelper.findItemFromModel(MyProfile, parseInt(myProfileId))
   }
   /**
    * Mets à jour l'activity de my profile