Browse Source

fix bug on profile refresh, fix lodash imports, add loading screen

Olivier Massot 2 years ago
parent
commit
1aa1efb903

+ 8 - 19
components/Layout/LoadingScreen.vue

@@ -1,7 +1,12 @@
 <!-- Animation circulaire à afficher durant les chargements -->
 
 <template>
-  <v-overlay :value="loading" class="loading-page">
+  <v-overlay
+          v-model="pageStore.loading"
+          z-index="9000"
+          persistent
+          class="align-center justify-center"
+  >
     <v-progress-circular
       indeterminate
       size="64"
@@ -10,25 +15,9 @@
 </template>
 
 <script setup lang="ts">
-  import {Ref, ref} from "@vue/reactivity";
+  import {usePageStore} from "~/stores/page";
 
-  const loading: Ref<boolean> = ref(false)
-
-  const set = (_num: number) => {
-    loading.value = true
-  }
-
-  const start = () => {
-    loading.value = true
-  }
-
-  const finish = () => {
-    loading.value = false
-  }
-
-  const fail = () => {
-    loading.value = false
-  }
+  const pageStore = usePageStore()
 </script>
 
 <style scoped>

+ 11 - 12
components/Layout/SubHeader/ActivityYear.vue

@@ -27,7 +27,6 @@
 </template>
 
 <script setup lang="ts">
-
 import {useEntityManager} from "~/composables/data/useEntityManager";
 import {useFormStore} from "~/stores/form";
 import {useOrganizationProfileStore} from "~/stores/organizationProfile";
@@ -35,22 +34,27 @@ import {useAccessProfileStore} from "~/stores/accessProfile";
 import Access from "~/models/Access/Access";
 import {Ref, ref} from "@vue/reactivity";
 import {useDisplay} from "vuetify";
+import {usePageStore} from "~/stores/page";
+import MyProfile from "~/models/Access/MyProfile";
 
 const { em } = useEntityManager()
 const accessProfileStore = useAccessProfileStore()
 const organizationProfileStore = useOrganizationProfileStore()
 const formStore = useFormStore()
+const pageStore = usePageStore()
 const { mdAndUp } = useDisplay()
 
-const currentActivityYear: Ref<number | undefined> = ref(accessProfileStore.activityYear ?? undefined)
+const currentActivityYear: ComputedRef<number | undefined> = computed(() => accessProfileStore.activityYear ?? undefined)
 const yearPlusOne: boolean = !organizationProfileStore.isManagerProduct
 const label: string = organizationProfileStore.isSchool ? 'schooling_year' : organizationProfileStore.isArtist ? 'season_year' : 'cotisation_year'
 
 /**
  * Persist a new activityYear
- * @param activityYear
+ * @param event
  */
