Olivier Massot před 1 rokem
rodič
revize
90f7bcf977

+ 1 - 1
.eslintrc.cjs

@@ -31,7 +31,7 @@ module.exports = {
       },
       },
     ],
     ],
     'vue/multi-word-component-names': 0,
     'vue/multi-word-component-names': 0,
-    '@typescript-eslint/no-inferrable-types': 0
+    '@typescript-eslint/no-inferrable-types': 0,
   },
   },
   globals: {
   globals: {
     useRuntimeConfig: 'readonly',
     useRuntimeConfig: 'readonly',

+ 7 - 7
components/Layout/Dialog.vue

@@ -100,13 +100,13 @@ const _show = computed(() => props.show) as boolean
   }
   }
 }
 }
 
 
-  .modal-level-warning {
-    .dialog-type {
-      background: rgb(var(--v-theme-warning, #f39c12));
-    }
+.modal-level-warning {
+  .dialog-type {
+    background: rgb(var(--v-theme-warning, #f39c12));
   }
   }
+}
 
 
-  :deep(.v-card-actions) {
-    min-height: 62px;
-  }
+:deep(.v-card-actions) {
+  min-height: 62px;
+}
 </style>
 </style>

+ 11 - 14
components/Layout/Header/Notification.vue

@@ -94,8 +94,8 @@ import type { AnyJson, Pagination } from '~/types/data'
 import { useEntityManager } from '~/composables/data/useEntityManager'
 import { useEntityManager } from '~/composables/data/useEntityManager'
 import UrlUtils from '~/services/utils/urlUtils'
 import UrlUtils from '~/services/utils/urlUtils'
 import NotificationRepository from '~/stores/repositories/NotificationRepository'
 import NotificationRepository from '~/stores/repositories/NotificationRepository'
-import Query from "~/services/data/Query";
-import PageFilter from "~/services/data/Filters/PageFilter";
+import Query from '~/services/data/Query'
+import PageFilter from '~/services/data/Filters/PageFilter'
 
 
 const accessProfileStore = useAccessProfileStore()
 const accessProfileStore = useAccessProfileStore()
 
 
@@ -112,9 +112,7 @@ const { em } = useEntityManager()
 const { fetchCollection } = useEntityFetch()
 const { fetchCollection } = useEntityFetch()
 const notificationRepo = useRepo(NotificationRepository)
 const notificationRepo = useRepo(NotificationRepository)
 
 
-const query = new Query(
-  new PageFilter(page, itemsPerPage)
-)
+const query = new Query(new PageFilter(page, itemsPerPage))
 
 
 const {
 const {
   data: collection,
   data: collection,
@@ -140,14 +138,12 @@ const unreadNotification: ComputedRef<Array<Notification>> = computed(() => {
  * Les metadata dépendront de la dernière valeur du GET lancé
  * Les metadata dépendront de la dernière valeur du GET lancé
  */
  */
 const pagination: ComputedRef<Pagination> = computed(() => {
 const pagination: ComputedRef<Pagination> = computed(() => {
-  return collection.value !== null
-    ? collection.value.pagination
-    : {}
+  return collection.value !== null ? collection.value.pagination : {}
 })
 })
 
 
 const notificationUrl = UrlUtils.join(
 const notificationUrl = UrlUtils.join(
   runtimeConfig.baseUrlAdminLegacy,
   runtimeConfig.baseUrlAdminLegacy,
-  '#/notifications/list/'
+  '#/notifications/list/',
 )
 )
 
 
 /**
 /**
@@ -191,14 +187,15 @@ const getMessage = (notification: Notification) => {
   switch (notification.type) {
   switch (notification.type) {
     case NOTIFICATION_TYPE.FILE:
     case NOTIFICATION_TYPE.FILE:
       return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t(
       return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t(
-        'is_ready_to_be_downloaded'
+        'is_ready_to_be_downloaded',
       )}`
       )}`
 
 
     case NOTIFICATION_TYPE.MESSAGE:
     case NOTIFICATION_TYPE.MESSAGE:
       if (notification.message?.action)
       if (notification.message?.action)
-        return `${i18n.t('your_message')} ${
-          notification.message?.fileName
-        } ${i18n.t('is_ready_to_be')} ${notification.message.action}`
+        return `${i18n.t('your_message')} ${notification.message
+          ?.fileName} ${i18n.t('is_ready_to_be')} ${
+          notification.message.action
+        }`
 
 
       return `${i18n.t('your_message')} ${
       return `${i18n.t('your_message')} ${
         notification.message?.about ?? ''
         notification.message?.about ?? ''
@@ -272,7 +269,7 @@ const download = (link: string) => {
     'api',
     'api',
     String(accessProfileStore.id),
     String(accessProfileStore.id),
     String(accessProfileStore.switchId || ''),
     String(accessProfileStore.switchId || ''),
-    path
+    path,
   )
   )
 
 
   window.open(url)
   window.open(url)

+ 1 - 4
components/Ui/Collection.vue

@@ -49,8 +49,5 @@ const { model, parent }: ToRefs = toRefs(props)
 
 
 const { fetchCollection } = useEntityFetch()
 const { fetchCollection } = useEntityFetch()
 
 
-const { data: collection, pending } = fetchCollection(
-  model.value,
-  parent.value,
-)
+const { data: collection, pending } = fetchCollection(model.value, parent.value)
 </script>
 </script>

+ 1 - 4
components/Ui/Form.vue

@@ -92,10 +92,7 @@ de quitter si des données ont été modifiées.
 <script setup lang="ts">
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue'
 import { computed, ref, watch } from 'vue'
 import type { ComputedRef, Ref, PropType } from 'vue'
 import type { ComputedRef, Ref, PropType } from 'vue'
-import type {
-  RouteLocationNormalized,
-  RouteLocationRaw,
-} from 'vue-router'
+import type { RouteLocationNormalized, RouteLocationRaw } from 'vue-router'
 import * as _ from 'lodash-es'
 import * as _ from 'lodash-es'
 import { FORM_FUNCTION, SUBMIT_TYPE, TYPE_ALERT } from '~/types/enum/enums'
 import { FORM_FUNCTION, SUBMIT_TYPE, TYPE_ALERT } from '~/types/enum/enums'
 import { useFormStore } from '~/stores/form'
 import { useFormStore } from '~/stores/form'

+ 6 - 3
components/Ui/Form/Edition.vue

@@ -24,9 +24,9 @@ import ApiModel from '~/models/ApiModel'
 import type { AnyJson } from '~/types/data'
 import type { AnyJson } from '~/types/data'
 import { SUBMIT_TYPE } from '~/types/enum/enums'
 import { SUBMIT_TYPE } from '~/types/enum/enums'
 import { useRoute } from 'vue-router'
 import { useRoute } from 'vue-router'
-import type { RouteLocationRaw} from 'vue-router'
+import type { RouteLocationRaw } from 'vue-router'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -78,7 +78,10 @@ const router = useRouter()
 const entityId =
 const entityId =
   props.id !== null ? props.id : parseInt(route.params.id as string)
   props.id !== null ? props.id : parseInt(route.params.id as string)
 
 
-const { data: entity, pending } = fetch(props.model, entityId) as AsyncData<ApiModel, Error | null>
+const { data: entity, pending } = fetch(props.model, entityId) as AsyncData<
+  ApiModel,
+  Error | null
+>
 
 
 const submitActions = computed(() => {
 const submitActions = computed(() => {
   let actions: AnyJson = {}
   let actions: AnyJson = {}

+ 29 - 41
doc/entity_manager.md

@@ -1,11 +1,11 @@
 # Entity Manager
 # Entity Manager
 
 
-L'entity manager est la classe au coeur du requêtage des données. Il assure la liaison entre l'api et 
+L'entity manager est la classe au coeur du requêtage des données. Il assure la liaison entre l'api et
 le store pinia-orm.
 le store pinia-orm.
 
 
 ## Modèles et entités
 ## Modèles et entités
 
 
-Les modèles d'entités sont définis dans le répertoire `~/models`, sous forme de modèles 
+Les modèles d'entités sont définis dans le répertoire `~/models`, sous forme de modèles
 [Pinia-ORM](https://pinia-orm.codedredd.de/guide/model/getting-started) et en correspondance
 [Pinia-ORM](https://pinia-orm.codedredd.de/guide/model/getting-started) et en correspondance
 avec leur définition côté API.
 avec leur définition côté API.
 
 
@@ -20,7 +20,7 @@ Ces deux classes correspondent aux ApiResources et aux Models de Api-Platform.
 
 
 ## Format Json-Ld (Hydra) et normalizer
 ## Format Json-Ld (Hydra) et normalizer
 
 
-L'API renvoie ses données au format Json-Ld. La classe `hydraNormalizer` s'occupe ensuite de transformer ces données 
+L'API renvoie ses données au format Json-Ld. La classe `hydraNormalizer` s'occupe ensuite de transformer ces données
 dans un format compréhensible par l'entity manager.
 dans un format compréhensible par l'entity manager.
 
 
 Les entités simples sont castées en une instance de leur modèle.
 Les entités simples sont castées en une instance de leur modèle.
@@ -28,7 +28,7 @@ Les collections sont parsées en une liste d'instances de leur modèle.
 
 
 Les métadonnées, en particulier en ce qui concerne la pagination, sont aussi extraites.
 Les métadonnées, en particulier en ce qui concerne la pagination, sont aussi extraites.
 
 
-Le résultat est retourné sous la forme d'un objet de la forme : 
+Le résultat est retourné sous la forme d'un objet de la forme :
 
 
     {
     {
       data: [...],
       data: [...],
@@ -37,16 +37,16 @@ Le résultat est retourné sous la forme d'un objet de la forme :
 
 
 ### Les champs IriEncoded
 ### Les champs IriEncoded
 
 
-Les relations entre les entités sont fournies par l'API sous forme d'IRI. De même, lors des opérations PUT/POST/PATCH, 
+Les relations entre les entités sont fournies par l'API sous forme d'IRI. De même, lors des opérations PUT/POST/PATCH,
 celle-ci attend aussi des IRI pour représenter ces relations.
 celle-ci attend aussi des IRI pour représenter ces relations.
 
 
 Hors le choix a été fait de stocker les ids des entités sous leurs formes numériques dans les stores Pinia-ORM.
 Hors le choix a été fait de stocker les ids des entités sous leurs formes numériques dans les stores Pinia-ORM.
 
 
 Pour permettre cette inter-opérabilité dans les deux sens, on utilise le décorateur `IriEncoded`, qui va permettre
 Pour permettre cette inter-opérabilité dans les deux sens, on utilise le décorateur `IriEncoded`, qui va permettre
-de signaler au normalizer qu'une propriété doit être décodée en entrée (son id sera extrait au format numérique), et 
+de signaler au normalizer qu'une propriété doit être décodée en entrée (son id sera extrait au format numérique), et
 qu'elle doit être retransformée en IRI dans l'autre sens.
 qu'elle doit être retransformée en IRI dans l'autre sens.
 
 
-Exemple d'utilisation (classe `Access.ts`): 
+Exemple d'utilisation (classe `Access.ts`):
 
 
     @IriEncoded(Organization)
     @IriEncoded(Organization)
     declare organization: number | null
     declare organization: number | null
@@ -55,7 +55,7 @@ Exemple d'utilisation (classe `Access.ts`):
 
 
 ### Créer une nouvelle entité (non persistée)
 ### Créer une nouvelle entité (non persistée)
 
 
-On créé une nouvelle instance d'entité en se servant de la méthode `newInstance`, à laquelle on peut passer les 
+On créé une nouvelle instance d'entité en se servant de la méthode `newInstance`, à laquelle on peut passer les
 propriétés du nouvel objet sous forme d'objet JS.
 propriétés du nouvel objet sous forme d'objet JS.
 
 
     const newOrganizationInstance = em.newInstance(Organization)
     const newOrganizationInstance = em.newInstance(Organization)
@@ -75,34 +75,34 @@ Une entité existante ou nouvellement crée peut-être persistée côté API. Po
 
 
 C'est entre autres la méthode utilisée par les formulaires lors de l'action "enregistrer".
 C'est entre autres la méthode utilisée par les formulaires lors de l'action "enregistrer".
 
 
-**Attention** : Lorsqu'on persiste une entité, l'entity manager va utiliser les données de retour de la requête envoyée 
-à l'API, et mettre à jour le store en fonction (et ce afin d'éviter des écarts involontaires entre les données front 
-et back). Ce qui veut dire que selon le traitement effectué par l'API, l'entité envoyée et celle qui résulte 
+**Attention** : Lorsqu'on persiste une entité, l'entity manager va utiliser les données de retour de la requête envoyée
+à l'API, et mettre à jour le store en fonction (et ce afin d'éviter des écarts involontaires entre les données front
+et back). Ce qui veut dire que selon le traitement effectué par l'API, l'entité envoyée et celle qui résulte
 de l'opération peuvent différer.
 de l'opération peuvent différer.
 
 
 ### Fetch une entité simple
 ### Fetch une entité simple
 
 
-Si l'entité a déjà été fetchée, et que l'argument `forceRefresh` est faux, l'entité est simplement retournée depuis le 
+Si l'entité a déjà été fetchée, et que l'argument `forceRefresh` est faux, l'entité est simplement retournée depuis le
 store.
 store.
 
 
 Sinon, l'opération fetch se déroule en deux temps.
 Sinon, l'opération fetch se déroule en deux temps.
 
 
-D'abord, une requête GET est envoyée à l'API au moyen de la classe ApiRequestService (surcouche de la librairie 
+D'abord, une requête GET est envoyée à l'API au moyen de la classe ApiRequestService (surcouche de la librairie
 ohfetch). Le résultat de cette requête est converti en ApiResource par le normalizer, puis enregistrée dans le store.
 ohfetch). Le résultat de cette requête est converti en ApiResource par le normalizer, puis enregistrée dans le store.
 
 
-On la récupère ensuite dans le store sous forme de référence avant de la retourner. En effet, si on retournait 
+On la récupère ensuite dans le store sous forme de référence avant de la retourner. En effet, si on retournait
 directement le résultat de l'appel à l'API plutôt qu'une référence au store Pinia-ORM, on perdrait la réactivité.
 directement le résultat de l'appel à l'API plutôt qu'une référence au store Pinia-ORM, on perdrait la réactivité.
 
 
 ### Fetch une Collection
 ### Fetch une Collection
 
 
 Fetcher une collection est plus compliqué. La requête implique des conditions de filtre, de tri, de pagination.
 Fetcher une collection est plus compliqué. La requête implique des conditions de filtre, de tri, de pagination.
-Afin de pouvoir garder un lien entre les résultats retournés par l'API et les résultats à aller 
-ensuite chercher dans le store, il faut un système permettant de passer les mêmes conditions à ces deux 
+Afin de pouvoir garder un lien entre les résultats retournés par l'API et les résultats à aller
+ensuite chercher dans le store, il faut un système permettant de passer les mêmes conditions à ces deux
 interfaces (API / PiniaOrm). C'est le rôle de la classe Query et des Filters (voir plus bas).
 interfaces (API / PiniaOrm). C'est le rôle de la classe Query et des Filters (voir plus bas).
 
 
-Une fois définies les conditions au moyen de l'objet Query, l'appel à fetchCollection se passe de la même manière 
-que lors de l'appel à la méthode `fetch` (appel, puis récupération des résultats dans le store). La seule nuance 
-est que le résultat est un objet Collection, incluant les résultats sous la forme d'une référence 
+Une fois définies les conditions au moyen de l'objet Query, l'appel à fetchCollection se passe de la même manière
+que lors de l'appel à la méthode `fetch` (appel, puis récupération des résultats dans le store). La seule nuance
+est que le résultat est un objet Collection, incluant les résultats sous la forme d'une référence
 calculée (`ComputedRef`), et les données de pagination :
 calculée (`ComputedRef`), et les données de pagination :
 
 
     {
     {
@@ -118,28 +118,28 @@ calculée (`ComputedRef`), et les données de pagination :
 
 
 #### L'objet Query et les Filters
 #### L'objet Query et les Filters
 
 
-Les filtres, les tris et la pagination sont donc empaquetés dans un objet Query : 
+Les filtres, les tris et la pagination sont donc empaquetés dans un objet Query :
 
 
     const query = new Query()
     const query = new Query()
 
 
-On pourra ensuite ajouter des filtres à cette query : 
+On pourra ensuite ajouter des filtres à cette query :
 
 
     query.add(new SearchFilter('name', searchFilter, SEARCH_STRATEGY.IPARTIAL))
     query.add(new SearchFilter('name', searchFilter, SEARCH_STRATEGY.IPARTIAL))
     query.add(new OrderBy('name', ORDER_BY_DIRECTION.ASC))
     query.add(new OrderBy('name', ORDER_BY_DIRECTION.ASC))
     query.add(new PageFilter(page, itemsPerPage))
     query.add(new PageFilter(page, itemsPerPage))
 
 
-Cette query traduira ensuite ces conditions en URL query pour l'appel à l'API d'une part, 
+Cette query traduira ensuite ces conditions en URL query pour l'appel à l'API d'une part,
 et en query PiniaORM d'autre part.
 et en query PiniaORM d'autre part.
 
 
 > NB : les filtres de type 'where' seront toujours appliqués en premier, puis les tris, et enfin la pagination.
 > NB : les filtres de type 'where' seront toujours appliqués en premier, puis les tris, et enfin la pagination.
 
 
 ### Reset une entité
 ### Reset une entité
 
 
-L'entity manager stocke dans le store un clone non modifié de chaque entité fetchée depuis l'API. Ces clones sont 
-enregistrés avec des ids préfixés par `__clone__`, et ils sont automatiquement exclus des requêtes Pinia-ORM 
+L'entity manager stocke dans le store un clone non modifié de chaque entité fetchée depuis l'API. Ces clones sont
+enregistrés avec des ids préfixés par `__clone__`, et ils sont automatiquement exclus des requêtes Pinia-ORM
 lorsque celles-ci sont construites au moyen de la méthode `getQuery` de l'entity manager.
 lorsque celles-ci sont construites au moyen de la méthode `getQuery` de l'entity manager.
 
 
-La méthode `reset` permettra de réinitialiser une entité modifiée dans le store pour la ramener à l'état qu'elle 
+La méthode `reset` permettra de réinitialiser une entité modifiée dans le store pour la ramener à l'état qu'elle
 avait la dernière fois que l'API l'a renvoyée.
 avait la dernière fois que l'API l'a renvoyée.
 
 
 ### Supprimer une entité
 ### Supprimer une entité
@@ -147,7 +147,6 @@ avait la dernière fois que l'API l'a renvoyée.
 On peut supprimer une entité au moyen de la méthode `delete` de l'entity manager.
 On peut supprimer une entité au moyen de la méthode `delete` de l'entity manager.
 Si l'entité existe dans l'API, une requête de suppression sera envoyée à celle ci.
 Si l'entité existe dans l'API, une requête de suppression sera envoyée à celle ci.
 
 
-
 ## EnumManager et ImageManager
 ## EnumManager et ImageManager
 
 
 ### EnumManager
 ### EnumManager
@@ -156,36 +155,25 @@ La classe EnumManager permettra de fetcher auprès de l'API des Enum, et de les
 
 
 ### ImageManager
 ### ImageManager
 
 
-La classe ImageManger donnera accès à des méthodes permettant de télécharger une image depuis l'API et de la retourner 
+La classe ImageManger donnera accès à des méthodes permettant de télécharger une image depuis l'API et de la retourner
 sous forme de Base64, ou d'uploader une image.
 sous forme de Base64, ou d'uploader une image.
 
 
-
 ## Composables et useAsyncData
 ## Composables et useAsyncData
 
 
 ### Utiliser les services dans Vue
 ### Utiliser les services dans Vue
 
 
-Les différentes classes de manager, ainsi que le service de requête à l'API, sont disponibles sous forme de 
+Les différentes classes de manager, ainsi que le service de requête à l'API, sont disponibles sous forme de
 composables : `useEntityManger`, `useImageManager`, ...etc.
 composables : `useEntityManger`, `useImageManager`, ...etc.
 
 
-Exemple : 
+Exemple :
 
 
     const ap2iRequestService = useAp2iRequestService()
     const ap2iRequestService = useAp2iRequestService()
 
 
 ### Fetch avec useAsyncData
 ### Fetch avec useAsyncData
 
 
-Les composables `useEntityFetch`, `useEnumFetch` et `useImageFetch` permettent d'accéder aux différentes 
+Les composables `useEntityFetch`, `useEnumFetch` et `useImageFetch` permettent d'accéder aux différentes
 méthodes `fetch` des managers, sous la forme d'un appel à [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data).
 méthodes `fetch` des managers, sous la forme d'un appel à [useAsyncData](https://nuxt.com/docs/api/composables/use-async-data).
 
 
 Exemple d'utilisation :
 Exemple d'utilisation :
 
 
     const { data: parameters, pending, refresh } = fetch(Parameters, 123)
     const { data: parameters, pending, refresh } = fetch(Parameters, 123)
-
-
-    
-
-
-
-
-
-
-

+ 7 - 2
types/data.d.ts

@@ -1,6 +1,9 @@
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
 import type { EnumChoice } from '~/types/interfaces'
 import type { EnumChoice } from '~/types/interfaces'
-import type {Query as PiniaOrmQuery, Collection as PiniaOrmCollection} from "pinia-orm";
+import type {
+  Query as PiniaOrmQuery,
+  Collection as PiniaOrmCollection,
+} from 'pinia-orm'
 
 
 type AnyJson = Record<string, any>
 type AnyJson = Record<string, any>
 
 
@@ -51,7 +54,9 @@ interface Collection {
 }
 }
 
 
 interface ApiFilter {
 interface ApiFilter {
-  applyToPiniaOrmQuery: (query: PiniaOrmQuery<ApiResource>) => PiniaOrmQuery<ApiResource>
+  applyToPiniaOrmQuery: (
+    query: PiniaOrmQuery<ApiResource>,
+  ) => PiniaOrmQuery<ApiResource>
   getApiQueryPart: () => string
   getApiQueryPart: () => string
 }
 }