-const setActivityYear = async (activityYear: number) => {
+const setActivityYear = async (event: string) => {
+  const activityYear = parseInt(event)
+
   if (!(1900 < activityYear) || !(activityYear <= 2100)) {
     throw new Error("Error: 'year' shall be a valid year")
   }
@@ -59,17 +63,12 @@ const setActivityYear = async (activityYear: number) => {
   }
   formStore.setDirty(false)
 
-  const access = await em.fetch(Access, accessProfileStore.id)
-  access.activityYear = activityYear
-  await em.persist(Access, access)
-
-  // Update the store
-  // TODO: voir si mieux d'automatiser ces maj du profil, ou de les faire à la main au cas par cas?
-  accessProfileStore.$patch({ activityYear: activityYear })
+  pageStore.loading = true
+  await em.patch(Access, accessProfileStore.currentAccessId, { activityYear: activityYear })
+  await em.refreshProfile()
 
   window.location.reload()
 }
-
 </script>
 
 <style lang="scss">

+ 3 - 0
components/Layout/SubHeader/DataTiming.vue

@@ -35,6 +35,7 @@ import {Ref} from "@vue/reactivity";
 import {useEntityManager} from "~/composables/data/useEntityManager";
 import {useDisplay, useTheme} from "vuetify";
 import Access from "~/models/Access/Access";
+import {usePageStore} from "~/stores/page";
 
 // TODO: en v3.0.5, pas de solution documentée pour renseigner directement la couleur dans le template, à revoir
 const color = useTheme().current.value.colors['primary']
@@ -43,6 +44,7 @@ const { setDirty } = useFormStore()
 const accessProfileStore = useAccessProfileStore()
 const { em } = useEntityManager()
 const { mdAndUp } = useDisplay()
+const pageStore = usePageStore()
 
 const toggle = ref(null)
 
@@ -64,6 +66,7 @@ const onUpdate = async (newValue: Array<string>) => {
   )
 
   setDirty(false)
+  pageStore.loading = true
 
   await em.patch(
       Access,

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

@@ -37,6 +37,7 @@ import {WatchStopHandle} from "@vue/runtime-core";
 import {useEntityManager} from "~/composables/data/useEntityManager";
 import Access from "~/models/Access/Access";
 import DateUtils from "~/services/utils/dateUtils";
+import {usePageStore} from "~/stores/page";
 
 const btn: Ref = ref(null)
 const show: Ref<boolean> = ref(false)
@@ -44,6 +45,7 @@ const show: Ref<boolean> = ref(false)
 const { setDirty } = useFormStore()
 const accessProfileStore = useAccessProfileStore()
 const { em } = useEntityManager()
+const pageStore = usePageStore()
 
 const start = accessProfileStore.historical.dateStart
 const end = accessProfileStore.historical.dateEnd
@@ -66,6 +68,7 @@ const updateDateTimeRange = async (dates: Array<Date>): Promise<any> => {
     accessProfileStore.setHistorical(false, true, false)
   }
   setDirty(false)
+  pageStore.loading = true
 
   await em.patch(
       Access,

+ 14 - 2
components/Ui/Input/Text.vue

@@ -15,8 +15,8 @@ Champs de saisie de texte
     :error-messages="errorMessage || (fieldViolations ? $t(fieldViolations) : '')"
     :append-icon="type === 'password' ? (show ? 'mdi-eye' : 'mdi-eye-off') : ''"
     @click:append="show = !show"
-    @update:modelValue="$emit('update:modelValue', $event.target.value)"
-    @change="updateViolationState($event); $emit('change', $event)"
+    @update:model-value="onUpdate($event)"
+    @change="onChange($event)"
   />
 
 
@@ -117,6 +117,18 @@ const i18n = useI18n()
 const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
 
 const show = ref(false)
+
+const emit = defineEmits(['update:model-value', 'change'])
+
+const onUpdate = (event: string) => {
+    emit('update:model-value', event)
+}
+
+const onChange = (event: Event | undefined) => {
+    updateViolationState(event)
+    emit('change', event)
+}
+
 // const label = computed(() => {
 //   if (props.label)
 // })

+ 4 - 3
components/Ui/Xeditable/Text.vue

@@ -11,8 +11,7 @@ Utilisé par exemple pour le choix de l'année active
       <UiInputText
           class="ma-0 pa-0"
           :type="type"
-          :modelValue="inputValue"
-          @update="$emit('update:modelValue', $event.target.value)"
+          v-model="inputValue"
       />
 
       <v-icon
@@ -61,7 +60,9 @@ import {ref, Ref} from "@vue/reactivity";
 
   const update = () => {
     edit.value = false
-    if (inputValue.value !== props.data) { emit('update', inputValue.value) }
+    if (inputValue.value !== props.data) {
+        emit('update', inputValue.value)
+    }
   }
 
   const close = () => {

+ 1 - 2
composables/data/useEntityManager.ts

@@ -1,6 +1,5 @@
 import EntityManager from "~/services/data/entityManager";
 import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import ApiResource from "~/models/ApiResource";
 import {useRepo} from "pinia-orm";
 
 let entityManager: EntityManager | null = null
@@ -8,7 +7,7 @@ let entityManager: EntityManager | null = null
 export const useEntityManager = () => {
     if (entityManager === null) {
         const { apiRequestService } = useAp2iRequestService()
-        const getRepo = (model: typeof ApiResource) => useRepo(model)
+        const getRepo = useRepo
 
         entityManager = new EntityManager(apiRequestService, getRepo)
     }

+ 2 - 2
composables/form/useFieldViolation.ts

@@ -1,6 +1,6 @@
 import {computed, ComputedRef} from "@vue/reactivity";
 import {useFormStore} from "~/stores/form";
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 /**
  * Composable pour gérer l'apparition de message d'erreurs de validation d'un champ de formulaire
@@ -19,7 +19,7 @@ export function useFieldViolation(field: string) {
    */
   function updateViolationState(field: string, value: any) {
     //@ts-ignore
-    useFormStore().setViolations(useOmit(useFormStore().violations, field))
+    useFormStore().setViolations(_.omit(useFormStore().violations, field))
   }
 
   return {

+ 1 - 0
layouts/default.vue

@@ -4,6 +4,7 @@
     <client-only placeholder-tag="client-only-placeholder" placeholder=" " />
 
     <v-app>
+      <LayoutLoadingScreen />
 
       <LayoutHeader />
 

+ 1 - 1
models/Access/MyProfile.ts

@@ -13,7 +13,7 @@ export default class MyProfile extends ApiResource {
   static entity = 'my_profile'
 
   @Uid()
-  declare id: number | string | null
+  declare id: number | string
 
   @Bool(false)
   declare isAdminAccess: boolean

+ 1 - 1
package.json

@@ -47,7 +47,7 @@
     "vite-plugin-vuetify": "^1.0.1",
     "vue-tel-input-vuetify": "^1.5.3",
     "vue-the-mask": "^0.11.1",
-    "vuetify": "3.1.9",
+    "vuetify": "3.1.15",
     "yaml-import": "^2.0.0"
   },
   "devDependencies": {

+ 27 - 6
services/data/entityManager.ts

@@ -9,7 +9,7 @@ import {v4 as uuid4} from 'uuid'
 import {AssociativeArray, Collection} from "~/types/data.d"
 import models from "~/models/models";
 import {useAccessProfileStore} from "~/stores/accessProfile"
-import _ from "lodash"
+import * as _ from "lodash-es"
 
 /**
  * Entity manager: make operations on the models defined with the Pinia-Orm library
@@ -118,10 +118,21 @@ class EntityManager {
      */
     public save(model: typeof ApiResource, instance: ApiResource, permanent: boolean = false): ApiResource {
         instance = this.cast(model, instance)
+        if (model === MyProfile) {
+            console.log(instance)
+        }
         if (permanent) {
             this.saveInitialState(model, instance)
         }
-        return this.getRepository(model).save(instance)
+
+        const repository = this.getRepository(model)
+        repository.save(instance)
+
+        instance = repository.find(instance.id) as ApiResource
+        if (model === MyProfile) {
+            console.log(instance)
+        }
+        return instance
     }
 
     /**
@@ -161,6 +172,7 @@ class EntityManager {
 
         // deserialize the response
         const attributes = HydraDenormalizer.denormalize(response).data as object
+
         return this.newInstance(model, attributes)
     }
 
@@ -300,12 +312,21 @@ class EntityManager {
      *
      * Re-fetch the user profile and update the store
      */
-    public async refreshProfile(accessId: number) {
-        const profile = await this.fetch(MyProfile, accessId)
+    public async refreshProfile(accessId: number | null = null) {
+        const accessProfileStore = useAccessProfileStore()
+
+        if (accessId === null) {
+            accessId = accessProfileStore.currentAccessId
+        }
+
+        // Sans le flush, on observe un bug non-expliqué au rechargement de la page en mode dev : la fonction save
+        //  du repo de MyProfile ne fonctionne pas quand le plugin init.server.ts re-fetch le profil
+        this.flush(MyProfile)
+
+        const profile = await this.fetch(MyProfile, accessId, true)
 
         // On met à jour le store accessProfile
-        const accessProfile = useAccessProfileStore()
-        accessProfile.setProfile(profile)
+        accessProfileStore.setProfile(profile)
     }
 
     /**

+ 1 - 1
services/layout/menuBuilder/myAccessesMenuBuilder.ts

@@ -1,7 +1,7 @@
 import AbstractMenuBuilder from '~/services/layout/menuBuilder/abstractMenuBuilder'
 import {MenuGroup, MenuItems} from "~/types/layout";
 import {MENU_LINK_TYPE} from "~/types/enum/layout";
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 /**
  * Menu Mon Profil

+ 1 - 1
services/layout/menuBuilder/myFamilyMenuBuilder.ts

@@ -1,7 +1,7 @@
 import AbstractMenuBuilder from '~/services/layout/menuBuilder/abstractMenuBuilder'
 import {MenuGroup, MenuItems} from "~/types/layout";
 import {MENU_LINK_TYPE} from "~/types/enum/layout";
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 /**
  * Menu Famille

+ 1 - 1
services/layout/menuBuilder/websiteListMenuBuilder.ts

@@ -1,7 +1,7 @@
 import AbstractMenuBuilder from '~/services/layout/menuBuilder/abstractMenuBuilder'
 import {MenuGroup, MenuItems} from "~/types/layout";
 import {MENU_LINK_TYPE} from "~/types/enum/layout";
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 /**
  * Menu : Liste des sites internet de la structure et de ses structures parentes

+ 1 - 1
services/rights/abilityBuilder.ts

@@ -3,7 +3,7 @@ import {AbilitiesType} from '~/types/interfaces'
 import {MongoAbility} from "@casl/ability/dist/types/Ability";
 import {ABILITIES} from "~/types/enum/enums";
 import yaml from "yaml-import";
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 interface Condition {
     function: string

+ 1 - 1
services/rights/roleUtils.ts

@@ -1,6 +1,6 @@
 import { AbilitiesType } from '~/types/interfaces'
 import {AnyJson} from "~/types/data"
-import * as _ from 'lodash'
+import * as _ from 'lodash-es'
 
 // TODO: peut-être passer ces constantes dans la config?
 const rolesByFunction: Array<string> = [

+ 1 - 1
stores/accessProfile.ts

@@ -61,7 +61,7 @@ export const useAccessProfileStore = defineStore('accessProfile', () => {
    * @return {boolean}
    */
   const currentAccessId = computed((): number => {
-    return switchId.value ?? (id.value ?? 0)
+    return (!switchId.value || isNaN(switchId.value)) ? (id.value ?? 0) : switchId.value
   })
 
   // Actions

+ 2 - 0
stores/page.ts

@@ -5,6 +5,7 @@ import {Ref, ref} from "@vue/reactivity";
 
 export const usePageStore = defineStore('page', () => {
     const alerts: Ref<Array<Alert>> = ref([])
+    const loading: Ref<Boolean> = ref(false)
 
     const removeSlowlyAlert = () => {
       setTimeout(() => {
@@ -27,6 +28,7 @@ export const usePageStore = defineStore('page', () => {
 
     return {
         alerts,
+        loading,
         removeSlowlyAlert,
         addAlert
     }