소스 검색

apply prettier fixes

Olivier Massot 1 년 전
부모
커밋
d6ee61954c
100개의 변경된 파일3504개의 추가작업 그리고 3074개의 파일을 삭제
  1. 28 33
      .eslintrc.cjs
  2. 1 1
      .gitlab-ci.yml
  3. 1 0
      .prettierignore
  4. 13 23
      README.md
  5. 2 2
      assets/css/global.scss
  6. 1 1
      assets/css/settings.scss
  7. 0 1
      assets/css/theme.scss
  8. 12 13
      components/Layout/Alert/Container.vue
  9. 9 11
      components/Layout/Alert/Content.vue
  10. 10 6
      components/Layout/AlertBar.vue
  11. 43 24
      components/Layout/AlertBar/Cotisation.vue
  12. 7 7
      components/Layout/AlertBar/Env.vue
  13. 17 16
      components/Layout/AlertBar/OnlineRegistration.vue
  14. 17 16
      components/Layout/AlertBar/RegistrationStatus.vue
  15. 34 26
      components/Layout/AlertBar/SuperAdmin.vue
  16. 11 8
      components/Layout/AlertBar/SwitchUser.vue
  17. 50 43
      components/Layout/AlertBar/SwitchYear.vue
  18. 10 10
      components/Layout/BannerTop.vue
  19. 3 3
      components/Layout/Container.vue
  20. 42 40
      components/Layout/Dialog.vue
  21. 45 44
      components/Layout/Header.vue
  22. 11 13
      components/Layout/Header/HomeBtn.vue
  23. 57 57
      components/Layout/Header/Menu.vue
  24. 90 85
      components/Layout/Header/Notification.vue
  25. 89 89
      components/Layout/Header/UniversalCreation/Card.vue
  26. 153 137
      components/Layout/Header/UniversalCreation/CreateButton.vue
  27. 72 62
      components/Layout/Header/UniversalCreation/EventParams.vue
  28. 190 178
      components/Layout/Header/UniversalCreation/GenerateCardsSteps.vue
  29. 15 18
      components/Layout/LoadingScreen.vue
  30. 74 75
      components/Layout/MainMenu.vue
  31. 101 98
      components/Layout/ParametersMenu.vue
  32. 45 35
      components/Layout/SubHeader/ActivityYear.vue
  33. 12 12
      components/Layout/SubHeader/Breadcrumbs.vue
  34. 44 30
      components/Layout/SubHeader/DataTiming.vue
  35. 25 18
      components/Layout/SubHeader/DataTimingRange.vue
  36. 45 39
      components/Layout/SubHeader/PersonnalizedList.vue
  37. 55 42
      components/Layout/Subheader.vue
  38. 14 14
      components/Layout/ThemeSwitcher.vue
  39. 27 27
      components/Ui/Button/Delete.vue
  40. 24 23
      components/Ui/Button/Submit.vue
  41. 8 16
      components/Ui/Card.vue
  42. 18 16
      components/Ui/Collection.vue
  43. 17 29
      components/Ui/DataTable.vue
  44. 25 28
      components/Ui/DatePicker.vue
  45. 69 66
      components/Ui/DateRangePicker.vue
  46. 30 30
      components/Ui/ExpansionPanel.vue
  47. 52 51
      components/Ui/Form.vue
  48. 13 21
      components/Ui/Form/Creation.vue
  49. 22 28
      components/Ui/Form/Edition.vue
  50. 10 8
      components/Ui/Help.vue
  51. 58 50
      components/Ui/Image.vue
  52. 92 67
      components/Ui/Input/Autocomplete.vue
  53. 62 50
      components/Ui/Input/Autocomplete/Accesses.vue
  54. 44 45
      components/Ui/Input/AutocompleteWithAPI.vue
  55. 37 28
      components/Ui/Input/AutocompleteWithAp2i.vue
  56. 31 28
      components/Ui/Input/AutocompleteWithEnum.vue
  57. 11 13
      components/Ui/Input/Checkbox.vue
  58. 16 19
      components/Ui/Input/Combobox.vue
  59. 20 22
      components/Ui/Input/DatePicker.vue
  60. 14 18
      components/Ui/Input/Email.vue
  61. 20 21
      components/Ui/Input/Enum.vue
  62. 147 127
      components/Ui/Input/Image.vue
  63. 34 21
      components/Ui/Input/Number.vue
  64. 28 24
      components/Ui/Input/Phone.vue
  65. 33 23
      components/Ui/Input/Text.vue
  66. 23 25
      components/Ui/Input/TextArea.vue
  67. 14 17
      components/Ui/ItemFromUri.vue
  68. 4 14
      components/Ui/LoadingPanel.vue
  69. 38 36
      components/Ui/SystemBar.vue
  70. 6 15
      components/Ui/Template/DataTable.vue
  71. 5 5
      components/Ui/Template/Date.vue
  72. 39 44
      components/Ui/Xeditable/Text.vue
  73. 2 3
      config/abilities/config.yaml
  74. 52 31
      config/abilities/pages/addressBook.yaml
  75. 40 31
      config/abilities/pages/admin2ios.yaml
  76. 88 46
      config/abilities/pages/billing.yaml
  77. 27 27
      config/abilities/pages/communication.yaml
  78. 176 101
      config/abilities/pages/cotisations.yaml
  79. 8 5
      config/abilities/pages/donor.yaml
  80. 68 35
      config/abilities/pages/educational.yaml
  81. 8 5
      config/abilities/pages/equipment.yaml
  82. 8 5
      config/abilities/pages/medals.yaml
  83. 87 68
      config/abilities/pages/myAccount.yaml
  84. 194 122
      config/abilities/pages/parameters.yaml
  85. 50 35
      config/abilities/pages/schedule.yaml
  86. 38 20
      config/abilities/pages/stats.yaml
  87. 7 14
      doc/abilities.md
  88. 8 18
      doc/colors.md
  89. 2 6
      doc/unittests.md
  90. 27 33
      env/setupEnv.mjs
  91. 5 5
      lang/fr.json
  92. 1 1
      layouts/.eslintrc.cjs
  93. 5 3
      package.json
  94. 1 1
      pages/.eslintrc.cjs
  95. 5 5
      tests/.eslintrc.cjs
  96. 0 1
      tests/units/readme.md
  97. 2 7
      tsconfig.json
  98. 29 30
      types/data.d.ts
  99. 9 13
      types/interfaces.d.ts
  100. 18 18
      types/layout.d.ts

+ 28 - 33
.eslintrc.cjs

@@ -2,49 +2,44 @@ module.exports = {
   root: true,
   root: true,
   env: {
   env: {
     browser: true,
     browser: true,
-    node: true
+    node: true,
   },
   },
-  parser: "vue-eslint-parser",
+  parser: 'vue-eslint-parser',
   parserOptions: {
   parserOptions: {
-    "ecmaVersion": 2020,
-    "parser": "@typescript-eslint/parser",
-    "sourceType": "module"
+    ecmaVersion: 2020,
+    parser: '@typescript-eslint/parser',
+    sourceType: 'module',
   },
   },
   extends: [
   extends: [
     '@nuxtjs/eslint-config-typescript',
     '@nuxtjs/eslint-config-typescript',
     'plugin:nuxt/recommended',
     'plugin:nuxt/recommended',
-    "eslint:recommended",
-    "plugin:@typescript-eslint/recommended",
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
     'plugin:vue/vue3-recommended',
     'plugin:vue/vue3-recommended',
     'plugin:prettier/recommended',
     'plugin:prettier/recommended',
-
-  ],
-  ignorePatterns: [
-    ".nuxt",
-    "coverage/*",
-    "vendor/*",
-    "dist/*",
-  ],
-  plugins: [
-    "vue",
-    "@typescript-eslint"
   ],
   ],
+  ignorePatterns: ['.nuxt', 'coverage/*', 'vendor/*', 'dist/*'],
+  plugins: ['vue', '@typescript-eslint'],
   // add your custom rules here
   // add your custom rules here
   rules: {
   rules: {
-    "no-console": 0, // on autorise les appels à la console (puisque ceux ci seront de toute façon nettoyés à la compilation)
-    'vue/valid-v-slot': ['error', {
-      allowModifiers: true,
-    }],
+    'prettier/prettier': 'error',
+    'no-console': 0, // on autorise les appels à la console (puisque ceux-ci seront de toute façon nettoyés à la compilation)
+    'vue/valid-v-slot': [
+      'error',
+      {
+        allowModifiers: true,
+      },
+    ],
+  },
+  globals: {
+    useRuntimeConfig: 'readonly',
+    navigateTo: 'readonly',
+    computed: 'readonly',
+    ref: 'readonly',
+    definePageMeta: 'readonly',
+    useRouter: 'readonly',
+    useRoute: 'readonly',
+    useI18n: 'readonly',
+    onMounted: 'readonly',
   },
   },
-  "globals": {
-    "useRuntimeConfig": "readonly",
-    "navigateTo": "readonly",
-    "computed": "readonly",
-    "ref": "readonly",
-    "definePageMeta": "readonly",
-    "useRouter": "readonly",
-    "useRoute": "readonly",
-    "useI18n": "readonly",
-    "onMounted": "readonly",
-  }
 }
 }

+ 1 - 1
.gitlab-ci.yml

@@ -14,7 +14,7 @@ cache:
 
 
 code_quality:
 code_quality:
   script:
   script:
-    - yarn eslint . --env=staging
+    - yarn eslint .
 
 
 code_style:
 code_style:
   script:
   script:

+ 1 - 0
.prettierignore

@@ -2,3 +2,4 @@
 coverage
 coverage
 node_modules
 node_modules
 dist
 dist
+assets/css/*.css

+ 13 - 23
README.md

@@ -2,25 +2,23 @@
 
 
 [![Latest Release](http://gitlab.2iopenservice.com/opentalent/app/-/badges/release.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/releases)
 [![Latest Release](http://gitlab.2iopenservice.com/opentalent/app/-/badges/release.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/releases)
 
 
-| Branch  | Status                                                                                                                                                                         | Coverage                                                                                                                                                                       |
-|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| Branch  | Status                                                                                                                                                             | Coverage                                                                                                                                                                       |
+| ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
 | master  | [![pipeline status](http://gitlab.2iopenservice.com/opentalent/app/badges/master/pipeline.svg)](http://gitlab.2iopenservice.com/opentalent/app/-/commits/master)   | [![coverage report](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/badges/master/coverage.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/commits/master)   |
 | master  | [![pipeline status](http://gitlab.2iopenservice.com/opentalent/app/badges/master/pipeline.svg)](http://gitlab.2iopenservice.com/opentalent/app/-/commits/master)   | [![coverage report](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/badges/master/coverage.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/commits/master)   |
 | develop | [![pipeline status](http://gitlab.2iopenservice.com/opentalent/app/badges/develop/pipeline.svg)](http://gitlab.2iopenservice.com/opentalent/app/-/commits/develop) | [![coverage report](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/badges/develop/coverage.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/commits/develop) |
 | develop | [![pipeline status](http://gitlab.2iopenservice.com/opentalent/app/badges/develop/pipeline.svg)](http://gitlab.2iopenservice.com/opentalent/app/-/commits/develop) | [![coverage report](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/badges/develop/coverage.svg)](http://gitlab.2iopenservice.com/opentalent/app_nuxt3/-/commits/develop) |
 
 
-
 Frontend Opentalent, avec NuxtJs 3
 Frontend Opentalent, avec NuxtJs 3
 
 
 A voir :
 A voir :
 
 
-* [vuejs.org](https://vuejs.org/guide/introduction.html) : Vue est le framework de base de l'application
-* [nuxtjs.org](https://nuxt.com/docs/getting-started/introduction) : Nuxt est une surcouche à Vue qui automatise et simplifie beaucoup de choses
-* [pinia.vuejs.org](https://pinia.vuejs.org/introduction.html) : Store library that allow you to share a state accross your components / pages
-* [pinia-orm.codedredd.de](https://pinia-orm.codedredd.de/guide/getting-started/quick-start) : Ajoute une gestion par modèles / repos au store Pinia
-* [vuetifyjs.com](https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-v3-slim-text-light.svg) : Composants graphiques préconstruits
-* [typescriptlang.org](https://www.typescriptlang.org/) : Typescript
-* [jestjs.io](https://jestjs.io/docs/getting-started) : Tests unitaires pour JS
-* [cypress.io](https://docs.cypress.io/guides/getting-started/installing-cypress) : Tests end-to-end pour JS
-
+- [vuejs.org](https://vuejs.org/guide/introduction.html) : Vue est le framework de base de l'application
+- [nuxtjs.org](https://nuxt.com/docs/getting-started/introduction) : Nuxt est une surcouche à Vue qui automatise et simplifie beaucoup de choses
+- [pinia.vuejs.org](https://pinia.vuejs.org/introduction.html) : Store library that allow you to share a state accross your components / pages
+- [pinia-orm.codedredd.de](https://pinia-orm.codedredd.de/guide/getting-started/quick-start) : Ajoute une gestion par modèles / repos au store Pinia
+- [vuetifyjs.com](https://cdn.vuetifyjs.com/docs/images/logos/vuetify-logo-v3-slim-text-light.svg) : Composants graphiques préconstruits
+- [typescriptlang.org](https://www.typescriptlang.org/) : Typescript
+- [jestjs.io](https://jestjs.io/docs/getting-started) : Tests unitaires pour JS
+- [cypress.io](https://docs.cypress.io/guides/getting-started/installing-cypress) : Tests end-to-end pour JS
 
 
 ## Installation (mode dev)
 ## Installation (mode dev)
 
 
@@ -28,23 +26,19 @@ Cloner le projet :
 
 
     git clone git@gitlab.2iopenservice.com:opentalent/app.git
     git clone git@gitlab.2iopenservice.com:opentalent/app.git
 
 
-
 Installer les dépendances :
 Installer les dépendances :
 
 
     yarn install
     yarn install
 
 
-
 Copier les certificats dans le répertoire `env/` de ce projet :
 Copier les certificats dans le répertoire `env/` de ce projet :
 
 
-* local.app.opentalent.fr.crt
-* local.app.opentalent.fr.key
-
+- local.app.opentalent.fr.crt
+- local.app.opentalent.fr.key
 
 
 Lancer le serveur de développement :
 Lancer le serveur de développement :
 
 
     yarn dev -o
     yarn dev -o
 
 
-
 ## Déploiement en prod
 ## Déploiement en prod
 
 
 ### Premier déploiement en tant que service
 ### Premier déploiement en tant que service
@@ -73,7 +67,6 @@ Attention, sur les environnements de test, il faut utiliser nvm pour exécuter l
 
 
     nvm exec yarn install
     nvm exec yarn install
 
 
-
 ## Autres
 ## Autres
 
 
 ### Lancer les tests
 ### Lancer les tests
@@ -101,7 +94,6 @@ Sur les environnements où app est servie par supervisor, on peut consulter les
 
 
     yarn eslint
     yarn eslint
 
 
-
 ### Faire fonctionner le HMR
 ### Faire fonctionner le HMR
 
 
 Si le HMR (Hot Module Reload) ne fontionne pas et qu'un message d'erreur est logué en console disant que l'adresse
 Si le HMR (Hot Module Reload) ne fontionne pas et qu'un message d'erreur est logué en console disant que l'adresse
@@ -113,13 +105,12 @@ n'est pas accessible, alors suivre les étapes suivantes :
 - Clic droit dessus, puis "ouvrir dans un nouvel onglet"
 - Clic droit dessus, puis "ouvrir dans un nouvel onglet"
 - Ajouter une exception de sécurité dans le navigateur
 - Ajouter une exception de sécurité dans le navigateur
 
 
-
 ## Plus d'infos
 ## Plus d'infos
 
 
 ## Structure du projet
 ## Structure du projet
 
 
 | Répertoire     | Rôle                                                                                                |
 | Répertoire     | Rôle                                                                                                |
-|----------------|-----------------------------------------------------------------------------------------------------|
+| -------------- | --------------------------------------------------------------------------------------------------- |
 | `assets`       | Contient les fichiers style et medias                                                               |
 | `assets`       | Contient les fichiers style et medias                                                               |
 | `components`   | Les différents composants graphiques qui composent l'application                                    |
 | `components`   | Les différents composants graphiques qui composent l'application                                    |
 | `composables`  | Des fonctions conscientes du contexte applicatif, qui font le lien entre les pages et les services  |
 | `composables`  | Des fonctions conscientes du contexte applicatif, qui font le lien entre les pages et les services  |
@@ -137,4 +128,3 @@ n'est pas accessible, alors suivre les étapes suivantes :
 | `stores`       | Le store et ses composants servent d'entrepôt de donnés, et s'assurent de la cohérence de celles-ci |
 | `stores`       | Le store et ses composants servent d'entrepôt de donnés, et s'assurent de la cohérence de celles-ci |
 | `tests`        | Regroupe les tests (unitaires, end-to-end...)                                                       |
 | `tests`        | Regroupe les tests (unitaires, end-to-end...)                                                       |
 | `types`        | Types Typescript (interfaces, enums...)                                                             |
 | `types`        | Types Typescript (interfaces, enums...)                                                             |
-

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 2 - 2
assets/css/global.scss


+ 1 - 1
assets/css/settings.scss

@@ -2,4 +2,4 @@
 @forward 'vuetify/settings' with (
 @forward 'vuetify/settings' with (
   $button-color: green,
   $button-color: green,
   $button-font-weight: 700
   $button-font-weight: 700
-);
+);

+ 0 - 1
assets/css/theme.scss

@@ -1,4 +1,3 @@
-
 .theme-primary {
 .theme-primary {
   background-color: rgb(var(--v-theme-primary)) !important;
   background-color: rgb(var(--v-theme-primary)) !important;
   color: rgb(var(--v-theme-on-primary)) !important;
   color: rgb(var(--v-theme-on-primary)) !important;

+ 12 - 13
components/Layout/Alert/Container.vue

@@ -17,27 +17,26 @@ Container principal pour l'affichage d'une ou plusieurs alertes
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import type { Alert } from '~/types/interfaces'
 import type { Alert } from '~/types/interfaces'
-import {usePageStore} from "~/stores/page";
-import type {ComputedRef} from "@vue/reactivity";
+import { usePageStore } from '~/stores/page'
+import type { ComputedRef } from '@vue/reactivity'
 
 
 const pageStore = usePageStore()
 const pageStore = usePageStore()
 
 
 const alerts: ComputedRef<Array<Alert>> = computed(() => {
 const alerts: ComputedRef<Array<Alert>> = computed(() => {
   return pageStore.alerts
   return pageStore.alerts
 })
 })
-
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
-  .alertContainer {
-    position: fixed;
-    bottom: 0;
-    right: 20px;
-    z-index: 1000;
-  }
+.alertContainer {
+  position: fixed;
+  bottom: 0;
+  right: 20px;
+  z-index: 1000;
+}
 
 
-  .alertContainer > .alertContent {
-    position: relative;
-    margin-bottom: 10px;
-  }
+.alertContainer > .alertContent {
+  position: relative;
+  margin-bottom: 10px;
+}
 </style>
 </style>

+ 9 - 11
components/Layout/Alert/Content.vue

@@ -13,20 +13,20 @@
     @mouseout="onMouseOut"
     @mouseout="onMouseOut"
   >
   >
     <ul v-if="props.alert.messages.length > 1">
     <ul v-if="props.alert.messages.length > 1">
-       <li v-for="message in props.alert.messages">
+      <li v-for="message in props.alert.messages">
         {{ $t(message) }}
         {{ $t(message) }}
       </li>
       </li>
     </ul>
     </ul>
     <span v-else>
     <span v-else>
-        {{ $t(props.alert.messages[0]) }}
+      {{ $t(props.alert.messages[0]) }}
     </span>
     </span>
   </v-alert>
   </v-alert>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import type {Alert} from '~/types/interfaces'
-import type {Ref} from "@vue/reactivity";
-import {usePageStore} from "~/stores/page";
+import type { Alert } from '~/types/interfaces'
+import type { Ref } from '@vue/reactivity'
+import { usePageStore } from '~/stores/page'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -34,7 +34,7 @@ const props = defineProps({
    */
    */
   alert: {
   alert: {
     type: Object as () => Alert,
     type: Object as () => Alert,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * The time after which the alert disappears
    * The time after which the alert disappears
@@ -42,8 +42,8 @@ const props = defineProps({
   timeout: {
   timeout: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: 3000
-  }
+    default: 3000,
+  },
 })
 })
 
 
 const show: Ref<boolean> = ref(true)
 const show: Ref<boolean> = ref(true)
@@ -77,8 +77,6 @@ const onMouseOut = () => {
 }
 }
 
 
 clearAlert()
 clearAlert()
-
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 10 - 6
components/Layout/AlertBar.vue

@@ -11,19 +11,23 @@ Contient les différentes barres d'alertes qui s'affichent dans certains cas
     <LayoutAlertBarSwitchUser />
     <LayoutAlertBarSwitchUser />
 
 
     <client-only>
     <client-only>
-      <LayoutAlertBarCotisation v-if="organizationProfile.isCmf && ability.can('manage', 'cotisation')" />
+      <LayoutAlertBarCotisation
+        v-if="organizationProfile.isCmf && ability.can('manage', 'cotisation')"
+      />
     </client-only>
     </client-only>
 
 
     <LayoutAlertBarSwitchYear />
     <LayoutAlertBarSwitchYear />
     <LayoutAlertBarSuperAdmin />
     <LayoutAlertBarSuperAdmin />
-    <LayoutAlertBarRegistrationStatus v-if="organizationProfile.hasModule('IEL')" />
+    <LayoutAlertBarRegistrationStatus
+      v-if="organizationProfile.hasModule('IEL')"
+    />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import {useAbility} from "@casl/vue";
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { useAbility } from '@casl/vue'
 
 
-  const organizationProfile = useOrganizationProfileStore()
-  const ability = useAbility()
+const organizationProfile = useOrganizationProfileStore()
+const ability = useAbility()
 </script>
 </script>

+ 43 - 24
components/Layout/AlertBar/Cotisation.vue

@@ -7,22 +7,22 @@ Barre d'alerte qui s'affiche pour donner l'état de la cotisation
 <template>
 <template>
   <main>
   <main>
     <UiSystemBar
     <UiSystemBar
-        v-if="alert && alert.text && alert.callback"
-        :text="$t(alert.text)"
-        icon="fas fa-info-circle"
-        :on-click="alert.callback"
-        class="theme-info"
+      v-if="alert && alert.text && alert.callback"
+      :text="$t(alert.text)"
+      icon="fas fa-info-circle"
+      :on-click="alert.callback"
+      class="theme-info"
     />
     />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {Ref} from "vue";
-import UrlUtils from "~/services/utils/urlUtils";
-import {ALERT_STATE_COTISATION} from "~/types/enum/enums";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import Cotisation from "~/models/Organization/Cotisation";
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import type { Ref } from 'vue'
+import UrlUtils from '~/services/utils/urlUtils'
+import { ALERT_STATE_COTISATION } from '~/types/enum/enums'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import Cotisation from '~/models/Organization/Cotisation'
 
 
 const organizationProfile = useOrganizationProfileStore()
 const organizationProfile = useOrganizationProfileStore()
 
 
@@ -38,7 +38,12 @@ const goToCotisation = () => {
   if (!organizationProfile.id) {
   if (!organizationProfile.id) {
     throw new Error('missing organization id')
     throw new Error('missing organization id')
   }
   }
-  window.location.href = UrlUtils.join(baseLegacyUrl, '/cotisation/cotisation_steps', organizationProfile.id, 'steps/1')
+  window.location.href = UrlUtils.join(
+    baseLegacyUrl,
+    '/cotisation/cotisation_steps',
+    organizationProfile.id,
+    'steps/1',
+  )
 }
 }
 
 
 /**
 /**
@@ -48,21 +53,30 @@ const openInvoiceWindow = () => {
   if (!cotisationYear.value) {
   if (!cotisationYear.value) {
     throw new Error('no cotisation year defined')
     throw new Error('no cotisation year defined')
   }
   }
-  window.open(UrlUtils.join(baseLegacyUrl, 'cotisation/invoice', cotisationYear.value), '_blank')
+  window.open(
+    UrlUtils.join(baseLegacyUrl, 'cotisation/invoice', cotisationYear.value),
+    '_blank',
+  )
 }
 }
 
 
 /**
 /**
  * Redirige l'utilisateur vers la page des assurances
  * Redirige l'utilisateur vers la page des assurances
  */
  */
 const goToInsurancePage = () => {
 const goToInsurancePage = () => {
-  window.location.href = UrlUtils.join(baseLegacyUrl, 'cotisation/insuranceedit')
+  window.location.href = UrlUtils.join(
+    baseLegacyUrl,
+    'cotisation/insuranceedit',
+  )
 }
 }
 
 
 /**
 /**
  * Redirige (dans un nouvel onglet) l'utilsateur vers le site web de la CMF
  * Redirige (dans un nouvel onglet) l'utilsateur vers le site web de la CMF
  */
  */
 const openCmfSubscriptionPage = () => {
 const openCmfSubscriptionPage = () => {
-  window.open('https://www.cmf-musique.org/services/assurances/assurance-de-groupe/', '_blank')
+  window.open(
+    'https://www.cmf-musique.org/services/assurances/assurance-de-groupe/',
+    '_blank',
+  )
 }
 }
 
 
 // On récupère l'état des cotisations via l'API
 // On récupère l'état des cotisations via l'API
@@ -71,7 +85,10 @@ if (!organizationProfile.id) {
 }
 }
 
 
 const { fetch } = useEntityFetch()
 const { fetch } = useEntityFetch()
-const { data: cotisation, pending } = await fetch(Cotisation, organizationProfile.id)
+const { data: cotisation, pending } = await fetch(
+  Cotisation,
+  organizationProfile.id,
+)
 
 
 interface Alert {
 interface Alert {
   text: string
   text: string
@@ -86,10 +103,13 @@ const alert: ComputedRef<Alert | null> = computed(() => {
   cotisationYear.value = cotisation.value.cotisationYear
   cotisationYear.value = cotisation.value.cotisationYear
 
 
   const mapping: Record<ALERT_STATE_COTISATION, Alert> = {
   const mapping: Record<ALERT_STATE_COTISATION, Alert> = {
-    'AFFILIATION': { text: 'cotisation_access', callback: goToCotisation },
-    'INVOICE': { text: 'upload_cotisation_invoice', callback: openInvoiceWindow },
-    'INSURANCE': { text: 'renew_insurance_cmf', callback: goToInsurancePage },
-    'ADVERTISINGINSURANCE': { text: 'insurance_cmf_subscription', callback: openCmfSubscriptionPage },
+    AFFILIATION: { text: 'cotisation_access', callback: goToCotisation },
+    INVOICE: { text: 'upload_cotisation_invoice', callback: openInvoiceWindow },
+    INSURANCE: { text: 'renew_insurance_cmf', callback: goToInsurancePage },
+    ADVERTISINGINSURANCE: {
+      text: 'insurance_cmf_subscription',
+      callback: openCmfSubscriptionPage,
+    },
   }
   }
 
 
   if (!cotisation.value.alertState) {
   if (!cotisation.value.alertState) {
@@ -98,11 +118,10 @@ const alert: ComputedRef<Alert | null> = computed(() => {
 
 
   return mapping[cotisation.value.alertState as ALERT_STATE_COTISATION]
   return mapping[cotisation.value.alertState as ALERT_STATE_COTISATION]
 })
 })
-
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  :deep(.clickable:hover) {
-    text-decoration: none !important;
-  }
+:deep(.clickable:hover) {
+  text-decoration: none !important;
+}
 </style>
 </style>

+ 7 - 7
components/Layout/AlertBar/Env.vue

@@ -6,16 +6,16 @@ Barre d'alerte qui s'affiche lorsque l'utilisateur n'est pas dans un environneme
 
 
 <template>
 <template>
   <UiSystemBar
   <UiSystemBar
-      v-if="show"
-      :text="$t('not_production_environment', { env: env })"
-      icon="fas fa-exclamation-triangle"
-      class="theme-warning"
+    v-if="show"
+    :text="$t('not_production_environment', { env: env })"
+    icon="fas fa-exclamation-triangle"
+    class="theme-warning"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  const runtimeConfig = useRuntimeConfig()
+const runtimeConfig = useRuntimeConfig()
 
 
-  const env = runtimeConfig.public.env ?? 'unknown'
-  const show = env !== 'production'
+const env = runtimeConfig.public.env ?? 'unknown'
+const show = env !== 'production'
 </script>
 </script>

+ 17 - 16
components/Layout/AlertBar/OnlineRegistration.vue

@@ -5,37 +5,38 @@ Barre d'alerte sur l'ouverture ou non de l'inscription en ligne
 
 
 <template>
 <template>
   <UiSystemBar
   <UiSystemBar
-      v-if="show"
-      :text="$t(message)"
-      icon="fas fa-id-card"
-      class="theme-secondary-alt"
+    v-if="show"
+    :text="$t(message)"
+    icon="fas fa-id-card"
+    class="theme-secondary-alt"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import RegistrationAvailability from "~/models/OnlineRegistration/RegistrationAvailability";
-import {ComputedRef} from "@vue/reactivity";
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import RegistrationAvailability from '~/models/OnlineRegistration/RegistrationAvailability'
+import { ComputedRef } from '@vue/reactivity'
 
 
 const { fetch } = useEntityFetch()
 const { fetch } = useEntityFetch()
 
 
 const accessProfile = useAccessProfileStore()
 const accessProfile = useAccessProfileStore()
 
 
-const { data: registrationAvailability, pending } = fetch(RegistrationAvailability, accessProfile.id ?? 0)
+const { data: registrationAvailability, pending } = fetch(
+  RegistrationAvailability,
+  accessProfile.id ?? 0,
+)
 
 
 const show: ComputedRef<boolean> = computed(() => {
 const show: ComputedRef<boolean> = computed(() => {
-  return !pending && (registrationAvailability.value as RegistrationAvailability).available
+  return (
+    !pending &&
+    (registrationAvailability.value as RegistrationAvailability).available
+  )
 })
 })
 
 
 const message: ComputedRef<string> = computed(() => {
 const message: ComputedRef<string> = computed(() => {
   return (registrationAvailability.value as RegistrationAvailability).message
   return (registrationAvailability.value as RegistrationAvailability).message
 })
 })
-
-
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 17 - 16
components/Layout/AlertBar/RegistrationStatus.vue

@@ -5,30 +5,33 @@ Barre d'alerte quand au statut (l'avancement) de l'inscription en ligne de l'uti
 
 
 <template>
 <template>
   <UiSystemBar
   <UiSystemBar
-      v-if="!pending && message"
-      :text="$t(message)"
-      icon="fas fa-id-card"
-      class="theme-secondary"
+    v-if="!pending && message"
+    :text="$t(message)"
+    icon="fas fa-id-card"
+    class="theme-secondary"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import RegistrationStatus from "~/models/OnlineRegistration/RegistrationStatus";
-import type {ComputedRef} from "@vue/reactivity";
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import RegistrationStatus from '~/models/OnlineRegistration/RegistrationStatus'
+import type { ComputedRef } from '@vue/reactivity'
 
 
 const { fetch } = useEntityFetch()
 const { fetch } = useEntityFetch()
 
 
 const accessProfile = useAccessProfileStore()
 const accessProfile = useAccessProfileStore()
 
 
-const { data: registrationStatus, pending } = fetch(RegistrationStatus, accessProfile.id ?? 0)
+const { data: registrationStatus, pending } = fetch(
+  RegistrationStatus,
+  accessProfile.id ?? 0,
+)
 
 
 const messagesByStatus = {
 const messagesByStatus = {
-  'NEGOTIABLE': "your_application_is_awaiting_processing",
-  'PENDING': "you_have_been_placed_on_the_waiting_list",
-  'ACCEPTED': "your_registration_file_has_been_validated",
-  'DENIED': "your_application_has_been_refused",
+  NEGOTIABLE: 'your_application_is_awaiting_processing',
+  PENDING: 'you_have_been_placed_on_the_waiting_list',
+  ACCEPTED: 'your_registration_file_has_been_validated',
+  DENIED: 'your_application_has_been_refused',
 }
 }
 
 
 const message: ComputedRef<string> = computed(() => {
 const message: ComputedRef<string> = computed(() => {
@@ -40,6 +43,4 @@ const message: ComputedRef<string> = computed(() => {
 })
 })
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 34 - 26
components/Layout/AlertBar/SuperAdmin.vue

@@ -13,33 +13,41 @@ Barre d'alerte qui s'affiche lorsque l'utilisateur est un super admin en mode sw
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {useAccessProfileStore} from "~/stores/accessProfile";
-  import UrlUtils from "~/services/utils/urlUtils";
-  import type {ComputedRef} from "@vue/reactivity";
-  import {navigateTo} from "#app";
-  import {useAdminUrl} from "~/composables/utils/useAdminUrl";
-
-  const { makeAdminUrl } = useAdminUrl()
-
-  const accessProfile = useAccessProfileStore()
-
-  const show: ComputedRef<boolean> = computed(() =>
-      accessProfile.originalAccess !== null && accessProfile.originalAccess.isSuperAdminAccess
-  )
-
-  const url: ComputedRef<string> = computed(() => {
-    const orgId = accessProfile.originalAccess ? accessProfile.originalAccess.organization.id : null
-    const originalAccessId = accessProfile.originalAccess ? accessProfile.originalAccess.id : null
-
-    if (show && orgId && originalAccessId) {
-      return makeAdminUrl(UrlUtils.join('#', 'switch_user', orgId, originalAccessId, 'exit'))
-    }
-    return ''
-  })
-
-  const onClick = () => {
-    navigateTo(url.value,{ external: true })
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import UrlUtils from '~/services/utils/urlUtils'
+import type { ComputedRef } from '@vue/reactivity'
+import { navigateTo } from '#app'
+import { useAdminUrl } from '~/composables/utils/useAdminUrl'
+
+const { makeAdminUrl } = useAdminUrl()
+
+const accessProfile = useAccessProfileStore()
+
+const show: ComputedRef<boolean> = computed(
+  () =>
+    accessProfile.originalAccess !== null &&
+    accessProfile.originalAccess.isSuperAdminAccess,
+)
+
+const url: ComputedRef<string> = computed(() => {
+  const orgId = accessProfile.originalAccess
+    ? accessProfile.originalAccess.organization.id
+    : null
+  const originalAccessId = accessProfile.originalAccess
+    ? accessProfile.originalAccess.id
+    : null
+
+  if (show && orgId && originalAccessId) {
+    return makeAdminUrl(
+      UrlUtils.join('#', 'switch_user', orgId, originalAccessId, 'exit'),
+    )
   }
   }
+  return ''
+})
+
+const onClick = () => {
+  navigateTo(url.value, { external: true })
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 11 - 8
components/Layout/AlertBar/SwitchUser.vue

@@ -8,23 +8,26 @@ Barre qui s'affiche lorsque l'utilisateur possède un compte multi user
   <UiSystemBar v-if="show" class="theme-info">
   <UiSystemBar v-if="show" class="theme-info">
     <v-icon small icon="fas fa-info-circle" />
     <v-icon small icon="fas fa-info-circle" />
     <span>
     <span>
-      {{ $t('multi_account_alert_part1') }} <strong>{{ fullName }}</strong> {{ $t('multi_account_alert_part2') }}
+      {{ $t('multi_account_alert_part1') }} <strong>{{ fullName }}</strong>
+      {{ $t('multi_account_alert_part2') }}
     </span>
     </span>
 
 
-    <v-icon class="pl-1" small icon="fa fa-users"/> &nbsp;{{$t('multi_account_alert_part3')}}
+    <v-icon class="pl-1" small icon="fa fa-users" /> &nbsp;{{
+      $t('multi_account_alert_part3')
+    }}
   </UiSystemBar>
   </UiSystemBar>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {useAccessProfileStore} from "~/stores/accessProfile";
-  import {useMenu} from "~/composables/layout/useMenu";
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { useMenu } from '~/composables/layout/useMenu'
 
 
-  const accessProfile = useAccessProfileStore()
-  const { hasMenu } = useMenu()
+const accessProfile = useAccessProfileStore()
+const { hasMenu } = useMenu()
 
 
-  const show = computed(() => hasMenu('MyFamily'))
+const show = computed(() => hasMenu('MyFamily'))
 
 
-  const fullName = `${accessProfile.givenName} ${accessProfile.name}`
+const fullName = `${accessProfile.givenName} ${accessProfile.name}`
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 50 - 43
components/Layout/AlertBar/SwitchYear.vue

@@ -7,63 +7,70 @@ Barre d'alerte qui s'affiche lorsque l'utilisateur n'est pas sur l'année couran
 <template>
 <template>
   <!-- TODO : fonctionnement à valider -->
   <!-- TODO : fonctionnement à valider -->
   <UiSystemBar v-if="show" class="theme-warning flex-column">
   <UiSystemBar v-if="show" class="theme-warning flex-column">
-    {{$t('not_current_year')}}
+    {{ $t('not_current_year') }}
 
 
-    <a @click="resetYear" class="text-decoration-none on-warning" style="cursor: pointer;">
+    <a
+      @click="resetYear"
+      class="text-decoration-none on-warning"
+      style="cursor: pointer"
+    >
       <strong class="pl-2 text-neutral-strong">
       <strong class="pl-2 text-neutral-strong">
-        {{$t('not_current_year_reset')}}
+        {{ $t('not_current_year_reset') }}
       </strong>
       </strong>
     </a>
     </a>
   </UiSystemBar>
   </UiSystemBar>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {useAccessProfileStore} from "~/stores/accessProfile";
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import type {ComputedRef} from "@vue/reactivity";
-  import {useFormStore} from "~/stores/form";
-  import Access from "~/models/Access/Access";
-  import {usePageStore} from "~/stores/page";
-  import {useEntityManager} from "~/composables/data/useEntityManager";
-  import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import type { ComputedRef } from '@vue/reactivity'
+import { useFormStore } from '~/stores/form'
+import Access from '~/models/Access/Access'
+import { usePageStore } from '~/stores/page'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
 
-  const { em } = useEntityManager()
-  const accessProfile = useAccessProfileStore()
-  const organizationProfile = useOrganizationProfileStore()
-  const { setDirty } = useFormStore()
-  const pageStore = usePageStore()
-  const { refreshProfile } = useRefreshProfile()
+const { em } = useEntityManager()
+const accessProfile = useAccessProfileStore()
+const organizationProfile = useOrganizationProfileStore()
+const { setDirty } = useFormStore()
+const pageStore = usePageStore()
+const { refreshProfile } = useRefreshProfile()
 
 
-  const show: ComputedRef<boolean> = computed(() => {
-    return (
-        accessProfile.historical.past || accessProfile.historical.future ||
-        (accessProfile.historical.dateStart && accessProfile.historical.dateStart.length > 0) ||
-        (accessProfile.historical.dateEnd && accessProfile.historical.dateEnd.length > 0) ||
-        accessProfile.activityYear !== organizationProfile.currentActivityYear
-    )
-  })
+const show: ComputedRef<boolean> = computed(() => {
+  return (
+    accessProfile.historical.past ||
+    accessProfile.historical.future ||
+    (accessProfile.historical.dateStart &&
+      accessProfile.historical.dateStart.length > 0) ||
+    (accessProfile.historical.dateEnd &&
+      accessProfile.historical.dateEnd.length > 0) ||
+    accessProfile.activityYear !== organizationProfile.currentActivityYear
+  )
+})
 
 
-  const resetYear = async () => {
-      const defaultValues = {
-        historical: {
-            "future": false,
-            "past": false,
-            "present": true,
-        },
-        activityYear: organizationProfile.currentActivityYear
-      }
+const resetYear = async () => {
+  const defaultValues = {
+    historical: {
+      future: false,
+      past: false,
+      present: true,
+    },
+    activityYear: organizationProfile.currentActivityYear,
+  }
 
 
-    // Il faut ajouter un patch sur le profile ici
-    setDirty(false)
+  // Il faut ajouter un patch sur le profile ici
+  setDirty(false)
 
 
-    pageStore.loading = true
-    await em.patch(Access, accessProfile.currentAccessId, defaultValues)
-    if (process.server) {
-        // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
-      await refreshProfile()
-    }
-    window.location.reload()
+  pageStore.loading = true
+  await em.patch(Access, accessProfile.currentAccessId, defaultValues)
+  if (process.server) {
+    // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
+    await refreshProfile()
   }
   }
+  window.location.reload()
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 10 - 10
components/Layout/BannerTop.vue

@@ -18,14 +18,14 @@ Troisième bandeau en partant du haut, contenant entre autre le numéro SIRET de
 </template>
 </template>
 
 
 <style scoped>
 <style scoped>
-  .bannerTopForm{
-    min-height: 100px;
-    margin-top: 10px !important;
-    margin-bottom: 10px !important;
-  }
-  .bannerTopForm > .col{
-    min-height: 100px;
-    padding: 10px;
-    padding-left: 24px;
-  }
+.bannerTopForm {
+  min-height: 100px;
+  margin-top: 10px !important;
+  margin-bottom: 10px !important;
+}
+.bannerTopForm > .col {
+  min-height: 100px;
+  padding: 10px;
+  padding-left: 24px;
+}
 </style>
 </style>

+ 3 - 3
components/Layout/Container.vue

@@ -12,7 +12,7 @@
 </template>
 </template>
 
 
 <style scoped>
 <style scoped>
-  .container{
-    padding-top: 0;
-  }
+.container {
+  padding-top: 0;
+}
 </style>
 </style>

+ 42 - 40
components/Layout/Dialog.vue

@@ -7,7 +7,12 @@
     :content-class="contentClass"
     :content-class="contentClass"
   >
   >
     <v-card class="d-flex flex-row">
     <v-card class="d-flex flex-row">
-      <div :class="'dialog-type flex-column justify-center d-none d-sm-flex theme-' + theme">
+      <div
+        :class="
+          'dialog-type flex-column justify-center d-none d-sm-flex theme-' +
+          theme
+        "
+      >
         <h3 class="d-flex">
         <h3 class="d-flex">
           <slot name="dialogType" />
           <slot name="dialogType" />
         </h3>
         </h3>
@@ -34,15 +39,14 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
 const props = defineProps({
 const props = defineProps({
   show: {
   show: {
     type: [Boolean, Object],
     type: [Boolean, Object],
-    required: true
+    required: true,
   },
   },
   contentClass: {
   contentClass: {
     type: String,
     type: String,
-    required: false
+    required: false,
   },
   },
   theme: {
   theme: {
     type: String,
     type: String,
@@ -52,55 +56,53 @@ const props = defineProps({
   maxWidth: {
   maxWidth: {
     type: [Number, String],
     type: [Number, String],
     required: false,
     required: false,
-    default: 800
-  }
+    default: 800,
+  },
 })
 })
 
 
 // @ts-ignore  -> just to avoid the error with the prop's type of v-dialog
 // @ts-ignore  -> just to avoid the error with the prop's type of v-dialog
 const _show = computed(() => props.show) as boolean
 const _show = computed(() => props.show) as boolean
-
-
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-  .dialog-title {
-    padding-left: 40px;
+.dialog-title {
+  padding-left: 40px;
+  font-weight: normal;
+}
+
+.dialog-type {
+  width: 60px;
+  min-width: 60px;
+  max-width: 60px;
+  min-height: 120px;
+  padding: 25px 10px;
+
+  h3 {
+    font-size: 25px;
     font-weight: normal;
     font-weight: normal;
+    writing-mode: vertical-lr;
+    transform: rotate(-180deg);
   }
   }
+}
 
 
-  .dialog-type {
-    width: 60px;
-    min-width: 60px;
-    max-width: 60px;
-    min-height: 120px;
-    padding: 25px 10px;
-
-   h3 {
-     font-size: 25px;
-     font-weight: normal;
-     writing-mode: vertical-lr;
-     transform: rotate(-180deg);
-    }
-  }
-
-  .dialog-container {
-    overflow-x: scroll;
-  }
+.dialog-container {
+  overflow-x: scroll;
+}
 
 
-  .dialog-text-container {
-    max-height: 70vh;
-    overflow: auto;
-  }
+.dialog-text-container {
+  max-height: 70vh;
+  overflow: auto;
+}
 
 
-  .modal-level-alert {
-    .dialog-type{
-      background: rgb(var(--v-theme-danger, #f56954));
-    }
+.modal-level-alert {
+  .dialog-type {
+    background: rgb(var(--v-theme-danger, #f56954));
   }
   }
+}
 
 
-  .modal-level-warning {
-    .dialog-type{
-      background: rgb(var(--v-theme-warning, #f39c12));
-    }
+.modal-level-warning {
+  .dialog-type {
+    background: rgb(var(--v-theme-warning, #f39c12));
   }
   }
+}
 </style>
 </style>

+ 45 - 44
components/Layout/Header.vue

@@ -4,22 +4,15 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
 -->
 -->
 
 
 <template>
 <template>
-  <v-app-bar
-      order="0"
-      density="compact"
-      class="theme-primary"
-  >
+  <v-app-bar order="0" density="compact" class="theme-primary">
     <template #prepend>
     <template #prepend>
       <v-app-bar-nav-icon
       <v-app-bar-nav-icon
-          v-if="hasMainMenu && layoutStore.name !== 'parameters'"
-          :icon="isMainMenuOpened ? 'mdi:mdi-menu-open' : 'mdi:mdi-menu'"
-          @click="toggleMainMenu"
+        v-if="hasMainMenu && layoutStore.name !== 'parameters'"
+        :icon="isMainMenuOpened ? 'mdi:mdi-menu-open' : 'mdi:mdi-menu'"
+        @click="toggleMainMenu"
       />
       />
       <div v-else-if="hasParametersMenu && layoutStore.name === 'parameters'">
       <div v-else-if="hasParametersMenu && layoutStore.name === 'parameters'">
-        <v-app-bar-nav-icon
-          v-if="mdAndUp"
-          icon="fa fa-gear"
-        />
+        <v-app-bar-nav-icon v-if="mdAndUp" icon="fa fa-gear" />
         <v-app-bar-nav-icon
         <v-app-bar-nav-icon
           v-else
           v-else
           :icon="isParametersMenuOpened ? 'mdi:mdi-menu-open' : 'mdi:mdi-menu'"
           :icon="isParametersMenuOpened ? 'mdi:mdi-menu-open' : 'mdi:mdi-menu'"
@@ -28,11 +21,15 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
       </div>
       </div>
     </template>
     </template>
 
 
-    <v-toolbar-title v-if="mdAndUp" v-text="title"/>
+    <v-toolbar-title v-if="mdAndUp" v-text="title" />
 
 
-    <LayoutThemeSwitcher v-if="false" /> <!-- En attente validation PO -->
+    <LayoutThemeSwitcher v-if="false" />
+    <!-- En attente validation PO -->
 
 
-    <LayoutHeaderUniversalCreationCreateButton v-if="showUniversalButton" class="mr-3" />
+    <LayoutHeaderUniversalCreationCreateButton
+      v-if="showUniversalButton"
+      class="mr-3"
+    />
 
 
     <LayoutHeaderHomeBtn v-if="smAndUp" />
     <LayoutHeaderHomeBtn v-if="smAndUp" />
 
 
@@ -49,29 +46,34 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
     <LayoutHeaderMenu name="Account" color="on-primary" icon="fas fa-sun" />
     <LayoutHeaderMenu name="Account" color="on-primary" icon="fas fa-sun" />
 
 
     <a
     <a
-        :href="runtimeConfig.supportUrl"
-        class="text-body px-3 py-4 ml-2 theme-secondary text-decoration-none h-100"
-        target="_blank"
+      :href="runtimeConfig.supportUrl"
+      class="text-body px-3 py-4 ml-2 theme-secondary text-decoration-none h-100"
+      target="_blank"
     >
     >
       <span class="d-none d-sm-none d-md-flex">{{ $t('help_access') }}</span>
       <span class="d-none d-sm-none d-md-flex">{{ $t('help_access') }}</span>
-      <v-icon icon="fas fa-question-circle" class="d-sm-flex d-md-none" color="on-secondary" />
+      <v-icon
+        icon="fas fa-question-circle"
+        class="d-sm-flex d-md-none"
+        color="on-secondary"
+      />
     </a>
     </a>
   </v-app-bar>
   </v-app-bar>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {computed} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
-import {useMenu} from "~/composables/layout/useMenu";
-import {useAbility} from "@casl/vue";
-import {useDisplay} from 'vuetify'
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import {useLayoutStore} from "~/stores/layout";
+import { computed } from '@vue/reactivity'
+import type { ComputedRef } from '@vue/reactivity'
+import { useMenu } from '~/composables/layout/useMenu'
+import { useAbility } from '@casl/vue'
+import { useDisplay } from 'vuetify'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { useLayoutStore } from '~/stores/layout'
 
 
 const organizationProfile = useOrganizationProfileStore()
 const organizationProfile = useOrganizationProfileStore()
 const runtimeConfig = useRuntimeConfig()
 const runtimeConfig = useRuntimeConfig()
-const title: ComputedRef<string> = computed(() => organizationProfile.name ?? 'Opentalent')
+const title: ComputedRef<string> = computed(
+  () => organizationProfile.name ?? 'Opentalent',
+)
 
 
 const { hasMenu, isMenuOpened, toggleMenu } = useMenu()
 const { hasMenu, isMenuOpened, toggleMenu } = useMenu()
 
 
@@ -87,25 +89,24 @@ const toggleParametersMenu = () => toggleMenu('Parameters')
 
 
 const ability = useAbility()
 const ability = useAbility()
 const showUniversalButton =
 const showUniversalButton =
-    ability.can('manage', 'users')
-    || ability.can('manage', 'courses')
-    || ability.can('manage', 'examens')
-    || ability.can('manage', 'educationalprojects')
-    || ability.can('manage', 'events')
-    || ability.can('manage', 'emails')
-    || ability.can('manage', 'mails')
-    || ability.can('manage', 'texto')
-    || ability.can('display', 'message_send_page')
-    || ability.can('manage', 'equipments')
+  ability.can('manage', 'users') ||
+  ability.can('manage', 'courses') ||
+  ability.can('manage', 'examens') ||
+  ability.can('manage', 'educationalprojects') ||
+  ability.can('manage', 'events') ||
+  ability.can('manage', 'emails') ||
+  ability.can('manage', 'mails') ||
+  ability.can('manage', 'texto') ||
+  ability.can('display', 'message_send_page') ||
+  ability.can('manage', 'equipments')
 
 
 const layoutStore = useLayoutStore()
 const layoutStore = useLayoutStore()
-
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
-  .help {
-    padding: 14px 14px 13px;
-    font-size: 14px;
-    text-decoration: none;
-  }
+.help {
+  padding: 14px 14px 13px;
+  font-size: 14px;
+  text-decoration: none;
+}
 </style>
 </style>

+ 11 - 13
components/Layout/Header/HomeBtn.vue

@@ -1,26 +1,24 @@
 <template>
 <template>
   <div>
   <div>
     <v-btn
     <v-btn
-        ref="btn"
-        icon="fas fa-home"
-        size="small"
-        :href="homeUrl"
-        class="on-primary"
+      ref="btn"
+      icon="fas fa-home"
+      size="small"
+      :href="homeUrl"
+      class="on-primary"
     />
     />
     <v-tooltip :activator="btn" :text="$t('welcome')" location="bottom" />
     <v-tooltip :activator="btn" :text="$t('welcome')" location="bottom" />
   </div>
   </div>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {ref} from "@vue/reactivity";
-  import {useDisplay} from "vuetify";
-  import {useHomeUrl} from "~/composables/utils/useHomeUrl";
+import { ref } from '@vue/reactivity'
+import { useDisplay } from 'vuetify'
+import { useHomeUrl } from '~/composables/utils/useHomeUrl'
 
 
-  const { homeUrl } = useHomeUrl()
+const { homeUrl } = useHomeUrl()
 
 
-  const btn = ref(null);
+const btn = ref(null)
 </script>
 </script>
 
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 57 - 57
components/Layout/Header/Menu.vue

@@ -5,79 +5,79 @@ header principal (configuration, paramètres du compte...)
 
 
 <template>
 <template>
   <div v-if="displayMenu">
   <div v-if="displayMenu">
-
-    <v-btn
-        ref="btn"
-        icon
-        size="small"
-        class="ml-2"
-    >
+    <v-btn ref="btn" icon size="small" class="ml-2">
       <v-avatar
       <v-avatar
-          v-if="menu.icon.avatarId || menu.icon.avatarByDefault"
-          size="30"
+        v-if="menu.icon.avatarId || menu.icon.avatarByDefault"
+        size="30"
       >
       >
         <UiImage
         <UiImage
-            :imageId="menu.icon.avatarId"
-            :defaultImage="menu.icon.avatarByDefault"
-            :width="30"
+          :imageId="menu.icon.avatarId"
+          :defaultImage="menu.icon.avatarByDefault"
+          :width="30"
         />
         />
       </v-avatar>
       </v-avatar>
 
 
-      <v-icon
-          v-else
-          :icon="menu.icon.name"
-          class="on-primary"
-      />
+      <v-icon v-else :icon="menu.icon.name" class="on-primary" />
     </v-btn>
     </v-btn>
 
 
-    <v-tooltip
-        :activator="btn"
-        :text="$t(menu.label)"
-        location="bottom"
-    />
+    <v-tooltip :activator="btn" :text="$t(menu.label)" location="bottom" />
 
 
     <v-menu
     <v-menu
-        :activator="btn"
-        :model-value="isOpened()"
-        @update:modelValue="onStateUpdated"
+      :activator="btn"
+      :model-value="isOpened()"
+      @update:modelValue="onStateUpdated"
     >
     >
       <v-card>
       <v-card>
         <v-card-title class="theme-neutral text-body-2 font-weight-bold">
         <v-card-title class="theme-neutral text-body-2 font-weight-bold">
-          {{$t(menu.label)}}
+          {{ $t(menu.label) }}
         </v-card-title>
         </v-card-title>
 
 
         <v-card-text class="ma-0 pa-0 header-menu">
         <v-card-text class="ma-0 pa-0 header-menu">
           <v-list density="compact" :subheader="true">
           <v-list density="compact" :subheader="true">
             <template v-for="(child, index) in menu.children" :key="index">
             <template v-for="(child, index) in menu.children" :key="index">
               <v-list-item
               <v-list-item
-                  :id="child.label"
-                  :href="!isInternalLink(child) ? child.to : undefined"
-                  :to="isInternalLink(child) ? child.to : undefined"
+                :id="child.label"
+                :href="!isInternalLink(child) ? child.to : undefined"
+                :to="isInternalLink(child) ? child.to : undefined"
               >
               >
                 <span v-if="child.icon" class="pr-2 d-flex align-center">
                 <span v-if="child.icon" class="pr-2 d-flex align-center">
-                  <v-avatar v-if="menu.icon.avatarId || child.icon.avatarByDefault" size="30" >
-                    <UiImage :imageId="child.icon.avatarId" :defaultImage="child.icon.avatarByDefault" :width="30" />
+                  <v-avatar
+                    v-if="menu.icon.avatarId || child.icon.avatarByDefault"
+                    size="30"
+                  >
+                    <UiImage
+                      :imageId="child.icon.avatarId"
+                      :defaultImage="child.icon.avatarByDefault"
+                      :width="30"
+                    />
                   </v-avatar>
                   </v-avatar>
                   <v-icon v-else class="on-primary" size="small">
                   <v-icon v-else class="on-primary" size="small">
                     {{ child.icon.name }}
                     {{ child.icon.name }}
                   </v-icon>
                   </v-icon>
                 </span>
                 </span>
 
 
-                <span>{{ translateLabel ? $t(child.label) : child.label }}</span>
+                <span>{{
+                  translateLabel ? $t(child.label) : child.label
+                }}</span>
               </v-list-item>
               </v-list-item>
-
             </template>
             </template>
           </v-list>
           </v-list>
         </v-card-text>
         </v-card-text>
 
 
-        <v-card-actions v-if="menu.actions.length > 0" class="ma-0 pa-0 theme-primary">
+        <v-card-actions
+          v-if="menu.actions.length > 0"
+          class="ma-0 pa-0 theme-primary"
+        >
           <template v-for="(action, index) in menu.actions" :key="index">
           <template v-for="(action, index) in menu.actions" :key="index">
             <v-list-item
             <v-list-item
-                :id="action.label"
-                :href="!isInternalLink(action) ? action.to : undefined"
-                :to="isInternalLink(action) ? action.to : undefined"
+              :id="action.label"
+              :href="!isInternalLink(action) ? action.to : undefined"
+              :to="isInternalLink(action) ? action.to : undefined"
             >
             >
-              <v-list-item-title class="text-body-2" v-text="$t(action.label)"/>
+              <v-list-item-title
+                class="text-body-2"
+                v-text="$t(action.label)"
+              />
             </v-list-item>
             </v-list-item>
           </template>
           </template>
         </v-card-actions>
         </v-card-actions>
@@ -87,22 +87,23 @@ header principal (configuration, paramètres du compte...)
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useMenu} from "~/composables/layout/useMenu";
-import {computed, ref} from "@vue/reactivity";
+import { useMenu } from '~/composables/layout/useMenu'
+import { computed, ref } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   name: {
   name: {
     type: String,
     type: String,
-    required: true
+    required: true,
   },
   },
   translateLabel: {
   translateLabel: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: true
-  }
+    default: true,
+  },
 })
 })
 
 
-const { getMenu, isInternalLink, hasMenu, setMenuState, isMenuOpened } = useMenu()
+const { getMenu, isInternalLink, hasMenu, setMenuState, isMenuOpened } =
+  useMenu()
 
 
 const menu = getMenu(props.name)
 const menu = getMenu(props.name)
 const displayMenu = computed(() => hasMenu(props.name))
 const displayMenu = computed(() => hasMenu(props.name))
@@ -113,23 +114,22 @@ const onStateUpdated = (e: any) => {
 }
 }
 
 
 const btn = ref(null)
 const btn = ref(null)
-
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  :deep(.v-btn .v-icon) {
-    font-size: 1rem !important;
-  }
+:deep(.v-btn .v-icon) {
+  font-size: 1rem !important;
+}
 
 
-  .v-list {
-    padding: 0;
-  }
+.v-list {
+  padding: 0;
+}
 
 
-  .v-list-item {
-    width: 100%;
-  }
+.v-list-item {
+  width: 100%;
+}
 
 
-  .header-menu .v-list .v-list-item:last-child {
-    border-bottom: none;
-  }
+.header-menu .v-list .v-list-item:last-child {
+  border-bottom: none;
+}
 </style>
 </style>

+ 90 - 85
components/Layout/Header/Notification.vue

@@ -1,19 +1,13 @@
 <template>
 <template>
-  <v-btn
-      ref="btn"
-      icon
-      size="small"
-      class="ml-2"
-  >
+  <v-btn ref="btn" icon size="small" class="ml-2">
     <v-badge
     <v-badge
-        color="warning"
-        offset-x="-4"
-        offset-y="17"
-        :model-value="unreadNotification.length > 0"
-        :content="unreadNotification.length">
-      <v-icon class="on-primary">
-        fa fa-bell
-      </v-icon>
+      color="warning"
+      offset-x="-4"
+      offset-y="17"
+      :model-value="unreadNotification.length > 0"
+      :content="unreadNotification.length"
+    >
+      <v-icon class="on-primary"> fa fa-bell </v-icon>
     </v-badge>
     </v-badge>
   </v-btn>
   </v-btn>
 
 
@@ -22,10 +16,10 @@
   </v-tooltip>
   </v-tooltip>
 
 
   <v-menu
   <v-menu
-      v-if="btn !== null"
-      :activator="btn"
-      v-model="isOpen"
-      location="bottom left"
+    v-if="btn !== null"
+    :activator="btn"
+    v-model="isOpen"
+    location="bottom left"
   >
   >
     <v-card max-width="400">
     <v-card max-width="400">
       <v-card-title class="bg-neutral text-body-2 font-weight-bold">
       <v-card-title class="bg-neutral text-body-2 font-weight-bold">
@@ -35,21 +29,23 @@
       <v-card-text class="ma-0 pa-0 header-menu">
       <v-card-text class="ma-0 pa-0 header-menu">
         <v-list density="compact" :subheader="true" class="pa-0">
         <v-list density="compact" :subheader="true" class="pa-0">
           <v-list-item
           <v-list-item
-              v-for="(notification, index) in notifications"
-              :key="index"
-              :class="'list_item py-3' + `${notification.notificationUsers.length === 0 ? ' unread' : ''}`"
+            v-for="(notification, index) in notifications"
+            :key="index"
+            :class="
+              'list_item py-3' +
+              `${notification.notificationUsers.length === 0 ? ' unread' : ''}`
+            "
           >
           >
             <span class="">{{ getMessage(notification) }}</span>
             <span class="">{{ getMessage(notification) }}</span>
 
 
             <template #append>
             <template #append>
               <v-icon
               <v-icon
-                  v-if="notification.link"
-                  icon="mdi:mdi-download"
-                  @click="download(notification.link)"
-                  class="pt-4"
+                v-if="notification.link"
+                icon="mdi:mdi-download"
+                @click="download(notification.link)"
+                class="pt-4"
               />
               />
             </template>
             </template>
-
           </v-list-item>
           </v-list-item>
 
 
           <v-divider></v-divider>
           <v-divider></v-divider>
@@ -58,19 +54,14 @@
           <span v-intersect="onLastNotificationIntersect" />
           <span v-intersect="onLastNotificationIntersect" />
 
 
           <v-row
           <v-row
-              v-if="pending"
-              class="fill-height mt-3 mb-3"
-              align="center"
-              justify="center"
+            v-if="pending"
+            class="fill-height mt-3 mb-3"
+            align="center"
+            justify="center"
           >
           >
-            <v-progress-circular
-                indeterminate
-                color="neutral"
-            />
+            <v-progress-circular indeterminate color="neutral" />
           </v-row>
           </v-row>
-
         </v-list>
         </v-list>
-
       </v-card-text>
       </v-card-text>
 
 
       <v-card-actions class="ma-0 pa-0">
       <v-card-actions class="ma-0 pa-0">
@@ -79,11 +70,11 @@
           :href="notificationUrl"
           :href="notificationUrl"
           router
           router
           class="theme-primary"
           class="theme-primary"
-          style="width: 100%; height: 52px;"
+          style="width: 100%; height: 52px"
         >
         >
           <v-list-item-title
           <v-list-item-title
-              class="text-body-2"
-              v-text="$t('all_notification')"
+            class="text-body-2"
+            v-text="$t('all_notification')"
           />
           />
         </v-list-item>
         </v-list-item>
       </v-card-actions>
       </v-card-actions>
@@ -92,18 +83,18 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {NOTIFICATION_TYPE} from "~/types/enum/enums";
-import Notification from "~/models/Core/Notification";
-import NotificationUsers from "~/models/Core/NotificationUsers";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import {computed, ref} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import type {AnyJson, Pagination} from "~/types/data";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import UrlUtils from "~/services/utils/urlUtils";
-import {useRepo} from "pinia-orm";
-import NotificationRepository from "~/stores/repositories/NotificationRepository";
+import { NOTIFICATION_TYPE } from '~/types/enum/enums'
+import Notification from '~/models/Core/Notification'
+import NotificationUsers from '~/models/Core/NotificationUsers'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { computed, ref } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import type { AnyJson, Pagination } from '~/types/data'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import UrlUtils from '~/services/utils/urlUtils'
+import { useRepo } from 'pinia-orm'
+import NotificationRepository from '~/stores/repositories/NotificationRepository'
 
 
 const accessProfileStore = useAccessProfileStore()
 const accessProfileStore = useAccessProfileStore()
 
 
@@ -121,10 +112,14 @@ const { fetchCollection } = useEntityFetch()
 const notificationRepo = useRepo(NotificationRepository)
 const notificationRepo = useRepo(NotificationRepository)
 
 
 const query: ComputedRef<AnyJson> = computed(() => {
 const query: ComputedRef<AnyJson> = computed(() => {
-  return { 'page': page.value }
+  return { page: page.value }
 })
 })
 
 
-let { data: collection, pending, refresh } = await fetchCollection(Notification, null, query)
+let {
+  data: collection,
+  pending,
+  refresh,
+} = await fetchCollection(Notification, null, query)
 
 
 /**
 /**
  * On récupère les Notifications via le store (sans ça, les mises à jour SSE ne seront pas prises en compte)
  * On récupère les Notifications via le store (sans ça, les mises à jour SSE ne seront pas prises en compte)
@@ -144,10 +139,15 @@ 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 (!pending.value && collection.value !== null) ? collection.value.pagination : {}
+  return !pending.value && collection.value !== null
+    ? collection.value.pagination
+    : {}
 })
 })
 
 
-const notificationUrl = UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#/notifications/list/')
+const notificationUrl = UrlUtils.join(
+  runtimeConfig.baseUrlAdminLegacy,
+  '#/notifications/list/',
+)
 
 
 /**
 /**
  * L'utilisateur a fait défiler le menu jusqu'à la dernière notification affichée
  * L'utilisateur a fait défiler le menu jusqu'à la dernière notification affichée
@@ -165,10 +165,10 @@ const onLastNotificationIntersect = (isIntersecting: boolean) => {
  */
  */
 const update = async () => {
 const update = async () => {
   if (
   if (
-      !pending.value &&
-      pagination.value &&
-      pagination.value.next &&
-      pagination.value.next > 0
+    !pending.value &&
+    pagination.value &&
+    pagination.value.next &&
+    pagination.value.next > 0
   ) {
   ) {
     pending.value = true
     pending.value = true
     page.value = pagination.value.next
     page.value = pagination.value.next
@@ -185,20 +185,27 @@ const update = async () => {
  * @param notification
  * @param notification
  */
  */
 const getMessage = (notification: Notification) => {
 const getMessage = (notification: Notification) => {
-  switch (notification.type){
-    case NOTIFICATION_TYPE.FILE :
-      return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t('is_ready_to_be_downloaded')}`
+  switch (notification.type) {
+    case NOTIFICATION_TYPE.FILE:
+      return `${i18n.t('your_file')} ${notification.message?.fileName} ${i18n.t(
+        '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')} ${notification.message?.about ?? ''} ${i18n.t('has_been_sent')} `
+      return `${i18n.t('your_message')} ${
+        notification.message?.about ?? ''
+      } ${i18n.t('has_been_sent')} `
 
 
-    case NOTIFICATION_TYPE.SYSTEM :
+    case NOTIFICATION_TYPE.SYSTEM:
       if (notification.message?.about)
       if (notification.message?.about)
         return `${i18n.t(notification.message.about)}`
         return `${i18n.t(notification.message.about)}`
-      break;
+      break
 
 
     default:
     default:
       return i18n.t(notification.name)
       return i18n.t(notification.name)
@@ -209,7 +216,7 @@ const getMessage = (notification: Notification) => {
  * Dès la fermeture du menu, on indique que les notifications non lues, le sont.
  * Dès la fermeture du menu, on indique que les notifications non lues, le sont.
  */
  */
 const unwatch = watch(isOpen, (newValue, oldValue) => {
 const unwatch = watch(isOpen, (newValue, oldValue) => {
-  if (!newValue){
+  if (!newValue) {
     markNotificationsAsRead()
     markNotificationsAsRead()
   }
   }
 })
 })
@@ -225,9 +232,9 @@ const markNotificationAsRead = (notification: Notification) => {
     throw new Error('Current access id is null')
     throw new Error('Current access id is null')
   }
   }
   const notificationUsers = em.newInstance(NotificationUsers, {
   const notificationUsers = em.newInstance(NotificationUsers, {
-    access:`/api/accesses/${accessProfileStore.currentAccessId}`,
-    notification:`/api/notifications/${notification.id}`,
-    isRead: true
+    access: `/api/accesses/${accessProfileStore.currentAccessId}`,
+    notification: `/api/notifications/${notification.id}`,
+    isRead: true,
   })
   })
 
 
   em.persist(NotificationUsers, notificationUsers)
   em.persist(NotificationUsers, notificationUsers)
@@ -254,29 +261,27 @@ const download = (link: string) => {
   }
   }
   // TODO: passer cette logique dans un service ; tester ; voir si possible de réunir avec composables/utils/useDownloadFile.ts
   // TODO: passer cette logique dans un service ; tester ; voir si possible de réunir avec composables/utils/useDownloadFile.ts
 
 
-  const path: string = link.split('/api')[1];
+  const path: string = link.split('/api')[1]
 
 
   // En switch : https://api.test5.opentalent.fr/api/{accessId}/{switchId}/files/{fileId}/download
   // En switch : https://api.test5.opentalent.fr/api/{accessId}/{switchId}/files/{fileId}/download
   // Sans switch : https://local.api.opentalent.fr/api/{accessId}/files/{fileId}/download
   // Sans switch : https://local.api.opentalent.fr/api/{accessId}/files/{fileId}/download
   const url = UrlUtils.join(
   const url = UrlUtils.join(
-      runtimeConfig.baseUrlLegacy,
-      'api',
-      String(accessProfileStore.id),
-      String(accessProfileStore.switchId || ''),
-      path
+    runtimeConfig.baseUrlLegacy,
+    'api',
+    String(accessProfileStore.id),
+    String(accessProfileStore.switchId || ''),
+    path,
   )
   )
 
 
-  window.open(url);
+  window.open(url)
 }
 }
-
-
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .list_item{
-    white-space: normal;
-  }
-  .unread{
-    background: rgb(var(--v-theme-neutral-soft, white));
-  }
+.list_item {
+  white-space: normal;
+}
+.unread {
+  background: rgb(var(--v-theme-neutral-soft, white));
+}
 </style>
 </style>

+ 89 - 89
components/Layout/Header/UniversalCreation/Card.vue

@@ -19,15 +19,15 @@
     <v-row :no-gutters="true" style="height: 100px">
     <v-row :no-gutters="true" style="height: 100px">
       <v-col cols="3" class="flex-grow-0 flex-shrink-0 d-flex justify-center">
       <v-col cols="3" class="flex-grow-0 flex-shrink-0 d-flex justify-center">
         <v-icon
         <v-icon
-            :icon="icon"
-            size="50"
-            class="ma-2 pa-2 align-self-center text-neutral-strong"
+          :icon="icon"
+          size="50"
+          class="ma-2 pa-2 align-self-center text-neutral-strong"
         />
         />
       </v-col>
       </v-col>
       <v-col
       <v-col
-          cols="9"
-          align-self="center"
-          class="pl-2 infos-container flex-grow-1 flex-shrink-1"
+        cols="9"
+        align-self="center"
+        class="pl-2 infos-container flex-grow-1 flex-shrink-1"
       >
       >
         <h4 class="text-primary">{{ $t(title) }}</h4>
         <h4 class="text-primary">{{ $t(title) }}</h4>
         <p class="text-neutral-strong">
         <p class="text-neutral-strong">
@@ -39,100 +39,100 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import type {PropType} from "@vue/runtime-core";
-  import {MENU_LINK_TYPE} from "~/types/enum/layout";
-  import {useAdminUrl} from "~/composables/utils/useAdminUrl";
-  import UrlUtils from "~/services/utils/urlUtils";
+import type { PropType } from '@vue/runtime-core'
+import { MENU_LINK_TYPE } from '~/types/enum/layout'
+import { useAdminUrl } from '~/composables/utils/useAdminUrl'
+import UrlUtils from '~/services/utils/urlUtils'
 
 
-  const props = defineProps({
-    /**
-     * Target location in the wizard
-     */
-    to: {
-      type: String,
-      required: false,
-      default: null
-    },
-    /**
-     * Target url
-     */
-    href: {
-      type: String,
-      required: false,
-      default: null
-    },
-    /**
-     * Target url
-     */
-    linkType: {
-      type: Number as PropType<MENU_LINK_TYPE>,
-      required: false,
-      default: MENU_LINK_TYPE.V1
-    },
-    /**
-     * Title displayed on the card
-     */
-    title: {
-      type: String,
-      required: true
-    },
-    /**
-     * Description displayed on the card
-     */
-    textContent: {
-      type: String,
-      required: true
-    },
-    /**
-     * Icon displayed on the card
-     */
-    icon: {
-      type: String,
-      required: true
-    }
-  })
+const props = defineProps({
+  /**
+   * Target location in the wizard
+   */
+  to: {
+    type: String,
+    required: false,
+    default: null,
+  },
+  /**
+   * Target url
+   */
+  href: {
+    type: String,
+    required: false,
+    default: null,
+  },
+  /**
+   * Target url
+   */
+  linkType: {
+    type: Number as PropType<MENU_LINK_TYPE>,
+    required: false,
+    default: MENU_LINK_TYPE.V1,
+  },
+  /**
+   * Title displayed on the card
+   */
+  title: {
+    type: String,
+    required: true,
+  },
+  /**
+   * Description displayed on the card
+   */
+  textContent: {
+    type: String,
+    required: true,
+  },
+  /**
+   * Icon displayed on the card
+   */
+  icon: {
+    type: String,
+    required: true,
+  },
+})
 
 
-  const emit = defineEmits(['click'])
+const emit = defineEmits(['click'])
 
 
-  const { makeAdminUrl } = useAdminUrl()
+const { makeAdminUrl } = useAdminUrl()
 
 
-  let url: string | null = null;
+let url: string | null = null
 
 
-  if (props.href !== null) {
-    switch (props.linkType) {
-      case MENU_LINK_TYPE.V1:
-        url = makeAdminUrl(props.href)
-        break;
-      case MENU_LINK_TYPE.EXTERNAL:
-        url = UrlUtils.prependHttps(props.href)
-        break;
-      default:
-        url = props.href
-    }
+if (props.href !== null) {
+  switch (props.linkType) {
+    case MENU_LINK_TYPE.V1:
+      url = makeAdminUrl(props.href)
+      break
+    case MENU_LINK_TYPE.EXTERNAL:
+      url = UrlUtils.prependHttps(props.href)
+      break
+    default:
+      url = props.href
   }
   }
+}
 
 
-  const onClick = () => {
-    emit('click', props.to, url)
-  }
+const onClick = () => {
+  emit('click', props.to, url)
+}
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
-  h4 {
-    font-size: 15px;
-    font-weight: bold;
-    margin-bottom: 6px;
-  }
+h4 {
+  font-size: 15px;
+  font-weight: bold;
+  margin-bottom: 6px;
+}
 
 
-  p {
-    font-size: 13px;
-  }
+p {
+  font-size: 13px;
+}
 
 
-  .infos-container {
-    padding: 15px 0;
-  }
+.infos-container {
+  padding: 15px 0;
+}
 
 
-  .v-card:hover {
-    cursor: pointer;
-    background: rgb(var(--v-theme-primary-alt));
-  }
+.v-card:hover {
+  cursor: pointer;
+  background: rgb(var(--v-theme-primary-alt));
+}
 </style>
 </style>

+ 153 - 137
components/Layout/Header/UniversalCreation/CreateButton.vue

@@ -5,56 +5,74 @@
 <template>
 <template>
   <main>
   <main>
     <v-btn
     <v-btn
-        v-if="asIcon"
-        :elevation="0"
-        class="theme-primary"
-        :icon="true"
-        size="small"
-        @click="show"
+      v-if="asIcon"
+      :elevation="0"
+      class="theme-primary"
+      :icon="true"
+      size="small"
+      @click="show"
     >
     >
       <v-icon>fas fa-plus</v-icon>
       <v-icon>fas fa-plus</v-icon>
     </v-btn>
     </v-btn>
 
 
     <v-btn
     <v-btn
-        v-else
-        :elevation="2"
-        height="30"
-        class="theme-x-create-btn"
-        @click="show"
+      v-else
+      :elevation="2"
+      height="30"
+      class="theme-x-create-btn"
+      @click="show"
     >
     >
       <span>{{ $t('create') }}</span>
       <span>{{ $t('create') }}</span>
     </v-btn>
     </v-btn>
 
 
-    <LayoutDialog :show="showCreateDialog" :max-width="850" >
+    <LayoutDialog :show="showCreateDialog" :max-width="850">
       <template #dialogType>{{ $t('creative_assistant') }}</template>
       <template #dialogType>{{ $t('creative_assistant') }}</template>
 
 
       <template #dialogTitle>
       <template #dialogTitle>
-        <span v-if="location === 'home'">{{ $t('what_do_you_want_to_create') }}</span>
-        <span v-else-if="location === 'access'">{{ $t('what_type_of_contact_do_you_want_to_create') }}</span>
-        <span v-else-if="location === 'event'">{{ $t('what_do_you_want_to_add_to_your_planning') }}</span>
-        <span v-else-if="location === 'message'">{{ $t('what_do_you_want_to_send') }}</span>
-        <span v-else-if="location === 'event-params'">{{ $t('which_date_and_which_hour') }}</span>
+        <span v-if="location === 'home'">{{
+          $t('what_do_you_want_to_create')
+        }}</span>
+        <span v-else-if="location === 'access'">{{
+          $t('what_type_of_contact_do_you_want_to_create')
+        }}</span>
+        <span v-else-if="location === 'event'">{{
+          $t('what_do_you_want_to_add_to_your_planning')
+        }}</span>
+        <span v-else-if="location === 'message'">{{
+          $t('what_do_you_want_to_send')
+        }}</span>
+        <span v-else-if="location === 'event-params'">{{
+          $t('which_date_and_which_hour')
+        }}</span>
       </template>
       </template>
 
 
       <template #dialogText>
       <template #dialogText>
-         <LayoutHeaderUniversalCreationGenerateCardsSteps
-             :path="path"
-             @cardClick="onCardClick"
-             @urlUpdate="onUrlUpdate"
-         />
+        <LayoutHeaderUniversalCreationGenerateCardsSteps
+          :path="path"
+          @cardClick="onCardClick"
+          @urlUpdate="onUrlUpdate"
+        />
       </template>
       </template>
 
 
       <template #dialogBtn>
       <template #dialogBtn>
         <div class="text-center">
         <div class="text-center">
-          <v-btn class="theme-neutral-soft" @click="close" >
+          <v-btn class="theme-neutral-soft" @click="close">
             {{ $t('cancel') }}
             {{ $t('cancel') }}
           </v-btn>
           </v-btn>
 
 
-          <v-btn v-if="path.length > 1" class="theme-neutral-soft" @click="goToPrevious" >
+          <v-btn
+            v-if="path.length > 1"
+            class="theme-neutral-soft"
+            @click="goToPrevious"
+          >
             {{ $t('previous_step') }}
             {{ $t('previous_step') }}
           </v-btn>
           </v-btn>
 
 
-          <v-btn v-if="targetUrl !== null && !directRedirectionOngoing" class="theme-primary" @click="validate" >
+          <v-btn
+            v-if="targetUrl !== null && !directRedirectionOngoing"
+            class="theme-primary"
+            @click="validate"
+          >
             {{ $t('validate') }}
             {{ $t('validate') }}
           </v-btn>
           </v-btn>
         </div>
         </div>
@@ -64,122 +82,120 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {ref} from "@vue/reactivity";
-  import type {Ref} from "@vue/reactivity";
-  import {useDisplay} from "vuetify";
-  import type {ComputedRef} from "vue";
-  import {usePageStore} from "~/stores/page";
-
-  const { mdAndDown: asIcon } = useDisplay()
-
-  // Set to true to show the Create dialog
-  const showCreateDialog: Ref<boolean> = ref(false);
-
-  // The succession of menus the user has been through; used to keep track of the navigation
-  const path: Ref<Array<string>> = ref(['home'])
-
-  // The current menu
-  const location: ComputedRef<string> = computed(() => {
-    return path.value.at(-1) ?? 'home'
-  })
-
-  // The current target URL (@see onUrlUpdate())
-  const targetUrl: Ref<string | null> = ref(null)
-
-  // Already redirecting (to avoid the display of the 'validate' button when page has already been redirected and is loading)
-  const directRedirectionOngoing: Ref<boolean> = ref(false)
-
-  /**
-   * Return to the home menu
-   */
-  const reset = () => {
-    path.value = ['home']
-  }
-
-  /**
-   * Go back to the previous step
-   */
-  const goToPrevious = () => {
-    if (path.value.length === 1) {
-      return
-    }
-    path.value.pop()
-  }
-
-  /**
-   * Display the create dialog
-   */
-  const show = () => {
-    reset()
-    showCreateDialog.value = true
+import { ref } from '@vue/reactivity'
+import type { Ref } from '@vue/reactivity'
+import { useDisplay } from 'vuetify'
+import type { ComputedRef } from 'vue'
+import { usePageStore } from '~/stores/page'
+
+const { mdAndDown: asIcon } = useDisplay()
+
+// Set to true to show the Create dialog
+const showCreateDialog: Ref<boolean> = ref(false)
+
+// The succession of menus the user has been through; used to keep track of the navigation
+const path: Ref<Array<string>> = ref(['home'])
+
+// The current menu
+const location: ComputedRef<string> = computed(() => {
+  return path.value.at(-1) ?? 'home'
+})
+
+// The current target URL (@see onUrlUpdate())
+const targetUrl: Ref<string | null> = ref(null)
+
+// Already redirecting (to avoid the display of the 'validate' button when page has already been redirected and is loading)
+const directRedirectionOngoing: Ref<boolean> = ref(false)
+
+/**
+ * Return to the home menu
+ */
+const reset = () => {
+  path.value = ['home']
+}
+
+/**
+ * Go back to the previous step
+ */
+const goToPrevious = () => {
+  if (path.value.length === 1) {
+    return
   }
   }
-
-  const pageStore = usePageStore()
-
-  /**
-   * Redirect the user to the given url
-   * @param url
-   */
-  const redirect = (url: string) => {
-    pageStore.loading = true
-    window.location.href = url
-  }
-
-  /**
-   * Go to the current targetUrl
-   */
-  const validate = () => {
-    if (targetUrl.value === null) {
-      console.warn('No url defined')
-      return
-    }
-    redirect(targetUrl.value)
+  path.value.pop()
+}
+
+/**
+ * Display the create dialog
+ */
+const show = () => {
+  reset()
+  showCreateDialog.value = true
+}
+
+const pageStore = usePageStore()
+
+/**
+ * Redirect the user to the given url
+ * @param url
+ */
+const redirect = (url: string) => {
+  pageStore.loading = true
+  window.location.href = url
+}
+
+/**
+ * Go to the current targetUrl
+ */
+const validate = () => {
+  if (targetUrl.value === null) {
+    console.warn('No url defined')
+    return
   }
   }
-
-  /**
-   * Close the Create dialog
-   */
-  const close = () => {
-    showCreateDialog.value = false
-  }
-
-  /**
-   * A cart has been clicked. The reaction depends on the card's properties.
-   *
-   * @param to  Target location in the wizard
-   * @param href  Target absolute url
-   */
-  const onCardClick = (to: string | null, href: string | null) => {
-    if (to !== null) {
-      // La carte définit une nouvelle destination : on se dirige vers elle.
-      path.value.push(to)
-
-    } else if (href !== null) {
-      // La carte définit une url avec href, et pas de nouvelle destination : on suit directement le lien pour éviter
-      // l'étape de validation devenue inutile.
-      directRedirectionOngoing.value = true
-      redirect(href)
-
-    } else {
-      console.warn('Error: card has no `to` nor `href` defined')
-    }
-  }
-
-  /**
-   * The url has been updated in the GenerateCardsStep component
-   * @param url
-   */
-  const onUrlUpdate = (url: string) => {
-    targetUrl.value = url
+  redirect(targetUrl.value)
+}
+
+/**
+ * Close the Create dialog
+ */
+const close = () => {
+  showCreateDialog.value = false
+}
+
+/**
+ * A cart has been clicked. The reaction depends on the card's properties.
+ *
+ * @param to  Target location in the wizard
+ * @param href  Target absolute url
+ */
+const onCardClick = (to: string | null, href: string | null) => {
+  if (to !== null) {
+    // La carte définit une nouvelle destination : on se dirige vers elle.
+    path.value.push(to)
+  } else if (href !== null) {
+    // La carte définit une url avec href, et pas de nouvelle destination : on suit directement le lien pour éviter
+    // l'étape de validation devenue inutile.
+    directRedirectionOngoing.value = true
+    redirect(href)
+  } else {
+    console.warn('Error: card has no `to` nor `href` defined')
   }
   }
+}
+
+/**
+ * The url has been updated in the GenerateCardsStep component
+ * @param url
+ */
+const onUrlUpdate = (url: string) => {
+  targetUrl.value = url
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  :deep(.v-btn .v-icon) {
-    font-size: 16px !important;
-  }
-  :deep(.v-btn) {
-    text-transform: none !important;
-    font-weight: 600;
-  }
+:deep(.v-btn .v-icon) {
+  font-size: 16px !important;
+}
+:deep(.v-btn) {
+  text-transform: none !important;
+  font-weight: 600;
+}
 </style>
 </style>

+ 72 - 62
components/Layout/Header/UniversalCreation/EventParams.vue

@@ -17,7 +17,10 @@ Event parameters page in the create dialog
     <v-row v-show="eventStart < now" class="anteriorDateWarning mt-0">
     <v-row v-show="eventStart < now" class="anteriorDateWarning mt-0">
       <v-col cols="2" class="pt-1"></v-col>
       <v-col cols="2" class="pt-1"></v-col>
       <v-col cols="9" class="pt-1">
       <v-col cols="9" class="pt-1">
-        <i class="fa fa-circle-info" /> {{ $t('please_note_that_this_reservation_start_on_date_anterior_to_now') }}
+        <i class="fa fa-circle-info" />
+        {{
+          $t('please_note_that_this_reservation_start_on_date_anterior_to_now')
+        }}
       </v-col>
       </v-col>
     </v-row>
     </v-row>
 
 
@@ -36,7 +39,6 @@ Event parameters page in the create dialog
         <UiInputNumber v-model="eventDurationMinutes" class="mx-3" :min="0" />
         <UiInputNumber v-model="eventDurationMinutes" class="mx-3" :min="0" />
         <span>{{ $t('minute(s)') }}</span>
         <span>{{ $t('minute(s)') }}</span>
       </v-col>
       </v-col>
-
     </v-row>
     </v-row>
 
 
     <v-row>
     <v-row>
@@ -52,69 +54,77 @@ Event parameters page in the create dialog
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {ref} from "@vue/reactivity";
-  import type {Ref} from "@vue/reactivity";
-  import {add, format, startOfHour, formatISO} from "date-fns";
-  import type {ComputedRef} from "vue";
-  import DateUtils, {supportedLocales} from "~/services/utils/dateUtils";
-
-  const i18n = useI18n()
-
-  // An event is sent each time the resulting params are updated
-  const emit = defineEmits(['paramsUpdated'])
-
-  // Get the start of the next hour as a default event start
-  const now: Date = new Date()
-  const eventStart: Ref<Date> = ref(startOfHour(add(now, { 'hours': 1 })))
-
-  const eventDurationDays: Ref<number> = ref(0)
-  const eventDurationHours: Ref<number> = ref(1)
-  const eventDurationMinutes: Ref<number> = ref(0)
-
-  // Duration of the events, in minutes
-  const eventDuration: ComputedRef<number> = computed(() => {
-    return (eventDurationDays.value * 24 * 60) + (eventDurationHours.value * 60) + eventDurationMinutes.value
+import { ref } from '@vue/reactivity'
+import type { Ref } from '@vue/reactivity'
+import { add, format, startOfHour, formatISO } from 'date-fns'
+import type { ComputedRef } from 'vue'
+import DateUtils, { supportedLocales } from '~/services/utils/dateUtils'
+
+const i18n = useI18n()
+
+// An event is sent each time the resulting params are updated
+const emit = defineEmits(['paramsUpdated'])
+
+// Get the start of the next hour as a default event start
+const now: Date = new Date()
+const eventStart: Ref<Date> = ref(startOfHour(add(now, { hours: 1 })))
+
+const eventDurationDays: Ref<number> = ref(0)
+const eventDurationHours: Ref<number> = ref(1)
+const eventDurationMinutes: Ref<number> = ref(0)
+
+// Duration of the events, in minutes
+const eventDuration: ComputedRef<number> = computed(() => {
+  return (
+    eventDurationDays.value * 24 * 60 +
+    eventDurationHours.value * 60 +
+    eventDurationMinutes.value
+  )
+})
+
+// Event end
+const eventEnd: ComputedRef<Date> = computed(() =>
+  add(eventStart.value, { minutes: eventDuration.value }),
+)
+
+const fnsLocale = DateUtils.getFnsLocale(i18n.locale.value as supportedLocales)
+const formattedEventEnd: ComputedRef<string> = computed(() => {
+  return format(eventEnd.value, 'EEEE dd MMMM yyyy HH:mm', {
+    locale: fnsLocale,
   })
   })
+})
 
 
-  // Event end
-  const eventEnd: ComputedRef<Date> = computed(() => add(eventStart.value, { 'minutes': eventDuration.value }))
-
-  const fnsLocale = DateUtils.getFnsLocale(i18n.locale.value as supportedLocales)
-  const formattedEventEnd: ComputedRef<string> = computed(() => {
-    return format(eventEnd.value, 'EEEE dd MMMM yyyy HH:mm', {locale: fnsLocale})
-  })
-
-  // Build the event params
-  const params: ComputedRef<{'start': string, 'end': string}> = computed(() => {
-    return {
-      'start': formatISO(eventStart.value),
-      'end': formatISO(eventEnd.value),
-    }
-  })
-
-  // Send an update event as soon as the page is mounted
-  onMounted(() => {
-    emit('paramsUpdated', params.value)
-  })
-
-  // Send an update event every time the params change
-  const unwatch = watch(params, (newParams) => {
-    emit('paramsUpdated', newParams)
-  })
-  onUnmounted(() => {
-    unwatch()
-  })
+// Build the event params
+const params: ComputedRef<{ start: string; end: string }> = computed(() => {
+  return {
+    start: formatISO(eventStart.value),
+    end: formatISO(eventEnd.value),
+  }
+})
+
+// Send an update event as soon as the page is mounted
+onMounted(() => {
+  emit('paramsUpdated', params.value)
+})
+
+// Send an update event every time the params change
+const unwatch = watch(params, (newParams) => {
+  emit('paramsUpdated', newParams)
+})
+onUnmounted(() => {
+  unwatch()
+})
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .endDate {
-    font-weight: 600;
-    text-transform: capitalize;
-    color: rgb(var(--v-theme-on-neutral));
-  }
-
-  .anteriorDateWarning {
-    color: rgb(var(--v-theme-info));
-    font-weight: 600;
-  }
+.endDate {
+  font-weight: 600;
+  text-transform: capitalize;
+  color: rgb(var(--v-theme-on-neutral));
+}
+
+.anteriorDateWarning {
+  color: rgb(var(--v-theme-info));
+  font-weight: 600;
+}
 </style>
 </style>

+ 190 - 178
components/Layout/Header/UniversalCreation/GenerateCardsSteps.vue

@@ -3,57 +3,67 @@
 -->
 -->
 
 
 <template>
 <template>
-
   <!-- Menu Accueil -->
   <!-- Menu Accueil -->
   <v-container v-if="location === 'home'">
   <v-container v-if="location === 'home'">
     <v-row>
     <v-row>
-
       <!-- Une personne -->
       <!-- Une personne -->
       <v-col cols="6" v-if="ability.can('manage', 'users')">
       <v-col cols="6" v-if="ability.can('manage', 'users')">
-          <LayoutHeaderUniversalCreationCard
-              to="access"
-              title="a_person"
-              text-content="add_new_person_student"
-              icon="fa fa-user"
-              @click="onCardClick"
-          />
+        <LayoutHeaderUniversalCreationCard
+          to="access"
+          title="a_person"
+          text-content="add_new_person_student"
+          icon="fa fa-user"
+          @click="onCardClick"
+        />
       </v-col>
       </v-col>
 
 
       <!-- Un évènement -->
       <!-- Un évènement -->
-      <v-col cols="6" v-if="ability.can('display', 'agenda_page')
-                && (
-                   ability.can('display', 'course_page') ||
-                   ability.can('display', 'exam_page') ||
-                   ability.can('display', 'pedagogics_project_page')
-                )">
+      <v-col
+        cols="6"
+        v-if="
+          ability.can('display', 'agenda_page') &&
+          (ability.can('display', 'course_page') ||
+            ability.can('display', 'exam_page') ||
+            ability.can('display', 'pedagogics_project_page'))
+        "
+      >
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event"
-            title="an_event"
-            text-content="add_an_event_course"
-            icon="fa fa-calendar-alt"
-            @click="onCardClick"
+          to="event"
+          title="an_event"
+          text-content="add_an_event_course"
+          icon="fa fa-calendar-alt"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Autre évènement -->
       <!-- Autre évènement -->
-      <v-col cols="6" v-else-if="ability.can('display', 'agenda_page') && ability.can('manage', 'events')">
+      <v-col
+        cols="6"
+        v-else-if="
+          ability.can('display', 'agenda_page') &&
+          ability.can('manage', 'events')
+        "
+      >
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event-params"
-            title="other_event"
-            text-content="other_event_text_creation_card"
-            icon="far fa-calendar"
-            href="/calendar/create/events"
-            @click="onCardClick"
+          to="event-params"
+          title="other_event"
+          text-content="other_event_text_creation_card"
+          icon="far fa-calendar"
+          href="/calendar/create/events"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Une correspondance -->
       <!-- Une correspondance -->
-      <v-col cols="6" v-if="ability.can('display', 'message_send_page')
-                   && (
-                    ability.can('manage', 'emails') ||
-                    ability.can('manage', 'mails') ||
-                    ability.can('manage', 'texto')
-                  )">
+      <v-col
+        cols="6"
+        v-if="
+          ability.can('display', 'message_send_page') &&
+          (ability.can('manage', 'emails') ||
+            ability.can('manage', 'mails') ||
+            ability.can('manage', 'texto'))
+        "
+      >
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
           to="message"
           to="message"
           title="a_correspondence"
           title="a_correspondence"
@@ -83,99 +93,99 @@
       <!-- Un adhérent -->
       <!-- Un adhérent -->
       <v-col cols="6" v-if="isLaw1901">
       <v-col cols="6" v-if="isLaw1901">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="an_adherent"
-            text-content="adherent_text_creation_card"
-            icon="fa fa-user"
-            href="/universal_creation_person/adherent"
-            @click="onCardClick"
+          title="an_adherent"
+          text-content="adherent_text_creation_card"
+          icon="fa fa-user"
+          href="/universal_creation_person/adherent"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un membre du CA -->
       <!-- Un membre du CA -->
       <v-col cols="6" v-if="isLaw1901">
       <v-col cols="6" v-if="isLaw1901">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_ca_member"
-            text-content="ca_member_text_creation_card"
-            icon="fa fa-users"
-            href="/universal_creation_person/ca_member"
-            @click="onCardClick"
+          title="a_ca_member"
+          text-content="ca_member_text_creation_card"
+          icon="fa fa-users"
+          href="/universal_creation_person/ca_member"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un élève -->
       <!-- Un élève -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_student"
-            text-content="student_text_creation_card"
-            icon="fa fa-user"
-            href="/universal_creation_person/student"
-            @click="onCardClick"
+          title="a_student"
+          text-content="student_text_creation_card"
+          icon="fa fa-user"
+          href="/universal_creation_person/student"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un tuteur -->
       <!-- Un tuteur -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_guardian"
-            text-content="guardian_text_creation_card"
-            icon="fa fa-female"
-            href="/universal_creation_person/guardian"
-            @click="onCardClick"
+          title="a_guardian"
+          text-content="guardian_text_creation_card"
+          icon="fa fa-female"
+          href="/universal_creation_person/guardian"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un professeur -->
       <!-- Un professeur -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_teacher"
-            text-content="teacher_text_creation_card"
-            icon="fa fa-graduation-cap"
-            href="/universal_creation_person/teacher"
-            @click="onCardClick"
+          title="a_teacher"
+          text-content="teacher_text_creation_card"
+          icon="fa fa-graduation-cap"
+          href="/universal_creation_person/teacher"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un membre du personnel -->
       <!-- Un membre du personnel -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_member_of_staff"
-            text-content="personnel_text_creation_card"
-            icon="fa fa-suitcase"
-            href="/universal_creation_person/personnel"
-            @click="onCardClick"
+          title="a_member_of_staff"
+          text-content="personnel_text_creation_card"
+          icon="fa fa-suitcase"
+          href="/universal_creation_person/personnel"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Une entité légale -->
       <!-- Une entité légale -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_legal_entity"
-            text-content="moral_text_creation_card"
-            icon="fa fa-building"
-            href="/universal_creation_person/company"
-            @click="onCardClick"
+          title="a_legal_entity"
+          text-content="moral_text_creation_card"
+          icon="fa fa-building"
+          href="/universal_creation_person/company"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Une inscription en ligne -->
       <!-- Une inscription en ligne -->
       <v-col cols="6" v-if="hasOnlineRegistrationModule">
       <v-col cols="6" v-if="hasOnlineRegistrationModule">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="online_registration"
-            text-content="online_registration_text_creation_card"
-            icon="fa fa-list-alt"
-            href="/online/registration/new_registration"
-            @click="onCardClick"
+          title="online_registration"
+          text-content="online_registration_text_creation_card"
+          icon="fa fa-list-alt"
+          href="/online/registration/new_registration"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un autre type de contact -->
       <!-- Un autre type de contact -->
       <v-col cols="6">
       <v-col cols="6">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="another_type_of_contact"
-            text-content="other_contact_text_creation_card"
-            icon="fa fa-plus"
-            href="/universal_creation_person/other_contact"
-            @click="onCardClick"
+          title="another_type_of_contact"
+          text-content="other_contact_text_creation_card"
+          icon="fa fa-plus"
+          href="/universal_creation_person/other_contact"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
     </v-row>
     </v-row>
@@ -187,48 +197,48 @@
       <!-- Un cours -->
       <!-- Un cours -->
       <v-col cols="6" v-if="ability.can('display', 'course_page')">
       <v-col cols="6" v-if="ability.can('display', 'course_page')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event-params"
-            href="/calendar/create/courses"
-            title="course"
-            text-content="course_text_creation_card"
-            icon="fa fa-users"
-            @click="onCardClick"
+          to="event-params"
+          href="/calendar/create/courses"
+          title="course"
+          text-content="course_text_creation_card"
+          icon="fa fa-users"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un examen -->
       <!-- Un examen -->
       <v-col cols="6" v-if="ability.can('display', 'exam_page')">
       <v-col cols="6" v-if="ability.can('display', 'exam_page')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event-params"
-            href="/calendar/create/examens"
-            title="exam"
-            text-content="exam_text_creation_card"
-            icon="fa fa-graduation-cap"
-            @click="onCardClick"
+          to="event-params"
+          href="/calendar/create/examens"
+          title="exam"
+          text-content="exam_text_creation_card"
+          icon="fa fa-graduation-cap"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un projet pédagogique -->
       <!-- Un projet pédagogique -->
       <v-col cols="6" v-if="ability.can('display', 'pedagogics_project_page')">
       <v-col cols="6" v-if="ability.can('display', 'pedagogics_project_page')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event-params"
-            href="/calendar/create/educational_projects"
-            title="educational_services"
-            text-content="educational_services_text_creation_card"
-            icon="fa fa-suitcase"
-            @click="onCardClick"
+          to="event-params"
+          href="/calendar/create/educational_projects"
+          title="educational_services"
+          text-content="educational_services_text_creation_card"
+          icon="fa fa-suitcase"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un autre évènement -->
       <!-- Un autre évènement -->
       <v-col cols="6" v-if="ability.can('manage', 'events')">
       <v-col cols="6" v-if="ability.can('manage', 'events')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            to="event-params"
-            href="/calendar/create/events"
-            title="other_event"
-            text-content="other_event_text_creation_card"
-            icon="far fa-calendar"
-            @click="onCardClick"
+          to="event-params"
+          href="/calendar/create/events"
+          title="other_event"
+          text-content="other_event_text_creation_card"
+          icon="far fa-calendar"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
     </v-row>
     </v-row>
@@ -240,33 +250,33 @@
       <!-- Un email -->
       <!-- Un email -->
       <v-col cols="6" v-if="ability.can('manage', 'emails')">
       <v-col cols="6" v-if="ability.can('manage', 'emails')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="an_email"
-            text-content="email_text_creation_card"
-            icon="far fa-envelope"
-            href="/list/create/emails"
-            @click="onCardClick"
+          title="an_email"
+          text-content="email_text_creation_card"
+          icon="far fa-envelope"
+          href="/list/create/emails"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un courrier -->
       <!-- Un courrier -->
       <v-col cols="6" v-if="ability.can('manage', 'mails')">
       <v-col cols="6" v-if="ability.can('manage', 'mails')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_letter"
-            text-content="letter_text_creation_card"
-            icon="far fa-file-alt"
-            href="/list/create/mails"
-            @click="onCardClick"
+          title="a_letter"
+          text-content="letter_text_creation_card"
+          icon="far fa-file-alt"
+          href="/list/create/mails"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
 
 
       <!-- Un SMS -->
       <!-- Un SMS -->
       <v-col cols="6" v-if="ability.can('manage', 'texto')">
       <v-col cols="6" v-if="ability.can('manage', 'texto')">
         <LayoutHeaderUniversalCreationCard
         <LayoutHeaderUniversalCreationCard
-            title="a_sms"
-            text-content="sms_text_creation_card"
-            icon="fa fa-mobile-alt"
-            href="/list/create/sms"
-            @click="onCardClick"
+          title="a_sms"
+          text-content="sms_text_creation_card"
+          icon="fa fa-mobile-alt"
+          href="/list/create/sms"
+          @click="onCardClick"
         />
         />
       </v-col>
       </v-col>
     </v-row>
     </v-row>
@@ -274,74 +284,76 @@
 
 
   <!-- Page de pré-paramétrage des évènements -->
   <!-- Page de pré-paramétrage des évènements -->
   <LayoutHeaderUniversalCreationEventParams
   <LayoutHeaderUniversalCreationEventParams
-      v-if="location === 'event-params'"
-      @params-updated="onEventParamsUpdated"
+    v-if="location === 'event-params'"
+    @params-updated="onEventParamsUpdated"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import type {Ref} from "@vue/reactivity";
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import {useAbility} from "@casl/vue";
-  import type {ComputedRef} from "vue";
-  import UrlUtils from "~/services/utils/urlUtils";
-
-  const props = defineProps({
-    /**
-     * The path that the user followed troughout the wizard
-     */
-    path: {
-      type: Array<string>,
-      required: true
-    }
-  })
-
-  const location: ComputedRef<string> = computed(() => {
-    return props.path.at(-1) ?? 'home'
-  })
-
-  const ability = useAbility()
-
-  const organizationProfile = useOrganizationProfileStore()
-  const isLaw1901: ComputedRef<boolean> = organizationProfile.isAssociation
-  const hasOnlineRegistrationModule: Ref<boolean> = ref(organizationProfile.hasModule('IEL'))
-
-  const baseUrl: Ref<string | null> = ref(null)
-  const query: Ref<Record<string, string>> = ref({})
-
-  const url: ComputedRef<string | null> = computed(() => {
-    if (baseUrl.value === null) {
-      return null
-    }
-    return UrlUtils.addQuery(baseUrl.value, query.value)
-  })
-
-  const emit = defineEmits(['cardClick', 'urlUpdate'])
+import type { Ref } from '@vue/reactivity'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { useAbility } from '@casl/vue'
+import type { ComputedRef } from 'vue'
+import UrlUtils from '~/services/utils/urlUtils'
 
 
+const props = defineProps({
   /**
   /**
-   * Called when a card is clicked
-   * @param to  Target location in the wizard
-   * @param href  Target absolute url
+   * The path that the user followed troughout the wizard
    */
    */
-  const onCardClick = (to: string | null, href: string | null) => {
-    if (href !== null) {
-      baseUrl.value = href
-    }
-    emit('cardClick', to, url.value)
+  path: {
+    type: Array<string>,
+    required: true,
+  },
+})
+
+const location: ComputedRef<string> = computed(() => {
+  return props.path.at(-1) ?? 'home'
+})
+
+const ability = useAbility()
+
+const organizationProfile = useOrganizationProfileStore()
+const isLaw1901: ComputedRef<boolean> = organizationProfile.isAssociation
+const hasOnlineRegistrationModule: Ref<boolean> = ref(
+  organizationProfile.hasModule('IEL'),
+)
+
+const baseUrl: Ref<string | null> = ref(null)
+const query: Ref<Record<string, string>> = ref({})
+
+const url: ComputedRef<string | null> = computed(() => {
+  if (baseUrl.value === null) {
+    return null
   }
   }
-
-  /**
-   * Called when the event parameters page is updated
-   * @param event
-   */
-  const onEventParamsUpdated = (event: {'start': string, 'end': string}) => {
-    query.value = event
+  return UrlUtils.addQuery(baseUrl.value, query.value)
+})
+
+const emit = defineEmits(['cardClick', 'urlUpdate'])
+
+/**
+ * Called when a card is clicked
+ * @param to  Target location in the wizard
+ * @param href  Target absolute url
+ */
+const onCardClick = (to: string | null, href: string | null) => {
+  if (href !== null) {
+    baseUrl.value = href
   }
   }
-
-  const unwatch = watch(url, (newUrl: string | null) => {
-    emit('urlUpdate', newUrl)
-  })
-  onUnmounted(() => {
-    unwatch()
-  })
+  emit('cardClick', to, url.value)
+}
+
+/**
+ * Called when the event parameters page is updated
+ * @param event
+ */
+const onEventParamsUpdated = (event: { start: string; end: string }) => {
+  query.value = event
+}
+
+const unwatch = watch(url, (newUrl: string | null) => {
+  emit('urlUpdate', newUrl)
+})
+onUnmounted(() => {
+  unwatch()
+})
 </script>
 </script>

+ 15 - 18
components/Layout/LoadingScreen.vue

@@ -2,31 +2,28 @@
 
 
 <template>
 <template>
   <v-overlay
   <v-overlay
-          v-model="pageStore.loading"
-          z-index="9000"
-          persistent
-          class="align-center justify-center"
+    v-model="pageStore.loading"
+    z-index="9000"
+    persistent
+    class="align-center justify-center"
   >
   >
-    <v-progress-circular
-      indeterminate
-      size="64"
-    />
+    <v-progress-circular indeterminate size="64" />
   </v-overlay>
   </v-overlay>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {usePageStore} from "~/stores/page";
+import { usePageStore } from '~/stores/page'
 
 
-  const pageStore = usePageStore()
+const pageStore = usePageStore()
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
-  .loading-page {
-    position: fixed;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 100%;
-    z-index: 1001!important;
-  }
+.loading-page {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  z-index: 1001 !important;
+}
 </style>
 </style>

+ 74 - 75
components/Layout/MainMenu.vue

@@ -5,23 +5,18 @@ Prend en paramètre une liste de ItemMenu et les met en forme
 
 
 <template>
 <template>
   <v-navigation-drawer
   <v-navigation-drawer
-      v-model="displayMenu"
-      :rail="isRail"
-      :disable-resize-watcher="true"
-      class="theme-secondary main-menu"
+    v-model="displayMenu"
+    :rail="isRail"
+    :disable-resize-watcher="true"
+    class="theme-secondary main-menu"
   >
   >
     <template #prepend>
     <template #prepend>
       <slot name="title"></slot>
       <slot name="title"></slot>
     </template>
     </template>
 
 
-    <v-list
-        open-strategy="single"
-        active-class="active"
-        class="left-menu"
-    >
+    <v-list open-strategy="single" active-class="active" class="left-menu">
       <!-- TODO: que se passe-t-il si le menu ne comprend qu'un seul MenuItem? -->
       <!-- TODO: que se passe-t-il si le menu ne comprend qu'un seul MenuItem? -->
       <div v-for="(item, i) in items" :key="i">
       <div v-for="(item, i) in items" :key="i">
-
         <!-- Cas 1 : l'item n'a pas d'enfants, c'est un lien (ou le menu est en mode réduit) -->
         <!-- Cas 1 : l'item n'a pas d'enfants, c'est un lien (ou le menu est en mode réduit) -->
         <v-list-item
         <v-list-item
           v-if="!item.children || isRail"
           v-if="!item.children || isRail"
@@ -42,11 +37,11 @@ Prend en paramètre une liste de ItemMenu et les met en forme
         >
         >
           <template #activator="{ props }">
           <template #activator="{ props }">
             <v-list-item
             <v-list-item
-                v-bind="props"
-                :prepend-icon="item.icon.name"
-                :title="$t(item.label)"
-                class="theme-secondary menu-item"
-                height="48px"
+              v-bind="props"
+              :prepend-icon="item.icon.name"
+              :title="$t(item.label)"
+              class="theme-secondary menu-item"
+              height="48px"
             />
             />
           </template>
           </template>
 
 
@@ -69,16 +64,16 @@ Prend en paramètre une liste de ItemMenu et les met en forme
       <slot name="foot"></slot>
       <slot name="foot"></slot>
     </template>
     </template>
   </v-navigation-drawer>
   </v-navigation-drawer>
-
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useMenu} from "~/composables/layout/useMenu";
-import {computed} from "@vue/reactivity";
+import { useMenu } from '~/composables/layout/useMenu'
+import { computed } from '@vue/reactivity'
 import { useDisplay } from 'vuetify'
 import { useDisplay } from 'vuetify'
-import type { MenuGroup, MenuItem } from "~/types/layout";
+import type { MenuGroup, MenuItem } from '~/types/layout'
 
 
-const { getMenu, hasMenu, isInternalLink, setMenuState, isMenuOpened } = useMenu()
+const { getMenu, hasMenu, isInternalLink, setMenuState, isMenuOpened } =
+  useMenu()
 
 
 const { mdAndUp, lgAndUp } = useDisplay()
 const { mdAndUp, lgAndUp } = useDisplay()
 
 
@@ -103,11 +98,16 @@ const displayMenu = computed(() => {
 // En vue md+, fermer le menu le passe simplement en mode rail
 // En vue md+, fermer le menu le passe simplement en mode rail
 // Sinon, le fermer le masque complètement
 // Sinon, le fermer le masque complètement
 const isRail = computed(() => {
 const isRail = computed(() => {
-  return menu !== null && mdAndUp.value && !isOpened.value && !items.some((item) => item.expanded)
+  return (
+    menu !== null &&
+    mdAndUp.value &&
+    !isOpened.value &&
+    !items.some((item) => item.expanded)
+  )
 })
 })
 
 
 const unwatch = watch(lgAndUp, (newValue, oldValue) => {
 const unwatch = watch(lgAndUp, (newValue, oldValue) => {
-// Par défaut si l'écran est trop petit au chargement de la page, le menu doit rester fermé.
+  // Par défaut si l'écran est trop petit au chargement de la page, le menu doit rester fermé.
   if (process.client && menu !== null) {
   if (process.client && menu !== null) {
     setMenuState('Main', lgAndUp.value)
     setMenuState('Main', lgAndUp.value)
   }
   }
@@ -119,68 +119,67 @@ onUnmounted(() => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
+.v-list-item {
+  min-height: 10px !important;
+}
 
 
-  .v-list-item {
-    min-height: 10px !important;
-  }
-
-  :deep(.v-list-item-title), :deep(.v-icon)
-  {
-    font-size: 14px;
-    color: rgb(var(--v-theme-on-secondary));
-  }
-
-  .v-list-item__prepend {
-    margin: 10px 0;
-    margin-right: 10px !important;
-  }
+:deep(.v-list-item-title),
+:deep(.v-icon) {
+  font-size: 14px;
+  color: rgb(var(--v-theme-on-secondary));
+}
 
 
-  .v-application--is-ltr .v-list-group--no-action > .v-list-group__header {
-    margin-left: 0;
-    padding-left: 0;
-  }
-  .v-application--is-ltr .v-list-group--no-action > .v-list-group__items > .v-list-item {
-    padding-left: 30px;
-  }
+.v-list-item__prepend {
+  margin: 10px 0;
+  margin-right: 10px !important;
+}
 
 
-  .v-list-item__content {
-    padding: 8px 0;
-  }
+.v-application--is-ltr .v-list-group--no-action > .v-list-group__header {
+  margin-left: 0;
+  padding-left: 0;
+}
+.v-application--is-ltr
+  .v-list-group--no-action
+  > .v-list-group__items
+  > .v-list-item {
+  padding-left: 30px;
+}
 
 
-  .v-list-group__items .v-list-item {
-    padding-inline-start: 30px !important;
-  }
+.v-list-item__content {
+  padding: 8px 0;
+}
 
 
-  .v-list-group--no-action > .v-list-group__header,
-  .v-list-item
-  {
-    border-left: 3px solid rgb(var(--v-theme-secondary));
-    height: 48px;
-  }
+.v-list-group__items .v-list-item {
+  padding-inline-start: 30px !important;
+}
 
 
-  .v-list-item:hover,
-  .v-list-item.active,
-  :deep(.v-list-group__items .v-list-item)
-  {
-    border-left: 3px solid rgb(var(--v-theme-primary));
-    background-color: rgb(var(--v-theme-secondary-alt)) !important;
-    color: rgb(var(--v-theme-on-secondary-alt)) !important;
-  }
+.v-list-group--no-action > .v-list-group__header,
+.v-list-item {
+  border-left: 3px solid rgb(var(--v-theme-secondary));
+  height: 48px;
+}
 
 
-  :deep(.v-list-group__items .v-list-item-title) {
-    color: rgb(var(--v-theme-on-secondary-alt));
-  }
+.v-list-item:hover,
+.v-list-item.active,
+:deep(.v-list-group__items .v-list-item) {
+  border-left: 3px solid rgb(var(--v-theme-primary));
+  background-color: rgb(var(--v-theme-secondary-alt)) !important;
+  color: rgb(var(--v-theme-on-secondary-alt)) !important;
+}
 
 
-  :deep(.v-list-group__items .v-icon) {
-    color: rgb(var(--v-theme-on-secondary-alt));
-  }
+:deep(.v-list-group__items .v-list-item-title) {
+  color: rgb(var(--v-theme-on-secondary-alt));
+}
 
 
-  :deep(.v-list-item .v-icon) {
-    margin-right: 10px;
-  }
+:deep(.v-list-group__items .v-icon) {
+  color: rgb(var(--v-theme-on-secondary-alt));
+}
 
 
-  :deep(.menu-item .fa) {
-    text-align: center;
-  }
+:deep(.v-list-item .v-icon) {
+  margin-right: 10px;
+}
 
 
+:deep(.menu-item .fa) {
+  text-align: center;
+}
 </style>
 </style>

+ 101 - 98
components/Layout/ParametersMenu.vue

@@ -1,5 +1,9 @@
 <template>
 <template>
-  <v-navigation-drawer v-if="displayMenu" v-model="isOpened" mobile-breakpoint="sm">
+  <v-navigation-drawer
+    v-if="displayMenu"
+    v-model="isOpened"
+    mobile-breakpoint="sm"
+  >
     <template v-slot:prepend>
     <template v-slot:prepend>
       <div class="title">
       <div class="title">
         <h3>{{ $t('parameters') }}</h3>
         <h3>{{ $t('parameters') }}</h3>
@@ -8,21 +12,22 @@
 
 
     <v-list active-class="active">
     <v-list active-class="active">
       <v-list-item
       <v-list-item
-          v-for="(item, i) in menu!!.children"
-          :key="i"
-          :title="$t(item.label)"
-          :prepend-icon="item.icon ? item.icon.name : ''"
-          :to="(item as MenuItem).to">
+        v-for="(item, i) in menu!!.children"
+        :key="i"
+        :title="$t(item.label)"
+        :prepend-icon="item.icon ? item.icon.name : ''"
+        :to="(item as MenuItem).to"
+      >
       </v-list-item>
       </v-list-item>
     </v-list>
     </v-list>
 
 
     <template v-slot:append>
     <template v-slot:append>
       <v-btn
       <v-btn
-          :href="homeUrl"
-          prepend-icon="fa fa-right-from-bracket"
-          :flat="true"
-          color="on-neutral-very-soft"
-          class="cancel-btn py-2"
+        :href="homeUrl"
+        prepend-icon="fa fa-right-from-bracket"
+        :flat="true"
+        color="on-neutral-very-soft"
+        class="cancel-btn py-2"
       >
       >
         {{ $t('exit') }}
         {{ $t('exit') }}
       </v-btn>
       </v-btn>
@@ -31,102 +36,100 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  import {useMenu} from "~/composables/layout/useMenu";
-  import {useHomeUrl} from "~/composables/utils/useHomeUrl";
-  import {useDisplay} from "vuetify";
-  import {computed} from "@vue/reactivity";
-  import type {MenuGroup, MenuItem} from "~/types/layout";
+import { useMenu } from '~/composables/layout/useMenu'
+import { useHomeUrl } from '~/composables/utils/useHomeUrl'
+import { useDisplay } from 'vuetify'
+import { computed } from '@vue/reactivity'
+import type { MenuGroup, MenuItem } from '~/types/layout'
 
 
-  const { mdAndUp } = useDisplay()
+const { mdAndUp } = useDisplay()
 
 
-  const { getMenu, hasMenu, isMenuOpened, setMenuState } = useMenu()
+const { getMenu, hasMenu, isMenuOpened, setMenuState } = useMenu()
 
 
-  const menu: MenuGroup | null = getMenu('Parameters')
+const menu: MenuGroup | null = getMenu('Parameters')
 
 
-  const displayMenu = computed(() => {
-    return menu !== null && hasMenu('Parameters')
-  })
+const displayMenu = computed(() => {
+  return menu !== null && hasMenu('Parameters')
+})
 
 
-  const isOpened = computed(() => isMenuOpened('Parameters'))
+const isOpened = computed(() => isMenuOpened('Parameters'))
 
 
-  const unwatch = watch(mdAndUp, () => {
-    // Par défaut si l'écran est trop petit au chargement de la page, le menu doit rester fermé.
-    if (process.client && menu !== null) {
-      setMenuState('Parameters', mdAndUp.value)
-    }
-  })
+const unwatch = watch(mdAndUp, () => {
+  // Par défaut si l'écran est trop petit au chargement de la page, le menu doit rester fermé.
+  if (process.client && menu !== null) {
+    setMenuState('Parameters', mdAndUp.value)
+  }
+})
 
 
-  const { homeUrl } = useHomeUrl()
+const { homeUrl } = useHomeUrl()
 
 
-  onUnmounted(() => {
-    unwatch()
-  })
+onUnmounted(() => {
+  unwatch()
+})
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .title {
-    display: flex;
-    align-items: center;
-    height: 48px;
-    vertical-align: center;
-    margin-top: 18px;
-    padding: 4px 16px;
-    font-size: 18px;
-    color: rgb(var(--v-theme-on-neutral-very-soft));
-  }
-
-  .v-navigation-drawer {
-    background-color: rgb(var(--v-theme-neutral-very-soft));
-    border-right: solid 1px rgb(var(--v-theme-neutral-strong));
-  }
-
-  :deep(.v-list-item-title), :deep(.v-icon)
-  {
-    font-size: 14px;
-    color: rgb(var(--v-theme-on-neutral-very-soft));
-  }
-
-  .v-list-item:hover,
-  .v-list-item.active,
-  :deep(.v-list-group__items .v-list-item)
-  {
-    background-color: rgb(var(--v-theme-neutral)) !important;
-    color: rgb(var(--v-theme-on-secondary-alt)) !important;
-  }
-
-  :deep(.v-list-item.active .v-list-item-title) {
-    font-weight: 800 !important;
-  }
-
-  :deep(.v-list-item-title), :deep(.v-icon)
-  {
-    font-size: 14px;
-    color: rgb(var(--v-theme-on-neutral-very-soft));
-  }
-
-  :deep(.v-list-item__prepend) {
-    margin: 10px 0;
-    margin-right: 10px !important;
-  }
-
-  :deep(.v-list-item .v-icon) {
-    max-width: 24px;
-    margin-right: 10px;
-  }
-
-  .cancel-btn {
-    height: 42px;
-    color: rgb(var(--v-theme-on-neutral-very-soft));
-    background-color: transparent;
-    width: 100%;
-    border-top: solid 1px rgb(var(--v-theme-on-neutral-very-soft));
-    display: flex;
-    flex-direction: row;
-    justify-content: flex-start;
-  }
-
-  :deep(.cancel-btn .v-btn__prepend) {
-    margin: 0 16px 4px 2px;
-  }
-
+.title {
+  display: flex;
+  align-items: center;
+  height: 48px;
+  vertical-align: center;
+  margin-top: 18px;
+  padding: 4px 16px;
+  font-size: 18px;
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+}
+
+.v-navigation-drawer {
+  background-color: rgb(var(--v-theme-neutral-very-soft));
+  border-right: solid 1px rgb(var(--v-theme-neutral-strong));
+}
+
+:deep(.v-list-item-title),
+:deep(.v-icon) {
+  font-size: 14px;
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+}
+
+.v-list-item:hover,
+.v-list-item.active,
+:deep(.v-list-group__items .v-list-item) {
+  background-color: rgb(var(--v-theme-neutral)) !important;
+  color: rgb(var(--v-theme-on-secondary-alt)) !important;
+}
+
+:deep(.v-list-item.active .v-list-item-title) {
+  font-weight: 800 !important;
+}
+
+:deep(.v-list-item-title),
+:deep(.v-icon) {
+  font-size: 14px;
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+}
+
+:deep(.v-list-item__prepend) {
+  margin: 10px 0;
+  margin-right: 10px !important;
+}
+
+:deep(.v-list-item .v-icon) {
+  max-width: 24px;
+  margin-right: 10px;
+}
+
+.cancel-btn {
+  height: 42px;
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+  background-color: transparent;
+  width: 100%;
+  border-top: solid 1px rgb(var(--v-theme-on-neutral-very-soft));
+  display: flex;
+  flex-direction: row;
+  justify-content: flex-start;
+}
+
+:deep(.cancel-btn .v-btn__prepend) {
+  margin: 0 16px 4px 2px;
+}
 </style>
 </style>

+ 45 - 35
components/Layout/SubHeader/ActivityYear.vue

@@ -1,8 +1,7 @@
 <template>
 <template>
   <main class="d-flex flex-row align-center">
   <main class="d-flex flex-row align-center">
-    <span v-show="mdAndUp"
-          class="mr-2 font-weight-bold on-neutral">
-        {{ $t(label) }} :
+    <span v-show="mdAndUp" class="mr-2 font-weight-bold on-neutral">
+      {{ $t(label) }} :
     </span>
     </span>
 
 
     <UiXeditableText
     <UiXeditableText
@@ -11,14 +10,17 @@
       :data="currentActivityYear"
       :data="currentActivityYear"
       @update="setActivityYear"
       @update="setActivityYear"
     >
     >
-      <template #xeditable.read="{inputValue}">
+      <template #xeditable.read="{ inputValue }">
         <div class="d-flex align-center on-neutral--clickable">
         <div class="d-flex align-center on-neutral--clickable">
-          <v-icon aria-hidden="false" size="small" class="mr-1" icon="fas fa-edit" />
-          <strong >
+          <v-icon
+            aria-hidden="false"
+            size="small"
+            class="mr-1"
+            icon="fas fa-edit"
+          />
+          <strong>
             {{ inputValue }}
             {{ inputValue }}
-            <span v-if="yearPlusOne">
-              / {{ parseInt(inputValue) + 1 }}
-            </span>
+            <span v-if="yearPlusOne"> / {{ parseInt(inputValue) + 1 }} </span>
           </strong>
           </strong>
         </div>
         </div>
       </template>
       </template>
@@ -27,14 +29,14 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import {useFormStore} from "~/stores/form";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import Access from "~/models/Access/Access";
-import {useDisplay} from "vuetify";
-import {usePageStore} from "~/stores/page";
-import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import { useFormStore } from '~/stores/form'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import Access from '~/models/Access/Access'
+import { useDisplay } from 'vuetify'
+import { usePageStore } from '~/stores/page'
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
 
 const { em } = useEntityManager()
 const { em } = useEntityManager()
 const accessProfileStore = useAccessProfileStore()
 const accessProfileStore = useAccessProfileStore()
@@ -44,9 +46,15 @@ const pageStore = usePageStore()
 const { mdAndUp } = useDisplay()
 const { mdAndUp } = useDisplay()
 const { refreshProfile } = useRefreshProfile()
 const { refreshProfile } = useRefreshProfile()
 
 
-const currentActivityYear: ComputedRef<number | undefined> = computed(() => accessProfileStore.activityYear ?? undefined)
+const currentActivityYear: ComputedRef<number | undefined> = computed(
+  () => accessProfileStore.activityYear ?? undefined,
+)
 const yearPlusOne: boolean = !organizationProfileStore.isManagerProduct
 const yearPlusOne: boolean = !organizationProfileStore.isManagerProduct
-const label: string = organizationProfileStore.isSchool ? 'schooling_year' : organizationProfileStore.isArtist ? 'season_year' : 'cotisation_year'
+const label: string = organizationProfileStore.isSchool
+  ? 'schooling_year'
+  : organizationProfileStore.isArtist
+    ? 'season_year'
+    : 'cotisation_year'
 
 
 /**
 /**
  * Persist a new activityYear
  * Persist a new activityYear
@@ -61,9 +69,11 @@ const setActivityYear = async (event: string) => {
   formStore.setDirty(false)
   formStore.setDirty(false)
 
 
   pageStore.loading = true
   pageStore.loading = true
-  await em.patch(Access, accessProfileStore.currentAccessId, { activityYear: activityYear })
+  await em.patch(Access, accessProfileStore.currentAccessId, {
+    activityYear: activityYear,
+  })
   if (process.server) {
   if (process.server) {
-      // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
+    // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
     await refreshProfile()
     await refreshProfile()
   }
   }
 
 
@@ -72,21 +82,21 @@ const setActivityYear = async (event: string) => {
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
-  .activity-year-input {
-    width: 120px;
-    max-height: 20px;
+.activity-year-input {
+  width: 120px;
+  max-height: 20px;
 
 
-    .v-input {
-      min-width: 70px;
-    }
+  .v-input {
+    min-width: 70px;
+  }
 
 
-    input{
-      font-size: 14px;
-      width: 55px !important;
-      padding: 0 !important;
-      margin-top: 0 !important;
-      min-height: 24px;
-      height: 24px;
-    }
+  input {
+    font-size: 14px;
+    width: 55px !important;
+    padding: 0 !important;
+    margin-top: 0 !important;
+    min-height: 24px;
+    height: 24px;
   }
   }
+}
 </style>
 </style>

+ 12 - 12
components/Layout/SubHeader/Breadcrumbs.vue

@@ -1,26 +1,24 @@
 <template>
 <template>
-  <v-breadcrumbs
-    :items="items"
-  />
+  <v-breadcrumbs :items="items" />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {computed} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
-import type {AnyJson} from "~/types/data";
-import {useI18n} from "vue-i18n";
-import UrlUtils from "~/services/utils/urlUtils";
+import { computed } from '@vue/reactivity'
+import type { ComputedRef } from '@vue/reactivity'
+import type { AnyJson } from '~/types/data'
+import { useI18n } from 'vue-i18n'
+import UrlUtils from '~/services/utils/urlUtils'
 
 
 const runtimeConfig = useRuntimeConfig()
 const runtimeConfig = useRuntimeConfig()
 const i18n = useI18n()
 const i18n = useI18n()
 const router = useRouter()
 const router = useRouter()
 
 
 const items: ComputedRef<Array<AnyJson>> = computed(() => {
 const items: ComputedRef<Array<AnyJson>> = computed(() => {
-  const crumbs:Array<AnyJson> = []
+  const crumbs: Array<AnyJson> = []
 
 
   crumbs.push({
   crumbs.push({
     title: i18n.t('welcome'),
     title: i18n.t('welcome'),
-    href: UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#', 'dashboard')
+    href: UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#', 'dashboard'),
   })
   })
 
 
   const pathPart: Array<string> = UrlUtils.split(router.currentRoute.value.path)
   const pathPart: Array<string> = UrlUtils.split(router.currentRoute.value.path)
@@ -35,9 +33,11 @@ const items: ComputedRef<Array<AnyJson>> = computed(() => {
     match = router.resolve(path)
     match = router.resolve(path)
     if (match.name) {
     if (match.name) {
       crumbs.push({
       crumbs.push({
-        title: !parseInt(part, 10) ? i18n.t(part + '_breadcrumbs') : i18n.t('item'),
+        title: !parseInt(part, 10)
+          ? i18n.t(part + '_breadcrumbs')
+          : i18n.t('item'),
         exact: true,
         exact: true,
-        to: path
+        to: path,
       })
       })
     }
     }
   })
   })

+ 44 - 30
components/Layout/SubHeader/DataTiming.vue

@@ -1,6 +1,8 @@
 <template>
 <template>
   <main class="d-flex align-baseline">
   <main class="d-flex align-baseline">
-    <span v-show="mdAndUp" class="mr-2 font-weight-bold on-neutral">{{ $t('display_data') }} : </span>
+    <span v-show="mdAndUp" class="mr-2 font-weight-bold on-neutral"
+      >{{ $t('display_data') }} :
+    </span>
 
 
     <v-btn-toggle
     <v-btn-toggle
       ref="toggle"
       ref="toggle"
@@ -15,10 +17,15 @@
       @update:modelValue="onUpdate"
       @update:modelValue="onUpdate"
     >
     >
       <v-btn
       <v-btn
-          v-for="choice in historicalChoices"
-          :value="choice"
-          max-height="25"
-          :class="'font-weight-normal text-caption ' + (historicalValue.includes(choice) ? 'theme-primary' : 'theme-neutral-soft')"
+        v-for="choice in historicalChoices"
+        :value="choice"
+        max-height="25"
+        :class="
+          'font-weight-normal text-caption ' +
+          (historicalValue.includes(choice)
+            ? 'theme-primary'
+            : 'theme-neutral-soft')
+        "
       >
       >
         <!-- TODO: on ne devrait pas avoir besoin du if et de la classe 'btn-selected' dans v-btn, mais à l'heure
         <!-- TODO: on ne devrait pas avoir besoin du if et de la classe 'btn-selected' dans v-btn, mais à l'heure
          qu'il est, le component ne fonctionne pas comme attendu. A revoir quand vuetify 3 sera plus stable -->
          qu'il est, le component ne fonctionne pas comme attendu. A revoir quand vuetify 3 sera plus stable -->
@@ -29,14 +36,14 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useFormStore} from "~/stores/form";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import type {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";
-import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
+import { useFormStore } from '~/stores/form'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import type { 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'
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
 
 // TODO: en v3.0.5, pas de solution documentée pour renseigner directement la couleur dans le template, à revoir
 // 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']
 const color = useTheme().current.value.colors['primary']
@@ -50,9 +57,15 @@ const { refreshProfile } = useRefreshProfile()
 
 
 const toggle = ref(null)
 const toggle = ref(null)
 
 
-const historicalChoices: Array<'past' | 'present' | 'future'> = ['past', 'present', 'future']
+const historicalChoices: Array<'past' | 'present' | 'future'> = [
+  'past',
+  'present',
+  'future',
+]
 
 
-const historicalValue: Ref<Array<string>> = ref(historicalChoices.filter((item) => accessProfileStore.historical[item]))
+const historicalValue: Ref<Array<string>> = ref(
+  historicalChoices.filter((item) => accessProfileStore.historical[item]),
+)
 
 
 const onUpdate = async (newValue: Array<string>) => {
 const onUpdate = async (newValue: Array<string>) => {
   historicalValue.value = newValue
   historicalValue.value = newValue
@@ -60,35 +73,36 @@ const onUpdate = async (newValue: Array<string>) => {
   const accessId = accessProfileStore.currentAccessId
   const accessId = accessProfileStore.currentAccessId
 
 
   accessProfileStore.setHistorical(
   accessProfileStore.setHistorical(
-      historicalValue.value.includes('past'),
-      historicalValue.value.includes('present'),
-      historicalValue.value.includes('future')
+    historicalValue.value.includes('past'),
+    historicalValue.value.includes('present'),
+    historicalValue.value.includes('future'),
   )
   )
 
 
   setDirty(false)
   setDirty(false)
   pageStore.loading = true
   pageStore.loading = true
 
 
-  await em.patch(Access, accessId, {'historical': accessProfileStore.historical})
+  await em.patch(Access, accessId, {
+    historical: accessProfileStore.historical,
+  })
   if (process.server) {
   if (process.server) {
-      // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
+    // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
     await refreshProfile()
     await refreshProfile()
   }
   }
 
 
   window.location.reload()
   window.location.reload()
 }
 }
-
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .v-btn-group {
-    max-height: 22px;
-  }
+.v-btn-group {
+  max-height: 22px;
+}
 
 
-  .v-btn {
-    padding: 0 8px;
-  }
+.v-btn {
+  padding: 0 8px;
+}
 
 
-  .v-btn.btn-selected {
-    background-color: rgb(var(--v-theme-primary)) !important;
-  }
+.v-btn.btn-selected {
+  background-color: rgb(var(--v-theme-primary)) !important;
+}
 </style>
 </style>

+ 25 - 18
components/Layout/SubHeader/DataTimingRange.vue

@@ -6,23 +6,23 @@
       </span>
       </span>
 
 
       <UiDateRangePicker
       <UiDateRangePicker
-          :model-value="datesRange"
-          :max-height="28"
-          @update:model-value="updateDateTimeRange"
+        :model-value="datesRange"
+        :max-height="28"
+        @update:model-value="updateDateTimeRange"
       />
       />
     </div>
     </div>
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import type {Ref} from "@vue/reactivity";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import {useFormStore} from "~/stores/form";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import Access from "~/models/Access/Access";
-import DateUtils from "~/services/utils/dateUtils";
-import {usePageStore} from "~/stores/page";
-import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
+import type { Ref } from '@vue/reactivity'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { useFormStore } from '~/stores/form'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import Access from '~/models/Access/Access'
+import DateUtils from '~/services/utils/dateUtils'
+import { usePageStore } from '~/stores/page'
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
 
 const { setDirty } = useFormStore()
 const { setDirty } = useFormStore()
 const accessProfileStore = useAccessProfileStore()
 const accessProfileStore = useAccessProfileStore()
@@ -33,18 +33,23 @@ const { refreshProfile } = useRefreshProfile()
 const start = accessProfileStore.historical.dateStart
 const start = accessProfileStore.historical.dateStart
 const end = accessProfileStore.historical.dateEnd
 const end = accessProfileStore.historical.dateEnd
 
 
-const datesRange: Ref<Array<Date> | null> = ref((start && end) ? [new Date(start), new Date(end)] : null)
+const datesRange: Ref<Array<Date> | null> = ref(
+  start && end ? [new Date(start), new Date(end)] : null,
+)
 
 
 const updateDateTimeRange = async (dates: Array<Date>): Promise<any> => {
 const updateDateTimeRange = async (dates: Array<Date>): Promise<any> => {
-
   const accessId = accessProfileStore.currentAccessId
   const accessId = accessProfileStore.currentAccessId
 
 
   datesRange.value = dates
   datesRange.value = dates
 
 
-  if (datesRange.value !== null && datesRange.value[0] !== null && datesRange.value[1] !== null) {
+  if (
+    datesRange.value !== null &&
+    datesRange.value[0] !== null &&
+    datesRange.value[1] !== null
+  ) {
     accessProfileStore.setHistoricalRange(
     accessProfileStore.setHistoricalRange(
-        DateUtils.formatIsoShortDate(datesRange.value[0]),
-        DateUtils.formatIsoShortDate(datesRange.value[1])
+      DateUtils.formatIsoShortDate(datesRange.value[0]),
+      DateUtils.formatIsoShortDate(datesRange.value[1]),
     )
     )
   } else {
   } else {
     accessProfileStore.setHistorical(false, true, false)
     accessProfileStore.setHistorical(false, true, false)
@@ -52,9 +57,11 @@ const updateDateTimeRange = async (dates: Array<Date>): Promise<any> => {
   setDirty(false)
   setDirty(false)
   pageStore.loading = true
   pageStore.loading = true
 
 
-  await em.patch(Access, accessId, {'historical': accessProfileStore.historical})
+  await em.patch(Access, accessId, {
+    historical: accessProfileStore.historical,
+  })
   if (process.server) {
   if (process.server) {
-      // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
+    // Force profile refresh server side to avoid a bug where server and client stores diverge on profile refresh
     await refreshProfile()
     await refreshProfile()
   }
   }
 
 

+ 45 - 39
components/Layout/SubHeader/PersonnalizedList.vue

@@ -5,12 +5,17 @@
     </a>
     </a>
 
 
     <v-menu
     <v-menu
-        :activator="btn"
-        offset="10"
-        min-width="440"
-        :close-on-content-click="false"
+      :activator="btn"
+      offset="10"
+      min-width="440"
+      :close-on-content-click="false"
     >
     >
-      <v-card v-if="collection.totalItems === 0" height="80" width="440" class="pa-4">
+      <v-card
+        v-if="collection.totalItems === 0"
+        height="80"
+        width="440"
+        class="pa-4"
+      >
         <v-card-text class="ma-0 pa-0 header_menu">
         <v-card-text class="ma-0 pa-0 header_menu">
           {{ $t('nothing_to_show') }}
           {{ $t('nothing_to_show') }}
         </v-card-text>
         </v-card-text>
@@ -19,11 +24,11 @@
       <v-card v-else width="440">
       <v-card v-else width="440">
         <v-card-title class="text-body-2 header-personalized">
         <v-card-title class="text-body-2 header-personalized">
           <v-text-field
           <v-text-field
-              v-model="search"
-              :label="$t('searchList')"
-              :loading="pending"
-              density="compact"
-              clear-icon="header-personalized"
+            v-model="search"
+            :label="$t('searchList')"
+            :loading="pending"
+            density="compact"
+            clear-icon="header-personalized"
           />
           />
         </v-card-title>
         </v-card-title>
 
 
@@ -36,24 +41,23 @@
               :href="getListURL(item)"
               :href="getListURL(item)"
               exact
               exact
             >
             >
-              <strong>{{item.menuKey}}</strong> - {{item.label}}
+              <strong>{{ item.menuKey }}</strong> - {{ item.label }}
             </v-list-item>
             </v-list-item>
           </v-list>
           </v-list>
         </v-card-text>
         </v-card-text>
       </v-card>
       </v-card>
-
     </v-menu>
     </v-menu>
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
 import PersonalizedList from '~/models/Access/PersonalizedList'
 import PersonalizedList from '~/models/Access/PersonalizedList'
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {ref} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import type {AnyJson} from "~/types/data";
-import ApiResource from "~/models/ApiResource";
-import UrlUtils from "~/services/utils/urlUtils";
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { ref } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import type { AnyJson } from '~/types/data'
+import ApiResource from '~/models/ApiResource'
+import UrlUtils from '~/services/utils/urlUtils'
 
 
 const btn: Ref = ref(null)
 const btn: Ref = ref(null)
 
 
@@ -64,24 +68,26 @@ const { data: collection, pending } = await fetchCollection(PersonalizedList)
 const i18n = useI18n()
 const i18n = useI18n()
 
 
 const items: ComputedRef<Array<AnyJson>> = computed(() => {
 const items: ComputedRef<Array<AnyJson>> = computed(() => {
-  const lists: Array<ApiResource> = collection.value !== null ? collection.value.items : []
+  const lists: Array<ApiResource> =
+    collection.value !== null ? collection.value.items : []
 
 
-  lists.map(item => {
+  lists.map((item) => {
     item.menuKey = i18n.t(item.menuKey) as string
     item.menuKey = i18n.t(item.menuKey) as string
   })
   })
 
 
   return lists
   return lists
 })
 })
 
 
-const search = ref('');
+const search = ref('')
 
 
 const filteredItems = computed(() => {
 const filteredItems = computed(() => {
-  return items.value.filter( item => {
-      return !search.value ||
-             item.label.toLowerCase().indexOf(search.value.toLowerCase()) >= 0 ||
-             item.menuKey.toLowerCase().indexOf(search.value.toLowerCase()) >= 0
-    }
-  )
+  return items.value.filter((item) => {
+    return (
+      !search.value ||
+      item.label.toLowerCase().indexOf(search.value.toLowerCase()) >= 0 ||
+      item.menuKey.toLowerCase().indexOf(search.value.toLowerCase()) >= 0
+    )
+  })
 })
 })
 
 
 const runtimeConfig = useRuntimeConfig()
 const runtimeConfig = useRuntimeConfig()
@@ -93,16 +99,16 @@ const getListURL = (list: PersonalizedList) => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  #activator {
-    cursor: pointer;
-  }
-
-  #activator:hover {
-    color: rgb(var(--var-theme-on-neutral)) !important;
-  }
-
-  .header-personalized {
-    margin-bottom: 0;
-    padding-bottom: 0;
-  }
+#activator {
+  cursor: pointer;
+}
+
+#activator:hover {
+  color: rgb(var(--var-theme-on-neutral)) !important;
+}
+
+.header-personalized {
+  margin-bottom: 0;
+  padding-bottom: 0;
+}
 </style>
 </style>

+ 55 - 42
components/Layout/Subheader.vue

@@ -15,40 +15,52 @@ Contient entre autres le breadcrumb, les commandes de changement d'année et les
 
 
       <span class="flex-fill" />
       <span class="flex-fill" />
 
 
-      <v-card
-        class="d-flex flex-row align-center mr-6"
-        :flat="true"
-        tile
-      >
-        <LayoutSubHeaderActivityYear v-if="smAndUp && !showDateTimeRange" class="activity-year" />
-
-        <div v-if="hasMenuOrIsTeacher" class="d-flex flex-row align-center h-100">
+      <v-card class="d-flex flex-row align-center mr-6" :flat="true" tile>
+        <LayoutSubHeaderActivityYear
+          v-if="smAndUp && !showDateTimeRange"
+          class="activity-year"
+        />
+
+        <div
+          v-if="hasMenuOrIsTeacher"
+          class="d-flex flex-row align-center h-100"
+        >
           <LayoutSubHeaderDataTiming
           <LayoutSubHeaderDataTiming
-              v-if="smAndUp && !showDateTimeRange"
-              class="data-timing ml-2"
+            v-if="smAndUp && !showDateTimeRange"
+            class="data-timing ml-2"
           />
           />
 
 
           <LayoutSubHeaderDataTimingRange
           <LayoutSubHeaderDataTimingRange
-              v-if="smAndUp && showDateTimeRange"
-              class="data-timing-range ml-n1"
+            v-if="smAndUp && showDateTimeRange"
+            class="data-timing-range ml-n1"
           />
           />
 
 
           <v-btn
           <v-btn
-              v-if="smAndUp"
-              ref="btn"
-              class="switch-btn ml-1 theme-neutral-soft"
-              height="22" min-height="22" max-height="22"
-              width="25" min-width="25" max-width="25"
-              elevation="0"
-              @click="showDateTimeRange = !showDateTimeRange"
+            v-if="smAndUp"
+            ref="btn"
+            class="switch-btn ml-1 theme-neutral-soft"
+            height="22"
+            min-height="22"
+            max-height="22"
+            width="25"
+            min-width="25"
+            max-width="25"
+            elevation="0"
+            @click="showDateTimeRange = !showDateTimeRange"
           >
           >
-              <v-icon icon="fas fa-history" class="font-weight-normal" style="font-size: 14px;" />
+            <v-icon
+              icon="fas fa-history"
+              class="font-weight-normal"
+              style="font-size: 14px"
+            />
           </v-btn>
           </v-btn>
           <v-tooltip location="bottom" :activator="btn">
           <v-tooltip location="bottom" :activator="btn">
-              <span>{{ $t('history_help') }}</span>
+            <span>{{ $t('history_help') }}</span>
           </v-tooltip>
           </v-tooltip>
 
 
-          <LayoutSubHeaderPersonnalizedList class="personalized-list ml-2 d-flex align-center" />
+          <LayoutSubHeaderPersonnalizedList
+            class="personalized-list ml-2 d-flex align-center"
+          />
         </div>
         </div>
       </v-card>
       </v-card>
     </v-card>
     </v-card>
@@ -56,29 +68,30 @@ Contient entre autres le breadcrumb, les commandes de changement d'année et les
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-    import {useAccessProfileStore} from "~/stores/accessProfile";
-    import {computed, ref} from "@vue/reactivity";
-    import type {ComputedRef, Ref} from "@vue/reactivity";
-    import {useMenu} from "~/composables/layout/useMenu";
-    import {useDisplay} from "vuetify";
-
-    const { smAndUp, lgAndUp } = useDisplay()
-    const accessProfile = useAccessProfileStore()
-    const { hasMenu } = useMenu()
-    const btn: Ref = ref(null)
-
-    const hasMenuOrIsTeacher: ComputedRef<boolean> = computed(
-        () => hasMenu('Main') || (accessProfile.isTeacher ?? false)
-    )
-
-    const showDateTimeRange: Ref<boolean> = ref(
-        Object.hasOwn(accessProfile.historical, 'dateStart') && accessProfile.historical.dateStart !== null &&
-        Object.hasOwn(accessProfile.historical, 'dateEnd') && accessProfile.historical.dateEnd !== null
-    )
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { computed, ref } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import { useMenu } from '~/composables/layout/useMenu'
+import { useDisplay } from 'vuetify'
+
+const { smAndUp, lgAndUp } = useDisplay()
+const accessProfile = useAccessProfileStore()
+const { hasMenu } = useMenu()
+const btn: Ref = ref(null)
+
+const hasMenuOrIsTeacher: ComputedRef<boolean> = computed(
+  () => hasMenu('Main') || (accessProfile.isTeacher ?? false),
+)
+
+const showDateTimeRange: Ref<boolean> = ref(
+  Object.hasOwn(accessProfile.historical, 'dateStart') &&
+    accessProfile.historical.dateStart !== null &&
+    Object.hasOwn(accessProfile.historical, 'dateEnd') &&
+    accessProfile.historical.dateEnd !== null,
+)
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-
 main {
 main {
   font-size: 12px;
   font-size: 12px;
 }
 }

+ 14 - 14
components/Layout/ThemeSwitcher.vue

@@ -1,26 +1,26 @@
 <template>
 <template>
   <v-switch
   <v-switch
-      v-model="theme.global.name.value"
-      density="compact"
-      :inline="true"
-      false-value="light"
-      false-icon="fas fa-sun"
-      true-value="dark"
-      true-icon="fas fa-moon"
+    v-model="theme.global.name.value"
+    density="compact"
+    :inline="true"
+    false-value="light"
+    false-icon="fas fa-sun"
+    true-value="dark"
+    true-icon="fas fa-moon"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useTheme} from "vuetify";
+import { useTheme } from 'vuetify'
 
 
 const theme = useTheme()
 const theme = useTheme()
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .v-switch {
-    min-width: 60px;
-    max-width: 60px;
-    min-height: 40px;
-    max-height: 40px;
-  }
+.v-switch {
+  min-width: 60px;
+  max-width: 60px;
+  min-height: 40px;
+  max-height: 40px;
+}
 </style>
 </style>

+ 27 - 27
components/Ui/Button/Delete.vue

@@ -8,9 +8,7 @@ Bouton Delete avec modale de confirmation de la suppression
       <v-icon>fas fa-trash</v-icon>
       <v-icon>fas fa-trash</v-icon>
     </v-btn>
     </v-btn>
 
 
-    <LazyLayoutDialog
-      :show="showDialog"
-    >
+    <LazyLayoutDialog :show="showDialog">
       <template #dialogType>{{ $t('delete_assistant') }}</template>
       <template #dialogType>{{ $t('delete_assistant') }}</template>
       <template #dialogTitle>{{ $t('caution') }}</template>
       <template #dialogTitle>{{ $t('caution') }}</template>
       <template #dialogText>
       <template #dialogText>
@@ -31,27 +29,27 @@ Bouton Delete avec modale de confirmation de la suppression
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {TYPE_ALERT} from '~/types/enum/enums'
-import type {Ref} from "@vue/reactivity";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import ApiResource from "~/models/ApiResource";
-import {usePageStore} from "~/stores/page";
-import ApiModel from "~/models/ApiModel";
+import { TYPE_ALERT } from '~/types/enum/enums'
+import type { Ref } from '@vue/reactivity'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import ApiResource from '~/models/ApiResource'
+import { usePageStore } from '~/stores/page'
+import ApiModel from '~/models/ApiModel'
 
 
 const props = defineProps({
 const props = defineProps({
-    model: {
-      type: Function as any as () => typeof ApiModel,
-      required: true
-    },
-    entity: {
-      type: Object as () => ApiResource,
-      required: true
-    },
-    flat: {
-      type: Boolean,
-      required: false,
-      default: false
-    }
+  model: {
+    type: Function as any as () => typeof ApiModel,
+    required: true,
+  },
+  entity: {
+    type: Object as () => ApiResource,
+    required: true,
+  },
+  flat: {
+    type: Boolean,
+    required: false,
+    default: false,
+  },
 })
 })
 
 
 const showDialog: Ref<boolean> = ref(false)
 const showDialog: Ref<boolean> = ref(false)
@@ -70,10 +68,12 @@ const deleteItem = async () => {
   showDialog.value = false
   showDialog.value = false
 }
 }
 
 
-const alertDeleteItem = () => { showDialog.value = true }
-const closeDialog = () => { showDialog.value = false }
-
+const alertDeleteItem = () => {
+  showDialog.value = true
+}
+const closeDialog = () => {
+  showDialog.value = false
+}
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 24 - 23
components/Ui/Button/Submit.vue

@@ -1,12 +1,11 @@
 <template>
 <template>
   <v-btn
   <v-btn
-      class="mr-4 theme-primary"
-      :class="hasOtherActions ? 'pr-0' : ''"
-      @click="submitAction(mainAction)"
-      ref="mainBtn"
-      :disabled="validationPending"
+    class="mr-4 theme-primary"
+    :class="hasOtherActions ? 'pr-0' : ''"
+    @click="submitAction(mainAction)"
+    ref="mainBtn"
+    :disabled="validationPending"
   >
   >
-
     {{ $t(mainAction) }}
     {{ $t(mainAction) }}
 
 
     <v-divider class="ml-3" :vertical="true" v-if="hasOtherActions"></v-divider>
     <v-divider class="ml-3" :vertical="true" v-if="hasOtherActions"></v-divider>
@@ -22,13 +21,13 @@
       <template #activator="{ on, attrs }">
       <template #activator="{ on, attrs }">
         <v-toolbar-title v-on="on">
         <v-toolbar-title v-on="on">
           <v-icon class="pl-3 pr-3">
           <v-icon class="pl-3 pr-3">
-            {{ dropDirection === 'top' ? 'fa fa-caret-up' : 'fa fa-caret-down'}}
+            {{
+              dropDirection === 'top' ? 'fa fa-caret-up' : 'fa fa-caret-down'
+            }}
           </v-icon>
           </v-icon>
         </v-toolbar-title>
         </v-toolbar-title>
       </template>
       </template>
-      <v-list
-        :min-width="menuSize"
-      >
+      <v-list :min-width="menuSize">
         <v-list-item
         <v-list-item
           dense
           dense
           v-for="(action, index) in actions"
           v-for="(action, index) in actions"
@@ -36,7 +35,10 @@
           class="subAction"
           class="subAction"
           v-if="index > 0"
           v-if="index > 0"
         >
         >
-          <v-list-item-title v-text="$t(action)" @click="submitAction(action)" />
+          <v-list-item-title
+            v-text="$t(action)"
+            @click="submitAction(action)"
+          />
         </v-list-item>
         </v-list-item>
       </v-list>
       </v-list>
     </v-menu>
     </v-menu>
@@ -44,30 +46,30 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {computed, ref} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
+import { computed, ref } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   actions: {
   actions: {
     type: Array,
     type: Array,
-    required: true
+    required: true,
   },
   },
   dropDirection: {
   dropDirection: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default:'bottom'
+    default: 'bottom',
   },
   },
   validationPending: {
   validationPending: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
-  }
+    default: false,
+  },
 })
 })
 
 
 const emit = defineEmits(['submit'])
 const emit = defineEmits(['submit'])
 
 
 const mainBtn: Ref = ref(null)
 const mainBtn: Ref = ref(null)
-const menuSize = computed(()=>{
+const menuSize = computed(() => {
   // Btn size + 40px de padding
   // Btn size + 40px de padding
   return mainBtn.value?.$el.clientWidth + 40
   return mainBtn.value?.$el.clientWidth + 40
 })
 })
@@ -76,21 +78,20 @@ const submitAction = (action: string) => {
   emit('submit', action)
   emit('submit', action)
 }
 }
 
 
-const mainAction: ComputedRef<string> = computed(()=>{
+const mainAction: ComputedRef<string> = computed(() => {
   return props.actions[0] as string
   return props.actions[0] as string
 })
 })
 
 
-const hasOtherActions: ComputedRef<boolean> = computed(()=>{
+const hasOtherActions: ComputedRef<boolean> = computed(() => {
   return props.actions.length > 1
   return props.actions.length > 1
 })
 })
-
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
-.v-list-item--dense{
+.v-list-item--dense {
   min-height: 25px;
   min-height: 25px;
 }
 }
-.subAction{
+.subAction {
   cursor: pointer;
   cursor: pointer;
 }
 }
 </style>
 </style>

+ 8 - 16
components/Ui/Card.vue

@@ -3,12 +3,7 @@ Container de type Card
 -->
 -->
 
 
 <template>
 <template>
-  <v-card
-    elevation="2"
-    outlined
-    shaped
-    min-height="200"
-  >
+  <v-card elevation="2" outlined shaped min-height="200">
     <!-- Titre -->
     <!-- Titre -->
     <v-card-title>
     <v-card-title>
       <slot name="card.title" />
       <slot name="card.title" />
@@ -33,32 +28,29 @@ Container de type Card
 
 
       <slot name="card.action" />
       <slot name="card.action" />
     </v-card-actions>
     </v-card-actions>
-
   </v-card>
   </v-card>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
 const props = defineProps({
 const props = defineProps({
   link: {
   link: {
     type: String,
     type: String,
-    required: true
+    required: true,
   },
   },
   model: {
   model: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
   entity: {
   entity: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
-  withDeleteAction:{
+  withDeleteAction: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: true
-  }
+    default: true,
+  },
 })
 })
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 18 - 16
components/Ui/Collection.vue

@@ -4,15 +4,14 @@
   <main>
   <main>
     <v-skeleton-loader v-if="pending" :type="loaderType" />
     <v-skeleton-loader v-if="pending" :type="loaderType" />
     <div v-else>
     <div v-else>
-
       <!-- Content -->
       <!-- Content -->
-      <slot name="list.item" v-bind="{items}" />
+      <slot name="list.item" v-bind="{ items }" />
 
 
       <!-- New button -->
       <!-- New button -->
       <v-btn v-if="newLink" class="theme-primary float-right">
       <v-btn v-if="newLink" class="theme-primary float-right">
         <NuxtLink :to="newLink" class="no-decoration">
         <NuxtLink :to="newLink" class="no-decoration">
           <v-icon>fa-plus-circle</v-icon>
           <v-icon>fa-plus-circle</v-icon>
-          <span>{{$t('add')}}</span>
+          <span>{{ $t('add') }}</span>
         </NuxtLink>
         </NuxtLink>
       </v-btn>
       </v-btn>
     </div>
     </div>
@@ -21,38 +20,41 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {computed, toRefs} from "@vue/reactivity";
-import type {ComputedRef, ToRefs} from "@vue/reactivity";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import type {Collection} from "~/types/data";
+import { computed, toRefs } from '@vue/reactivity'
+import type { ComputedRef, ToRefs } from '@vue/reactivity'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import type { Collection } from '~/types/data'
 
 
 const props = defineProps({
 const props = defineProps({
   model: {
   model: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
   parent: {
   parent: {
     type: Object,
     type: Object,
-    required: false
+    required: false,
   },
   },
   loaderType: {
   loaderType: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: 'text'
+    default: 'text',
   },
   },
   newLink: {
   newLink: {
     type: String,
     type: String,
-    required: false
-  }
+    required: false,
+  },
 })
 })
 
 
 const { model, parent }: ToRefs = toRefs(props)
 const { model, parent }: ToRefs = toRefs(props)
 
 
 const { fetchCollection } = useEntityFetch()
 const { fetchCollection } = useEntityFetch()
 
 
-const { data: collection, pending } = await fetchCollection(model.value, parent.value)
-
-const items: ComputedRef<Collection> = computed(() => collection.value ?? { items: [], pagination: {}, totalItems: 0 })
+const { data: collection, pending } = await fetchCollection(
+  model.value,
+  parent.value,
+)
 
 
+const items: ComputedRef<Collection> = computed(
+  () => collection.value ?? { items: [], pagination: {}, totalItems: 0 },
+)
 </script>
 </script>

+ 17 - 29
components/Ui/DataTable.vue

@@ -5,10 +5,7 @@ Tableau interactif conçu pour l'affichage d'une collection d'entités
 -->
 -->
 
 
 <template>
 <template>
-  <v-col
-    cols="12"
-    sm="12"
-  >
+  <v-col cols="12" sm="12">
     <v-data-table
     <v-data-table
       :headers="headersWithItem"
       :headers="headersWithItem"
       :items="collection.items"
       :items="collection.items"
@@ -23,51 +20,39 @@ Tableau interactif conçu pour l'affichage d'une collection d'entités
       </template>
       </template>
 
 
       <template #item.actions="{ item }">
       <template #item.actions="{ item }">
-        <v-icon
-          small
-          class="mr-2"
-          @click="editItem(item)"
-        >
-          mdi-pencil
-        </v-icon>
-        <v-icon
-          small
-          @click="deleteItem(item)"
-        >
-          mdi-delete
-        </v-icon>
+        <v-icon small class="mr-2" @click="editItem(item)"> mdi-pencil </v-icon>
+        <v-icon small @click="deleteItem(item)"> mdi-delete </v-icon>
       </template>
       </template>
     </v-data-table>
     </v-data-table>
   </v-col>
   </v-col>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {ref, toRefs} from "@vue/reactivity";
-import type {Ref} from "@vue/reactivity";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import ApiResource from "~/models/ApiResource";
-import type {AnyJson} from "~/types/data";
+import { ref, toRefs } from '@vue/reactivity'
+import type { Ref } from '@vue/reactivity'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import ApiResource from '~/models/ApiResource'
+import type { AnyJson } from '~/types/data'
 
 
 const props = defineProps({
 const props = defineProps({
   parent: {
   parent: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
   model: {
   model: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
   headers: {
   headers: {
     type: Array,
     type: Array,
-    required: true
-  }
+    required: true,
+  },
 })
 })
 
 
 const { parent, model, headers } = toRefs(props)
 const { parent, model, headers } = toRefs(props)
 
 
 const headersWithItem = computed(() => {
 const headersWithItem = computed(() => {
-  return headers.value.map((header:any) => {
+  return headers.value.map((header: any) => {
     header.item = 'item.' + header.value
     header.item = 'item.' + header.value
     return header
     return header
   })
   })
@@ -78,7 +63,10 @@ const entries: Ref<Array<AnyJson>> = ref(Array<AnyJson>())
 
 
 const { fetchCollection } = useEntityFetch()
 const { fetchCollection } = useEntityFetch()
 
 
-const { data: collection, pending } = await fetchCollection(model.value as typeof ApiResource, parent.value as ApiResource)
+const { data: collection, pending } = await fetchCollection(
+  model.value as typeof ApiResource,
+  parent.value as ApiResource,
+)
 
 
 const itemId: Ref<number> = ref(0)
 const itemId: Ref<number> = ref(0)
 
 

+ 25 - 28
components/Ui/DatePicker.vue

@@ -7,26 +7,26 @@ Sélecteur de dates
 <template>
 <template>
   <main>
   <main>
     <VueDatePicker
     <VueDatePicker
-        :model-value="modelValue"
-        :locale="i18n.locale.value"
-        :format-locale="fnsLocale"
-        :format="dateFormat"
-        :enable-time-picker="withTime"
-        :teleport="true"
-        text-input
-        :auto-apply="true"
-        :select-text="$t('select')"
-        :cancel-text="$t('cancel')"
-        :disabled="readonly"
-        :position="position"
-        @update:model-value="onUpdate"
+      :model-value="modelValue"
+      :locale="i18n.locale.value"
+      :format-locale="fnsLocale"
+      :format="dateFormat"
+      :enable-time-picker="withTime"
+      :teleport="true"
+      text-input
+      :auto-apply="true"
+      :select-text="$t('select')"
+      :cancel-text="$t('cancel')"
+      :disabled="readonly"
+      :position="position"
+      @update:model-value="onUpdate"
     />
     />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import DateUtils, {supportedLocales} from "~/services/utils/dateUtils";
-import type {PropType} from "@vue/runtime-core";
+import DateUtils, { supportedLocales } from '~/services/utils/dateUtils'
+import type { PropType } from '@vue/runtime-core'
 
 
 const i18n = useI18n()
 const i18n = useI18n()
 
 
@@ -36,22 +36,22 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: Object as PropType<Date>,
     type: Object as PropType<Date>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   format: {
   format: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   withTime: {
   withTime: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * @see https://vue3datepicker.com/props/positioning/#position
    * @see https://vue3datepicker.com/props/positioning/#position
@@ -59,13 +59,13 @@ const props = defineProps({
   position: {
   position: {
     type: String as PropType<'left' | 'center' | 'right'>,
     type: String as PropType<'left' | 'center' | 'right'>,
     required: false,
     required: false,
-    default: 'center'
-  }
+    default: 'center',
+  },
 })
 })
 
 
-const defaultFormatPattern = props.withTime ?
-    DateUtils.getFormatPattern(i18n.locale.value as supportedLocales) :
-    DateUtils.getShortFormatPattern(i18n.locale.value as supportedLocales)
+const defaultFormatPattern = props.withTime
+  ? DateUtils.getFormatPattern(i18n.locale.value as supportedLocales)
+  : DateUtils.getShortFormatPattern(i18n.locale.value as supportedLocales)
 
 
 const dateFormat: Ref<string> = ref(props.format ?? defaultFormatPattern)
 const dateFormat: Ref<string> = ref(props.format ?? defaultFormatPattern)
 
 
@@ -74,9 +74,6 @@ const emit = defineEmits(['update:model-value'])
 const onUpdate = (event: Date) => {
 const onUpdate = (event: Date) => {
   emit('update:model-value', event)
   emit('update:model-value', event)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 69 - 66
components/Ui/DateRangePicker.vue

@@ -1,43 +1,43 @@
 <template>
 <template>
   <!-- @see https://vue3datepicker.com/props/modes/#multi-calendars -->
   <!-- @see https://vue3datepicker.com/props/modes/#multi-calendars -->
   <VueDatePicker
   <VueDatePicker
-      :model-value="modelValue"
-      range
-      multi-calendars
-      :auto-apply="autoApply"
-      :locale="i18n.locale.value"
-      :format-locale="fnsLocale"
-      :format="dateFormatPattern"
-      :start-date="today"
-      :teleport="true"
-      :alt-position="dateRangePickerAltPosition"
-      :enable-time-picker="false"
-      close-on-scroll
-      text-input
-      :select-text="$t('select')"
-      :cancel-text="$t('cancel')"
-      input-class-name="date-range-picker-input"
-      @update:model-value="updateDateTimeRange"
-      class="date-range-picker"
-      :style="style"
+    :model-value="modelValue"
+    range
+    multi-calendars
+    :auto-apply="autoApply"
+    :locale="i18n.locale.value"
+    :format-locale="fnsLocale"
+    :format="dateFormatPattern"
+    :start-date="today"
+    :teleport="true"
+    :alt-position="dateRangePickerAltPosition"
+    :enable-time-picker="false"
+    close-on-scroll
+    text-input
+    :select-text="$t('select')"
+    :cancel-text="$t('cancel')"
+    input-class-name="date-range-picker-input"
+    @update:model-value="updateDateTimeRange"
+    class="date-range-picker"
+    :style="style"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import DateUtils, {supportedLocales} from "~/services/utils/dateUtils";
-import type {PropType} from "@vue/runtime-core";
+import DateUtils, { supportedLocales } from '~/services/utils/dateUtils'
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   modelValue: {
   modelValue: {
     type: Array as PropType<Array<Date> | null>,
     type: Array as PropType<Array<Date> | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   maxHeight: {
   maxHeight: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const emit = defineEmits(['update:modelValue'])
 const emit = defineEmits(['update:modelValue'])
@@ -51,13 +51,16 @@ const updateDateTimeRange = (value: [string, string]) => {
 const i18n = useI18n()
 const i18n = useI18n()
 
 
 const fnsLocale = DateUtils.getFnsLocale(i18n.locale.value as supportedLocales)
 const fnsLocale = DateUtils.getFnsLocale(i18n.locale.value as supportedLocales)
-const dateFormatPattern = DateUtils.getShortFormatPattern(i18n.locale.value as supportedLocales)
+const dateFormatPattern = DateUtils.getShortFormatPattern(
+  i18n.locale.value as supportedLocales,
+)
 
 
 const today = new Date()
 const today = new Date()
 
 
-let style = '';
+let style = ''
 if (props.maxHeight !== null) {
 if (props.maxHeight !== null) {
-  style += 'height: ' + props.maxHeight + 'px;max-height: ' + props.maxHeight + 'px;'
+  style +=
+    'height: ' + props.maxHeight + 'px;max-height: ' + props.maxHeight + 'px;'
 }
 }
 
 
 /**
 /**
@@ -70,57 +73,57 @@ const dateRangePickerAltPosition = (el: HTMLElement) => {
   const rightPadding = 30
   const rightPadding = 30
   const rect = el.getBoundingClientRect()
   const rect = el.getBoundingClientRect()
 
 
-  if ((rect.left + fullWidth + rightPadding) > window.innerWidth) {
+  if (rect.left + fullWidth + rightPadding > window.innerWidth) {
     xOffset = window.innerWidth - (rect.left + fullWidth + rightPadding)
     xOffset = window.innerWidth - (rect.left + fullWidth + rightPadding)
   }
   }
 
 
   return {
   return {
     top: rect.bottom,
     top: rect.bottom,
-    left: rect.left + xOffset
+    left: rect.left + xOffset,
   }
   }
 }
 }
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
+// @see https://vue3datepicker.com/customization/theming/
+// [!] Sass variables overriding does not work in scoped mode
+.dp__theme_light,
+.dp__theme_dark {
+  --dp-background-color: #ffffff;
+  --dp-text-color: #212121;
+  --dp-hover-color: #f3f3f3;
+  --dp-hover-text-color: #212121;
+  --dp-hover-icon-color: #959595;
+  --dp-primary-color: rgb(var(--v-theme-primary)) !important;
+  --dp-primary-text-color: rgb(var(--v-theme-on-primary)) !important;
+  --dp-secondary-color: rgb(var(--v-theme-neutral-strong)) !important;
+  --dp-border-color: #ddd;
+  --dp-menu-border-color: #ddd;
+  --dp-border-color-hover: #aaaeb7;
+  --dp-disabled-color: #f6f6f6;
+  --dp-scroll-bar-background: #f3f3f3;
+  --dp-scroll-bar-color: #959595;
+  --dp-success-color: rgb(var(--v-theme-success)) !important;
+  --dp-success-color-disabled: rgb(var(--v-theme-neutral-strong)) !important;
+  --dp-icon-color: #959595;
+  --dp-danger-color: #ff6f60;
+  --dp-highlight-color: rgba(25, 118, 210, 0.1);
+}
 
 
-  // @see https://vue3datepicker.com/customization/theming/
-  // [!] Sass variables overriding does not work in scoped mode
-  .dp__theme_light, .dp__theme_dark {
-    --dp-background-color: #ffffff;
-    --dp-text-color: #212121;
-    --dp-hover-color: #f3f3f3;
-    --dp-hover-text-color: #212121;
-    --dp-hover-icon-color: #959595;
-    --dp-primary-color: rgb(var(--v-theme-primary)) !important;
-    --dp-primary-text-color: rgb(var(--v-theme-on-primary)) !important;
-    --dp-secondary-color: rgb(var(--v-theme-neutral-strong)) !important;
-    --dp-border-color: #ddd;
-    --dp-menu-border-color: #ddd;
-    --dp-border-color-hover: #aaaeb7;
-    --dp-disabled-color: #f6f6f6;
-    --dp-scroll-bar-background: #f3f3f3;
-    --dp-scroll-bar-color: #959595;
-    --dp-success-color: rgb(var(--v-theme-success)) !important;
-    --dp-success-color-disabled: rgb(var(--v-theme-neutral-strong)) !important;
-    --dp-icon-color: #959595;
-    --dp-danger-color: #ff6f60;
-    --dp-highlight-color: rgba(25, 118, 210, 0.1);
+.date-range-picker {
+  div {
+    height: 100% !important;
+    max-height: 100% !important;
   }
   }
 
 
-  .date-range-picker {
-    div {
-      height: 100% !important;
-      max-height: 100% !important;
-    }
-
-    .dp__input_wrap {
-      height: 100% !important;
-      max-height: 100% !important;
-    }
-
-    .date-range-picker-input {
-      height: 100% !important;
-      max-height: 100% !important;
-    }
+  .dp__input_wrap {
+    height: 100% !important;
+    max-height: 100% !important;
   }
   }
+
+  .date-range-picker-input {
+    height: 100% !important;
+    max-height: 100% !important;
+  }
+}
 </style>
 </style>

+ 30 - 30
components/Ui/ExpansionPanel.vue

@@ -25,47 +25,47 @@ Panneaux déroulants de type "accordéon"
 const props = defineProps({
 const props = defineProps({
   title: {
   title: {
     type: String,
     type: String,
-    required: true
+    required: true,
   },
   },
   icon: {
   icon: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>
-  .icon {
-    width: 47px;
-    height: 47px;
-    padding: 10px;
-    margin-right: 10px;
-    flex: none !important;
-  }
+.icon {
+  width: 47px;
+  height: 47px;
+  padding: 10px;
+  margin-right: 10px;
+  flex: none !important;
+}
 
 
-  .v-expansion-panel-header {
-    padding: 0;
-    padding-right: 20px;
-  }
+.v-expansion-panel-header {
+  padding: 0;
+  padding-right: 20px;
+}
 
 
-  .v-expansion-panel-title {
-    padding-left: 0;
-    padding-top: 0;
-    padding-bottom: 0;
-    max-height: 47px;
-    min-height: 47px;
-  }
+.v-expansion-panel-title {
+  padding-left: 0;
+  padding-top: 0;
+  padding-bottom: 0;
+  max-height: 47px;
+  min-height: 47px;
+}
 
 
-  .v-expansion-panel--active > .v-expansion-panel-title {
-    min-height: 47px !important;
-  }
+.v-expansion-panel--active > .v-expansion-panel-title {
+  min-height: 47px !important;
+}
 
 
-  :deep(.v-expansion-panel-title__icon > .v-icon) {
-    font-size: 16px;
-  }
+:deep(.v-expansion-panel-title__icon > .v-icon) {
+  font-size: 16px;
+}
 
 
-  .icon {
-    text-align: center;
-  }
+.icon {
+  text-align: center;
+}
 </style>
 </style>

+ 52 - 51
components/Ui/Form.vue

@@ -10,20 +10,20 @@ de quitter si des données ont été modifiées.
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <v-form
     <v-form
-        v-model="isValid"
-        ref="form"
-        :readonly="readonly"
-        @submit.prevent=""
+      v-model="isValid"
+      ref="form"
+      :readonly="readonly"
+      @submit.prevent=""
     >
     >
       <!-- Top action bar -->
       <!-- Top action bar -->
       <v-container
       <v-container
-          v-if="actionPosition === 'both' || actionPosition === 'top'"
-          :fluid="true"
-          class="container btnActions"
+        v-if="actionPosition === 'both' || actionPosition === 'top'"
+        :fluid="true"
+        class="container btnActions"
       >
       >
         <v-row>
         <v-row>
           <v-col cols="12" sm="12">
           <v-col cols="12" sm="12">
-            <slot name="form.button"/>
+            <slot name="form.button" />
 
 
             <UiButtonSubmit
             <UiButtonSubmit
               v-if="!readonly"
               v-if="!readonly"
@@ -36,17 +36,17 @@ de quitter si des données ont été modifiées.
       </v-container>
       </v-container>
 
 
       <!-- Content -->
       <!-- Content -->
-      <slot v-bind="{model, entity}"/>
+      <slot v-bind="{ model, entity }" />
 
 
       <!-- Bottom action bar -->
       <!-- Bottom action bar -->
       <v-container
       <v-container
-          v-if="actionPosition === 'both' || actionPosition === 'bottom'"
-          :fluid="true"
-          class="container btnActions mt-6"
+        v-if="actionPosition === 'both' || actionPosition === 'bottom'"
+        :fluid="true"
+        class="container btnActions mt-6"
       >
       >
         <v-row>
         <v-row>
           <v-col cols="12" sm="12">
           <v-col cols="12" sm="12">
-            <slot name="form.button"/>
+            <slot name="form.button" />
 
 
             <UiButtonSubmit
             <UiButtonSubmit
               @submit="submit"
               @submit="submit"
@@ -59,20 +59,21 @@ de quitter si des données ont été modifiées.
     </v-form>
     </v-form>
 
 
     <!-- Confirmation dialog -->
     <!-- Confirmation dialog -->
-    <LazyLayoutDialog
-      :show="isConfirmationDialogShowing"
-    >
+    <LazyLayoutDialog :show="isConfirmationDialogShowing">
       <template #dialogText>
       <template #dialogText>
         <v-card-title class="text-h5 theme-neutral">
         <v-card-title class="text-h5 theme-neutral">
           {{ $t('caution') }}
           {{ $t('caution') }}
         </v-card-title>
         </v-card-title>
         <v-card-text>
         <v-card-text>
-          <br>
+          <br />
           <p>{{ $t('quit_without_saving_warning') }}</p>
           <p>{{ $t('quit_without_saving_warning') }}</p>
         </v-card-text>
         </v-card-text>
       </template>
       </template>
       <template #dialogBtn>
       <template #dialogBtn>
-        <v-btn class="mr-4 submitBtn theme-primary" @click="closeConfirmationDialog">
+        <v-btn
+          class="mr-4 submitBtn theme-primary"
+          @click="closeConfirmationDialog"
+        >
           {{ $t('back_to_form') }}
           {{ $t('back_to_form') }}
         </v-btn>
         </v-btn>
         <v-btn class="mr-4 submitBtn theme-primary" @click="saveAndQuit">
         <v-btn class="mr-4 submitBtn theme-primary" @click="saveAndQuit">
@@ -83,24 +84,23 @@ de quitter si des données ont été modifiées.
         </v-btn>
         </v-btn>
       </template>
       </template>
     </LazyLayoutDialog>
     </LazyLayoutDialog>
-
   </LayoutContainer>
   </LayoutContainer>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {computed, ref} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import {FORM_FUNCTION, SUBMIT_TYPE, TYPE_ALERT} from "~/types/enum/enums";
-import { useFormStore } from "~/stores/form";
-import type {Route, RouteLocationRaw} from "@intlify/vue-router-bridge";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import ApiModel from "~/models/ApiModel";
-import {usePageStore} from "~/stores/page";
-import {watch} from "@vue/runtime-core";
-import type {PropType} from "@vue/runtime-core";
-import type {AnyJson} from "~/types/data";
+import { computed, ref } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import { FORM_FUNCTION, SUBMIT_TYPE, TYPE_ALERT } from '~/types/enum/enums'
+import { useFormStore } from '~/stores/form'
+import type { Route, RouteLocationRaw } from '@intlify/vue-router-bridge'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import ApiModel from '~/models/ApiModel'
+import { usePageStore } from '~/stores/page'
+import { watch } from '@vue/runtime-core'
+import type { PropType } from '@vue/runtime-core'
+import type { AnyJson } from '~/types/data'
 import * as _ from 'lodash-es'
 import * as _ from 'lodash-es'
-import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -108,26 +108,26 @@ const props = defineProps({
    */
    */
   model: {
   model: {
     type: Function as any as () => typeof ApiModel,
     type: Function as any as () => typeof ApiModel,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Instance de l'objet
    * Instance de l'objet
    */
    */
   entity: {
   entity: {
     type: Object as () => ApiModel,
     type: Object as () => ApiModel,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * TODO: compléter
    * TODO: compléter
    */
    */
   onChanged: {
   onChanged: {
     type: Function,
     type: Function,
-    required: false
+    required: false,
   },
   },
   goBackRoute: {
   goBackRoute: {
     type: Object as PropType<RouteLocationRaw>,
     type: Object as PropType<RouteLocationRaw>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Types de soumission disponibles (enregistrer / enregistrer et quitter)
    * Types de soumission disponibles (enregistrer / enregistrer et quitter)
@@ -139,7 +139,7 @@ const props = defineProps({
       let actions: AnyJson = {}
       let actions: AnyJson = {}
       actions[SUBMIT_TYPE.SAVE] = {}
       actions[SUBMIT_TYPE.SAVE] = {}
       return actions
       return actions
-    }
+    },
   },
   },
   /**
   /**
    * La validation est en cours
    * La validation est en cours
@@ -147,7 +147,7 @@ const props = defineProps({
   validationPending: {
   validationPending: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Faut-il rafraichir le profil à la soumission du formulaire?
    * Faut-il rafraichir le profil à la soumission du formulaire?
@@ -155,13 +155,13 @@ const props = defineProps({
   refreshProfile: {
   refreshProfile: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   actionPosition: {
   actionPosition: {
     type: String as PropType<'top' | 'bottom' | 'both'>,
     type: String as PropType<'top' | 'bottom' | 'both'>,
     required: false,
     required: false,
-    default: 'both'
-  }
+    default: 'both',
+  },
 })
 })
 
 
 // ### Définitions
 // ### Définitions
@@ -203,7 +203,7 @@ const closeConfirmationDialog = () => {
  *
  *
  * @param next
  * @param next
  */
  */
-const submit = async (next: string|null = null) => {
+const submit = async (next: string | null = null) => {
   if (props.validationPending) {
   if (props.validationPending) {
     return
     return
   }
   }
@@ -238,18 +238,21 @@ const submit = async (next: string|null = null) => {
     } else if (next === SUBMIT_TYPE.SAVE_AND_BACK) {
     } else if (next === SUBMIT_TYPE.SAVE_AND_BACK) {
       onSaveAndQuitAction(actionArgs)
       onSaveAndQuitAction(actionArgs)
     }
     }
-
   } catch (error: any) {
   } catch (error: any) {
-
-    if (error.response && error.response.status === 422 && error.response.data['violations']) {
-
+    if (
+      error.response &&
+      error.response.status === 422 &&
+      error.response.data['violations']
+    ) {
       // TODO: à revoir
       // TODO: à revoir
       const violations: Array<string> = []
       const violations: Array<string> = []
       let fields: AnyJson = {}
       let fields: AnyJson = {}
 
 
       for (const violation of error.response.data['violations']) {
       for (const violation of error.response.data['violations']) {
         violations.push(i18n.t(violation['message']) as string)
         violations.push(i18n.t(violation['message']) as string)
-        fields = Object.assign(fields, {[violation['propertyPath']] : violation['message']})
+        fields = Object.assign(fields, {
+          [violation['propertyPath']]: violation['message'],
+        })
       }
       }
 
 
       useFormStore().addViolation(fields)
       useFormStore().addViolation(fields)
@@ -280,7 +283,7 @@ const saveAndQuit = async () => {
  * @param route
  * @param route
  * @param id
  * @param id
  */
  */
-function onSaveAction(route: Route, id: number){
+function onSaveAction(route: Route, id: number) {
   if (useFormStore().formFunction === FORM_FUNCTION.CREATE) {
   if (useFormStore().formFunction === FORM_FUNCTION.CREATE) {
     route.path += id
     route.path += id
     navigateTo(route)
     navigateTo(route)
@@ -294,7 +297,7 @@ function onSaveAction(route: Route, id: number){
  *
  *
  * @param route
  * @param route
  */
  */
-function onSaveAndQuitAction(route: Route){
+function onSaveAndQuitAction(route: Route) {
   navigateTo(route)
   navigateTo(route)
 }
 }
 
 
@@ -314,7 +317,7 @@ const cancel = () => {
   }
   }
 }
 }
 
 
-const actions = computed(()=>{
+const actions = computed(() => {
   return _.keys(props.submitActions)
   return _.keys(props.submitActions)
 })
 })
 
 
@@ -346,7 +349,6 @@ const validate = async function () {
   errors.value = validation.errors
   errors.value = validation.errors
 }
 }
 
 
-
 // #### Gestion de l'état dirty
 // #### Gestion de l'état dirty
 watch(props.entity, async (newEntity, oldEntity) => {
 watch(props.entity, async (newEntity, oldEntity) => {
   setIsDirty(true)
   setIsDirty(true)
@@ -372,7 +374,6 @@ const setIsDirty = (dirty: boolean) => {
 }
 }
 
 
 defineExpose({ validate })
 defineExpose({ validate })
-
 </script>
 </script>
 
 
 <style scoped>
 <style scoped>

+ 13 - 21
components/Ui/Form/Creation.vue

@@ -1,27 +1,22 @@
 <template>
 <template>
-  <UiForm
-      :model="model"
-      :entity="entity"
-      :submitActions="submitActions"
-  >
+  <UiForm :model="model" :entity="entity" :submitActions="submitActions">
     <template #form.button>
     <template #form.button>
       <v-btn v-if="goBackRoute" class="theme-neutral mr-3" @click="quit">
       <v-btn v-if="goBackRoute" class="theme-neutral mr-3" @click="quit">
         {{ $t('cancel') }}
         {{ $t('cancel') }}
       </v-btn>
       </v-btn>
     </template>
     </template>
 
 
-    <slot v-bind="{model, entity}"/>
+    <slot v-bind="{ model, entity }" />
   </UiForm>
   </UiForm>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import type {PropType} from "@vue/runtime-core";
-import type {RouteLocationRaw} from "@intlify/vue-router-bridge";
-import ApiModel from "~/models/ApiModel";
-import type {AnyJson} from "~/types/data";
-import {SUBMIT_TYPE} from "~/types/enum/enums";
-import {useEntityManager} from "~/composables/data/useEntityManager";
+import type { PropType } from '@vue/runtime-core'
+import type { RouteLocationRaw } from '@intlify/vue-router-bridge'
+import ApiModel from '~/models/ApiModel'
+import type { AnyJson } from '~/types/data'
+import { SUBMIT_TYPE } from '~/types/enum/enums'
+import { useEntityManager } from '~/composables/data/useEntityManager'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -29,7 +24,7 @@ const props = defineProps({
    */
    */
   model: {
   model: {
     type: Function as any as () => typeof ApiModel,
     type: Function as any as () => typeof ApiModel,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Route de retour
    * Route de retour
@@ -37,7 +32,7 @@ const props = defineProps({
   goBackRoute: {
   goBackRoute: {
     type: Object as PropType<RouteLocationRaw>,
     type: Object as PropType<RouteLocationRaw>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * La validation est en cours
    * La validation est en cours
@@ -45,7 +40,7 @@ const props = defineProps({
   validationPending: {
   validationPending: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Faut-il rafraichir le profil à la soumission du formulaire ?
    * Faut-il rafraichir le profil à la soumission du formulaire ?
@@ -53,7 +48,7 @@ const props = defineProps({
   refreshProfile: {
   refreshProfile: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
 })
 })
 
 
@@ -81,9 +76,6 @@ const quit = () => {
 
 
   router.push(props.goBackRoute)
   router.push(props.goBackRoute)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 22 - 28
components/Ui/Form/Edition.vue

@@ -2,10 +2,10 @@
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
     <UiForm
     <UiForm
-        v-else
-        :model="model"
-        :entity="entity"
-        :submitActions="submitActions"
+      v-else
+      :model="model"
+      :entity="entity"
+      :submitActions="submitActions"
     >
     >
       <template #form.button>
       <template #form.button>
         <v-btn v-if="goBackRoute" class="theme-neutral mr-3" @click="quit">
         <v-btn v-if="goBackRoute" class="theme-neutral mr-3" @click="quit">
@@ -13,20 +13,19 @@
         </v-btn>
         </v-btn>
       </template>
       </template>
 
 
-      <slot v-bind="{model, entity}"/>
+      <slot v-bind="{ model, entity }" />
     </UiForm>
     </UiForm>
   </LayoutContainer>
   </LayoutContainer>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import type {PropType} from "@vue/runtime-core";
-import type {RouteLocationRaw} from "@intlify/vue-router-bridge";
-import ApiModel from "~/models/ApiModel";
-import type {AnyJson} from "~/types/data";
-import {SUBMIT_TYPE} from "~/types/enum/enums";
-import {useRoute} from "vue-router";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import type { PropType } from '@vue/runtime-core'
+import type { RouteLocationRaw } from '@intlify/vue-router-bridge'
+import ApiModel from '~/models/ApiModel'
+import type { AnyJson } from '~/types/data'
+import { SUBMIT_TYPE } from '~/types/enum/enums'
+import { useRoute } from 'vue-router'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -34,7 +33,7 @@ const props = defineProps({
    */
    */
   model: {
   model: {
     type: Function as any as () => typeof ApiModel,
     type: Function as any as () => typeof ApiModel,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Id de l'objet
    * Id de l'objet
@@ -43,7 +42,7 @@ const props = defineProps({
   id: {
   id: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Route de retour
    * Route de retour
@@ -51,7 +50,7 @@ const props = defineProps({
   goBackRoute: {
   goBackRoute: {
     type: Object as PropType<RouteLocationRaw>,
     type: Object as PropType<RouteLocationRaw>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * La validation est en cours
    * La validation est en cours
@@ -59,7 +58,7 @@ const props = defineProps({
   validationPending: {
   validationPending: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Faut-il rafraichir le profil à la soumission du formulaire ?
    * Faut-il rafraichir le profil à la soumission du formulaire ?
@@ -67,20 +66,18 @@ const props = defineProps({
   refreshProfile: {
   refreshProfile: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
-  }
+    default: false,
+  },
 })
 })
 
 
 const { fetch } = useEntityFetch()
 const { fetch } = useEntityFetch()
 const route = useRoute()
 const route = useRoute()
 const router = useRouter()
 const router = useRouter()
 
 
-const entityId = props.id !== null ? props.id : parseInt(route.params.id as string)
+const entityId =
+  props.id !== null ? props.id : parseInt(route.params.id as string)
 
 
-const { data: entity, pending } = fetch(
-    props.model,
-    entityId
-)
+const { data: entity, pending } = fetch(props.model, entityId)
 
 
 const submitActions = computed(() => {
 const submitActions = computed(() => {
   let actions: AnyJson = {}
   let actions: AnyJson = {}
@@ -100,9 +97,6 @@ const quit = () => {
 
 
   router.push(props.goBackRoute)
   router.push(props.goBackRoute)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 10 - 8
components/Ui/Help.vue

@@ -29,34 +29,34 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import type {Ref} from "@vue/reactivity";
+import type { Ref } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   left: {
   left: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   right: {
   right: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   top: {
   top: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   bottom: {
   bottom: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   icon: {
   icon: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: 'mdi-help-circle'
-  }
+    default: 'mdi-help-circle',
+  },
 })
 })
 
 
 const { $refs } = useNuxtApp()
 const { $refs } = useNuxtApp()
@@ -67,7 +67,9 @@ const show: Ref<Boolean> = ref(false)
 const iconRef = ref(null)
 const iconRef = ref(null)
 
 
 // Left is the default, set it to true if not any other is true
 // Left is the default, set it to true if not any other is true
-const leftOrDefault: Ref<Boolean> = ref(props.left || (!props.right && !props.bottom && !props.top))
+const leftOrDefault: Ref<Boolean> = ref(
+  props.left || (!props.right && !props.bottom && !props.top),
+)
 
 
 const onIconClicked = (e: any) => {
 const onIconClicked = (e: any) => {
   show.value = !show.value
   show.value = !show.value

+ 58 - 50
components/Ui/Image.vue

@@ -4,7 +4,7 @@ Permet d'afficher une image par défaut si l'image demandée n'est pas disponibl
 -->
 -->
 <template>
 <template>
   <main>
   <main>
-    <div class="image-wrapper" :style="{width: width + 'px'}">
+    <div class="image-wrapper" :style="{ width: width + 'px' }">
       <v-img
       <v-img
         :src="imageSrc ?? undefined"
         :src="imageSrc ?? undefined"
         :lazy-src="defaultImagePath"
         :lazy-src="defaultImagePath"
@@ -19,14 +19,15 @@ Permet d'afficher une image par défaut si l'image demandée n'est pas disponibl
             justify="center"
             justify="center"
             v-if="pending"
             v-if="pending"
           >
           >
-            <v-progress-circular
-              :indeterminate="true"
-              color="neutral"
-            />
+            <v-progress-circular :indeterminate="true" color="neutral" />
           </v-row>
           </v-row>
         </template>
         </template>
 
 
-        <div v-if="!pending && overlayIcon" class="overlay" @click="emit('overlay-clicked')">
+        <div
+          v-if="!pending && overlayIcon"
+          class="overlay"
+          @click="emit('overlay-clicked')"
+        >
           <v-icon>{{ overlayIcon }}</v-icon>
           <v-icon>{{ overlayIcon }}</v-icon>
         </div>
         </div>
       </v-img>
       </v-img>
@@ -35,10 +36,10 @@ Permet d'afficher une image par défaut si l'image demandée n'est pas disponibl
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useImageFetch} from "~/composables/data/useImageFetch";
-import ImageManager from "~/services/data/imageManager";
-import type {WatchStopHandle} from "@vue/runtime-core";
-import type {Ref} from "@vue/reactivity";
+import { useImageFetch } from '~/composables/data/useImageFetch'
+import ImageManager from '~/services/data/imageManager'
+import type { WatchStopHandle } from '@vue/runtime-core'
+import type { Ref } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -47,28 +48,28 @@ const props = defineProps({
   imageId: {
   imageId: {
     type: Number as PropType<number | null>,
     type: Number as PropType<number | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Image par défaut
    * Image par défaut
    */
    */
   defaultImage: {
   defaultImage: {
     type: String,
     type: String,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Hauteur de l'image à l'écran (en px)
    * Hauteur de l'image à l'écran (en px)
    */
    */
   height: {
   height: {
     type: Number,
     type: Number,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Largeur de l'image à l'écran (en px)
    * Largeur de l'image à l'écran (en px)
    */
    */
   width: {
   width: {
     type: Number,
     type: Number,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Icône à afficher en overlay au survol de la souris
    * Icône à afficher en overlay au survol de la souris
@@ -76,8 +77,8 @@ const props = defineProps({
   overlayIcon: {
   overlayIcon: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const { fetch } = useImageFetch()
 const { fetch } = useImageFetch()
@@ -88,7 +89,11 @@ const emit = defineEmits(['overlay-clicked'])
 
 
 const fileId = toRef(props, 'imageId')
 const fileId = toRef(props, 'imageId')
 
 
-const { data: imageSrc, pending, refresh: refreshImage } = await fetch(fileId, defaultImagePath, props.height, props.width) as any
+const {
+  data: imageSrc,
+  pending,
+  refresh: refreshImage,
+} = (await fetch(fileId, defaultImagePath, props.height, props.width)) as any
 
 
 const refresh = () => {
 const refresh = () => {
   refreshImage()
   refreshImage()
@@ -98,9 +103,12 @@ defineExpose({ refresh })
 /**
 /**
  * Si l'id change, on recharge l'image
  * Si l'id change, on recharge l'image
  */
  */
-const unwatch: WatchStopHandle = watch(() => props.imageId, async (value, oldValue) => {
-  refresh()
-})
+const unwatch: WatchStopHandle = watch(
+  () => props.imageId,
+  async (value, oldValue) => {
+    refresh()
+  },
+)
 
 
 /**
 /**
  * Lorsqu'on démonte le component, on supprime le watcher
  * Lorsqu'on démonte le component, on supprime le watcher
@@ -111,38 +119,38 @@ onUnmounted(() => {
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
-  div.image-wrapper {
-    display: block;
-    position: relative;
+div.image-wrapper {
+  display: block;
+  position: relative;
 
 
-    img {
-      display: block;
-      max-width: 100%;
-    }
+  img {
+    display: block;
+    max-width: 100%;
+  }
 
 
-    .overlay {
-      position: absolute;
-      top: 0;
-      bottom: 0;
-      left: 0;
-      right: 0;
-      height: 100%;
-      width: 100%;
-      opacity: 0;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      transition: .3s ease;
-    }
-    .overlay:hover {
-      opacity: 0.8;
-      background-color: rgb(var(--v-theme-neutral-strong));
-      cursor: pointer;
-    }
+  .overlay {
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 0;
+    height: 100%;
+    width: 100%;
+    opacity: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    transition: 0.3s ease;
+  }
+  .overlay:hover {
+    opacity: 0.8;
+    background-color: rgb(var(--v-theme-neutral-strong));
+    cursor: pointer;
+  }
 
 
-    .overlay .v-icon {
-      color: rgb(var(--v-theme-on-neutral-strong));
-      font-size: 36px;
-    }
+  .overlay .v-icon {
+    color: rgb(var(--v-theme-on-neutral-strong));
+    font-size: 36px;
   }
   }
+}
 </style>
 </style>

+ 92 - 67
components/Ui/Input/Autocomplete.vue

@@ -8,45 +8,49 @@ Liste déroulante avec autocompletion, à placer dans un composant `UiForm`
   <main>
   <main>
     <!--suppress TypeScriptValidateTypes -->
     <!--suppress TypeScriptValidateTypes -->
     <v-autocomplete
     <v-autocomplete
-        :model-value="modelValue"
-        autocomplete="search"
-        :items="items"
-        :label="$t(fieldLabel)"
-        :item-title="itemTitle"
-        :item-value="itemValue"
-        :no-filter="noFilter"
-        :auto-select-first="autoSelectFirst"
-        :multiple="multiple"
-        :loading="isLoading"
-        :return-object="returnObject"
-        :search-input.sync="search"
-        :prepend-icon="prependIcon"
-        :error="error || !!fieldViolations"
-        :error-messages="errorMessage || fieldViolations ? $t(fieldViolations) : ''"
-        :rules="rules"
-        :chips="chips"
-        :hide-no-data="hideNoData"
-        :no-data-text="isLoading ? $t('please_wait') : $t('no_result_matching_your_request')"
-        :variant="variant"
-        @update:model-value="onUpdate"
-        @update:search="emit('update:search', $event)"
-        @update:menu="emit('update:menu', $event)"
-        @update:focused="emit('update:focused', $event)"
+      :model-value="modelValue"
+      autocomplete="search"
+      :items="items"
+      :label="$t(fieldLabel)"
+      :item-title="itemTitle"
+      :item-value="itemValue"
+      :no-filter="noFilter"
+      :auto-select-first="autoSelectFirst"
+      :multiple="multiple"
+      :loading="isLoading"
+      :return-object="returnObject"
+      :search-input.sync="search"
+      :prepend-icon="prependIcon"
+      :error="error || !!fieldViolations"
+      :error-messages="
+        errorMessage || fieldViolations ? $t(fieldViolations) : ''
+      "
+      :rules="rules"
+      :chips="chips"
+      :hide-no-data="hideNoData"
+      :no-data-text="
+        isLoading ? $t('please_wait') : $t('no_result_matching_your_request')
+      "
+      :variant="variant"
+      @update:model-value="onUpdate"
+      @update:search="emit('update:search', $event)"
+      @update:menu="emit('update:menu', $event)"
+      @update:focused="emit('update:focused', $event)"
     >
     >
       <template v-if="slotText" #item="data">
       <template v-if="slotText" #item="data">
-<!--        <v-list-item-content v-text="data.item.slotTextDisplay"></v-list-item-content>-->
+        <!--        <v-list-item-content v-text="data.item.slotTextDisplay"></v-list-item-content>-->
       </template>
       </template>
     </v-autocomplete>
     </v-autocomplete>
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {computed} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import ObjectUtils from "~/services/utils/objectUtils";
-import type {AnyJson} from "~/types/data";
-import type {PropType} from "@vue/runtime-core";
+import { computed } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import ObjectUtils from '~/services/utils/objectUtils'
+import type { AnyJson } from '~/types/data'
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -55,7 +59,7 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: [String, Number, Object, Array] as PropType<any>,
     type: [String, Number, Object, Array] as PropType<any>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -65,7 +69,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -74,7 +78,7 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Liste des éléments de la liste
    * Liste des éléments de la liste
@@ -83,7 +87,7 @@ const props = defineProps({
   items: {
   items: {
     type: Array as PropType<Array<Object>>,
     type: Array as PropType<Array<Object>>,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
@@ -91,7 +95,7 @@ const props = defineProps({
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Le model est l'objet lui-même, et non pas son id (ou la propriété définie avec itemValue)
    * Le model est l'objet lui-même, et non pas son id (ou la propriété définie avec itemValue)
@@ -99,7 +103,7 @@ const props = defineProps({
    */
    */
   returnObject: {
   returnObject: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Autorise la sélection multiple
    * Autorise la sélection multiple
@@ -107,7 +111,7 @@ const props = defineProps({
    */
    */
   multiple: {
   multiple: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Propriété de l'objet à utiliser comme label
    * Propriété de l'objet à utiliser comme label
@@ -115,7 +119,7 @@ const props = defineProps({
    */
    */
   itemTitle: {
   itemTitle: {
     type: String,
     type: String,
-    default: 'title'
+    default: 'title',
   },
   },
   /**
   /**
    * Propriété de l'objet à utiliser comme clé (et correspondant au v-model)
    * Propriété de l'objet à utiliser comme clé (et correspondant au v-model)
@@ -123,14 +127,14 @@ const props = defineProps({
    */
    */
   itemValue: {
   itemValue: {
     type: String,
     type: String,
-    default: 'id'
+    default: 'id',
   },
   },
   /**
   /**
    * Icône de gauche
    * Icône de gauche
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-prepend-icon
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-prepend-icon
    */
    */
   prependIcon: {
   prependIcon: {
-    type: String
+    type: String,
   },
   },
   /**
   /**
    * Rends les résultats sous forme de puces
    * Rends les résultats sous forme de puces
@@ -138,7 +142,7 @@ const props = defineProps({
    */
    */
   chips: {
   chips: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Le contenu de la liste est en cours de chargement
    * Le contenu de la liste est en cours de chargement
@@ -146,7 +150,7 @@ const props = defineProps({
   isLoading: {
   isLoading: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Propriété de l'objet utilisé pour grouper les items ; laisser null pour ne pas grouper
    * Propriété de l'objet utilisé pour grouper les items ; laisser null pour ne pas grouper
@@ -154,7 +158,7 @@ const props = defineProps({
   group: {
   group: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-hide-no-data
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-hide-no-data
@@ -162,32 +166,32 @@ const props = defineProps({
   hideNoData: {
   hideNoData: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   // TODO: c'est quoi?
   // TODO: c'est quoi?
   slotText: {
   slotText: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-no-filter
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-no-filter
    */
    */
   noFilter: {
   noFilter: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-auto-select-first
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-auto-select-first
    */
    */
   autoSelectFirst: {
   autoSelectFirst: {
     type: Boolean,
     type: Boolean,
-    default: true
+    default: true,
   },
   },
   // TODO: c'est quoi?
   // TODO: c'est quoi?
   translate: {
   translate: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -196,14 +200,14 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
    */
    */
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur?
    * Si le champ est en état d'erreur, quel est le message d'erreur?
@@ -211,27 +215,41 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    */
    */
   variant: {
   variant: {
-    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    type: String as PropType<
+      | 'filled'
+      | 'outlined'
+      | 'plain'
+      | 'underlined'
+      | 'solo'
+      | 'solo-inverted'
+      | 'solo-filled'
+      | undefined
+    >,
     required: false,
     required: false,
-    default: 'filled'
-  }
+    default: 'filled',
+  },
 })
 })
 
 
 const i18n = useI18n()
 const i18n = useI18n()
 
 
-const search: Ref<string|null> = ref(null)
+const search: Ref<string | null> = ref(null)
 
 
 const fieldLabel: string = props.label ?? props.field
 const fieldLabel: string = props.label ?? props.field
 
 
-const {fieldViolations, updateViolationState} = useFieldViolation(props.field)
+const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
 
 
-const emit = defineEmits(['update:model-value', 'update:search', 'update:focused', 'update:menu'])
+const emit = defineEmits([
+  'update:model-value',
+  'update:search',
+  'update:focused',
+  'update:menu',
+])
 
 
 const onUpdate = (event: string) => {
 const onUpdate = (event: string) => {
   updateViolationState(event)
   updateViolationState(event)
@@ -292,16 +310,17 @@ const prepareGroups = (groupedItems: Array<Array<string>>): Array<AnyJson> => {
   let finalItems: Array<AnyJson> = []
   let finalItems: Array<AnyJson> = []
 
 
   for (const group in groupedItems) {
   for (const group in groupedItems) {
-
     // Si un groupe est présent, alors on créé le groupe options header
     // Si un groupe est présent, alors on créé le groupe options header
     if (group !== 'undefined') {
     if (group !== 'undefined') {
-      finalItems.push({header: i18n.t(group as string)})
+      finalItems.push({ header: i18n.t(group as string) })
     }
     }
 
 
     // On parcourt les items pour préparer les texts / slotTexts à afficher
     // On parcourt les items pour préparer les texts / slotTexts à afficher
-    finalItems = finalItems.concat(groupedItems[group].map((item: any) => {
-      return prepareItem(item)
-    }))
+    finalItems = finalItems.concat(
+      groupedItems[group].map((item: any) => {
+        return prepareItem(item)
+      }),
+    )
   }
   }
   return finalItems
   return finalItems
 }
 }
@@ -321,16 +340,22 @@ const prepareItem = (item: Object): AnyJson => {
   // Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
   // Si on souhaite avoir un texte différent dans les propositions que dans la sélection finale de select
   if (props.slotText) {
   if (props.slotText) {
     for (const text of props.slotText) {
     for (const text of props.slotText) {
-      slotTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
+      slotTextDisplay.push(
+        props.translate ? i18n.t(item[text as string]) : item[text as string],
+      )
     }
     }
   }
   }
 
 
   for (const text of props.itemTitle) {
   for (const text of props.itemTitle) {
-    itemTextDisplay.push(props.translate ? i18n.t(item[text as string]) : item[text as string])
+    itemTextDisplay.push(
+      props.translate ? i18n.t(item[text as string]) : item[text as string],
+    )
   }
   }
 
 
   // On reconstruit l'objet
   // On reconstruit l'objet
-  return Object.assign({}, item, { itemTextDisplay: itemTextDisplay.join(' '), slotTextDisplay: slotTextDisplay.join(' ') })
+  return Object.assign({}, item, {
+    itemTextDisplay: itemTextDisplay.join(' '),
+    slotTextDisplay: slotTextDisplay.join(' '),
+  })
 }
 }
-
 </script>
 </script>

+ 62 - 50
components/Ui/Input/Autocomplete/Accesses.vue

@@ -7,34 +7,34 @@ Champs autocomplete dédié à la recherche des access d'une structure
 <template>
 <template>
   <main>
   <main>
     <UiInputAutocomplete
     <UiInputAutocomplete
-        :model-value="modelValue"
-        :field="field"
-        :label="label"
-        :items="items"
-        item-value="id"
-        :isLoading="pending"
-        :multiple="multiple"
-        hide-no-data
-        :chips="chips"
-        :auto-select-first="false"
-        prependIcon="fas fa-magnifying-glass"
-        :return-object="false"
-        :variant="variant"
-        @update:model-value="onUpdateModelValue"
-        @update:search="onUpdateSearch"
+      :model-value="modelValue"
+      :field="field"
+      :label="label"
+      :items="items"
+      item-value="id"
+      :isLoading="pending"
+      :multiple="multiple"
+      hide-no-data
+      :chips="chips"
+      :auto-select-first="false"
+      prependIcon="fas fa-magnifying-glass"
+      :return-object="false"
+      :variant="variant"
+      @update:model-value="onUpdateModelValue"
+      @update:search="onUpdateSearch"
     />
     />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import type {PropType} from "@vue/runtime-core";
-import {computed} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import type {AnyJson, AssociativeArray} from "~/types/data";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import Access from "~/models/Access/Access";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import ArrayUtils from "~/services/utils/arrayUtils";
+import type { PropType } from '@vue/runtime-core'
+import { computed } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import type { AnyJson, AssociativeArray } from '~/types/data'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import Access from '~/models/Access/Access'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import ArrayUtils from '~/services/utils/arrayUtils'
 import * as _ from 'lodash-es'
 import * as _ from 'lodash-es'
 
 
 const props = defineProps({
 const props = defineProps({
@@ -44,7 +44,7 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: [Object, Array],
     type: [Object, Array],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Filtres à transmettre à la source de données
    * Filtres à transmettre à la source de données
@@ -52,7 +52,7 @@ const props = defineProps({
   filters: {
   filters: {
     type: Object as PropType<Ref<AssociativeArray>>,
     type: Object as PropType<Ref<AssociativeArray>>,
     required: false,
     required: false,
-    default: ref(null)
+    default: ref(null),
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -62,7 +62,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -71,7 +71,7 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
@@ -79,7 +79,7 @@ const props = defineProps({
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Autorise la sélection multiple
    * Autorise la sélection multiple
@@ -87,7 +87,7 @@ const props = defineProps({
    */
    */
   multiple: {
   multiple: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Rends les résultats sous forme de puces
    * Rends les résultats sous forme de puces
@@ -95,30 +95,39 @@ const props = defineProps({
    */
    */
   chips: {
   chips: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Closes the menu and clear the current search after the selection has been updated
    * Closes the menu and clear the current search after the selection has been updated
    */
    */
   clearSearchAfterUpdate: {
   clearSearchAfterUpdate: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    */
    */
   variant: {
   variant: {
-    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    type: String as PropType<
+      | 'filled'
+      | 'outlined'
+      | 'plain'
+      | 'underlined'
+      | 'solo'
+      | 'solo-inverted'
+      | 'solo-filled'
+      | undefined
+    >,
     required: false,
     required: false,
-    default: 'filled'
-  }
+    default: 'filled',
+  },
 })
 })
 
 
 /**
 /**
  * Element de la liste autocomplete
  * Element de la liste autocomplete
  */
  */
 interface AccessListItem {
 interface AccessListItem {
-  id: number | string,
+  id: number | string
   title: string
   title: string
 }
 }
 
 
@@ -133,7 +142,9 @@ const i18n = useI18n()
 const accessToItem = (access: Access): AccessListItem => {
 const accessToItem = (access: Access): AccessListItem => {
   return {
   return {
     id: access.id,
     id: access.id,
-    title: access.person ? `${access.person.name} ${access.person.givenName}` : i18n.t('unknown')
+    title: access.person
+      ? `${access.person.name} ${access.person.givenName}`
+      : i18n.t('unknown'),
   }
   }
 }
 }
 
 
@@ -148,7 +159,7 @@ const nameFilter: Ref<string | null> = ref(null)
  * Query transmise à l'API lors des changements de filtre de recherche
  * Query transmise à l'API lors des changements de filtre de recherche
  */
  */
 const query: ComputedRef<AnyJson> = computed(() => {
 const query: ComputedRef<AnyJson> = computed(() => {
-  let q: AnyJson = {'groups[]': 'access_people_ref', 'order[name]': 'asc'}
+  let q: AnyJson = { 'groups[]': 'access_people_ref', 'order[name]': 'asc' }
 
 
   if (!initialized.value && props.modelValue) {
   if (!initialized.value && props.modelValue) {
     if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
     if (Array.isArray(props.modelValue) && props.modelValue.length > 0) {
@@ -169,18 +180,17 @@ const query: ComputedRef<AnyJson> = computed(() => {
 /**
 /**
  * On commence par fetcher les accesses déjà actifs, pour affichage des noms
  * On commence par fetcher les accesses déjà actifs, pour affichage des noms
  */
  */
-const { data: collection, pending, refresh } = await fetchCollection(
-    Access,
-    null,
-    query
-)
+const {
+  data: collection,
+  pending,
+  refresh,
+} = await fetchCollection(Access, null, query)
 initialized.value = true
 initialized.value = true
 
 
 // On a déjà récupéré les access actifs, on relance une requête pour récupérer la première page
 // On a déjà récupéré les access actifs, on relance une requête pour récupérer la première page
 // des accesses suivants
 // des accesses suivants
 refresh()
 refresh()
 
 
-
 /**
 /**
  * Contenu de la liste autocomplete
  * Contenu de la liste autocomplete
  */
  */
@@ -200,7 +210,10 @@ const items: ComputedRef<Array<AccessListItem>> = computed(() => {
   fetchedItems.sort((a, b) => {
   fetchedItems.sort((a, b) => {
     if (props.modelValue.includes(a.id) && !props.modelValue.includes(b.id)) {
     if (props.modelValue.includes(a.id) && !props.modelValue.includes(b.id)) {
       return -1
       return -1
-    } else if (!props.modelValue.includes(a.id) && props.modelValue.includes(b.id)) {
+    } else if (
+      !props.modelValue.includes(a.id) &&
+      props.modelValue.includes(b.id)
+    ) {
       return 1
       return 1
     } else {
     } else {
       return a.title.localeCompare(b.title)
       return a.title.localeCompare(b.title)
@@ -210,7 +223,6 @@ const items: ComputedRef<Array<AccessListItem>> = computed(() => {
   return fetchedItems
   return fetchedItems
 })
 })
 
 
-
 /**
 /**
  * Délai entre le dernier caractère saisi et la requête de vérification de la mise à jour des résultats (en ms)
  * Délai entre le dernier caractère saisi et la requête de vérification de la mise à jour des résultats (en ms)
  */
  */
@@ -221,7 +233,7 @@ const inputDelay = 600
  * @see https://docs-lodash.com/v4/debounce/
  * @see https://docs-lodash.com/v4/debounce/
  */
  */
 const refreshDebounced: _.DebouncedFunc<() => void> = _.debounce(async () => {
 const refreshDebounced: _.DebouncedFunc<() => void> = _.debounce(async () => {
-  await refresh();
+  await refresh()
 }, inputDelay)
 }, inputDelay)
 
 
 // ### Events
 // ### Events
@@ -238,14 +250,14 @@ const onUpdateSearch = (event: string) => {
 
 
 const onUpdateModelValue = (event: Array<number>) => {
 const onUpdateModelValue = (event: Array<number>) => {
   if (props.clearSearchAfterUpdate) {
   if (props.clearSearchAfterUpdate) {
-    nameFilter.value = ""
+    nameFilter.value = ''
   }
   }
   emit('update:model-value', event)
   emit('update:model-value', event)
 }
 }
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .v-autocomplete {
-    min-width: 350px;
-  }
+.v-autocomplete {
+  min-width: 350px;
+}
 </style>
 </style>

+ 44 - 45
components/Ui/Input/AutocompleteWithAPI.vue

@@ -7,95 +7,94 @@ d'une api)
 <template>
 <template>
   <main>
   <main>
     <UiInputAutocomplete
     <UiInputAutocomplete
-        :field="field"
-        :label="label"
-        :data="remoteData ? remoteData : data"
-        :items="items"
-        :isLoading="isLoading"
-        :item-text="itemText"
-        :slotText="slotText"
-        :item-value="itemValue"
-        :multiple="multiple"
-        :chips="chips"
-        prependIcon="mdi-magnify"
-        :return-object="returnObject"
-        @research="search"
-        :no-filter="noFilter"
-        @update="$emit('update', $event, field)"
+      :field="field"
+      :label="label"
+      :data="remoteData ? remoteData : data"
+      :items="items"
+      :isLoading="isLoading"
+      :item-text="itemText"
+      :slotText="slotText"
+      :item-value="itemValue"
+      :multiple="multiple"
+      :chips="chips"
+      prependIcon="mdi-magnify"
+      :return-object="returnObject"
+      @research="search"
+      :no-filter="noFilter"
+      @update="$emit('update', $event, field)"
     />
     />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {ref, toRefs} from "@vue/reactivity";
-import type {Ref} from "@vue/reactivity";
-import UrlUtils from "~/services/utils/urlUtils";
-import {useFetch} from "#app";
-import {watch} from "@vue/runtime-core";
+import { ref, toRefs } from '@vue/reactivity'
+import type { Ref } from '@vue/reactivity'
+import UrlUtils from '~/services/utils/urlUtils'
+import { useFetch } from '#app'
+import { watch } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   searchFunction: {
   searchFunction: {
     type: Function,
     type: Function,
-    required: true
+    required: true,
   },
   },
   data: {
   data: {
     type: [String, Number, Object, Array],
     type: [String, Number, Object, Array],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   remoteUri: {
   remoteUri: {
     type: [Array],
     type: [Array],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   remoteUrl: {
   remoteUrl: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   itemValue: {
   itemValue: {
     type: String,
     type: String,
-    default: 'id'
+    default: 'id',
   },
   },
   itemTitle: {
   itemTitle: {
     type: Array,
     type: Array,
-    required: true
+    required: true,
   },
   },
   slotText: {
   slotText: {
     type: Array,
     type: Array,
-    required: false
+    required: false,
   },
   },
   returnObject: {
   returnObject: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   noFilter: {
   noFilter: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   multiple: {
   multiple: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   chips: {
   chips: {
     type: Boolean,
     type: Boolean,
-    default: false
-  }
+    default: false,
+  },
 })
 })
 
 
 const { data } = toRefs(props)
 const { data } = toRefs(props)
@@ -103,19 +102,19 @@ const items = ref([])
 const remoteData: Ref<Array<string> | null> = ref(null)
 const remoteData: Ref<Array<string> | null> = ref(null)
 const isLoading = ref(false)
 const isLoading = ref(false)
 
 
-
 if (props.data) {
 if (props.data) {
-  items.value = props.multiple ? (data.value ?? []) : [data.value]
-
+  items.value = props.multiple ? data.value ?? [] : [data.value]
 } else if (props.remoteUri) {
 } else if (props.remoteUri) {
+  const ids: Array<any> = []
 
 
-  const ids:Array<any> = []
-
-  for(const uri of props.remoteUri){
+  for (const uri of props.remoteUri) {
     ids.push(UrlUtils.extractIdFromUri(uri as string))
     ids.push(UrlUtils.extractIdFromUri(uri as string))
   }
   }
 
 
-  const options: FetchOptions = { method: 'GET', query: {key: 'id', value: ids.join(',')} }
+  const options: FetchOptions = {
+    method: 'GET',
+    query: { key: 'id', value: ids.join(',') },
+  }
 
 
   useFetch(async () => {
   useFetch(async () => {
     isLoading.value = true
     isLoading.value = true
@@ -128,14 +127,14 @@ if (props.data) {
   })
   })
 }
 }
 
 
-const search = async (research:string) => {
+const search = async (research: string) => {
   isLoading.value = true
   isLoading.value = true
   const func: Function = props.searchFunction
   const func: Function = props.searchFunction
   items.value = items.value.concat(await func(research, props.field))
   items.value = items.value.concat(await func(research, props.field))
   isLoading.value = false
   isLoading.value = false
 }
 }
 
 
-const unwatch = watch(data,(d) => {
+const unwatch = watch(data, (d) => {
   items.value = props.multiple ? d : [d]
   items.value = props.multiple ? d : [d]
 })
 })
 
 

+ 37 - 28
components/Ui/Input/AutocompleteWithAp2i.vue

@@ -22,14 +22,13 @@ Liste déroulante avec autocompletion issue de Ap2i
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {computed} from "@vue/reactivity";
-import type {ComputedRef, Ref} from "@vue/reactivity";
-import type {PropType} from "@vue/runtime-core";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import ApiResource from "~/models/ApiResource";
-import ApiModel from "~/models/ApiModel";
-import type {AnyJson, AssociativeArray} from "~/types/data";
+import { computed } from '@vue/reactivity'
+import type { ComputedRef, Ref } from '@vue/reactivity'
+import type { PropType } from '@vue/runtime-core'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import ApiResource from '~/models/ApiResource'
+import ApiModel from '~/models/ApiModel'
+import type { AnyJson, AssociativeArray } from '~/types/data'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -38,14 +37,14 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: [String, Number, Object, Array],
     type: [String, Number, Object, Array],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Classe de l'ApiModel (ex: Organization, Notification, ...) qui sert de source à la liste
    * Classe de l'ApiModel (ex: Organization, Notification, ...) qui sert de source à la liste
    */
    */
   model: {
   model: {
     type: Function as any as () => typeof ApiModel,
     type: Function as any as () => typeof ApiModel,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Filtres à transmettre à la source de données
    * Filtres à transmettre à la source de données
@@ -53,7 +52,7 @@ const props = defineProps({
   query: {
   query: {
     type: Object as PropType<Ref<AssociativeArray>>,
     type: Object as PropType<Ref<AssociativeArray>>,
     required: false,
     required: false,
-    default: ref(null)
+    default: ref(null),
   },
   },
   /**
   /**
    * Fonction qui sera exécutée sur chaque item, et qui doit renvoyer un objet contenant les
    * Fonction qui sera exécutée sur chaque item, et qui doit renvoyer un objet contenant les
@@ -63,9 +62,11 @@ const props = defineProps({
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-item-value
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-item-value
    */
    */
   transformation: {
   transformation: {
-    type: Function as PropType<(item: ApiResource) => { id: number | string, title: string }>,
+    type: Function as PropType<
+      (item: ApiResource) => { id: number | string; title: string }
+    >,
     required: false,
     required: false,
-    default: (item: ApiResource) => item
+    default: (item: ApiResource) => item,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -75,7 +76,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -84,7 +85,7 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
@@ -92,7 +93,7 @@ const props = defineProps({
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Autorise la sélection multiple
    * Autorise la sélection multiple
@@ -100,7 +101,7 @@ const props = defineProps({
    */
    */
   multiple: {
   multiple: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Rends les résultats sous forme de puces
    * Rends les résultats sous forme de puces
@@ -108,30 +109,38 @@ const props = defineProps({
    */
    */
   chips: {
   chips: {
     type: Boolean,
     type: Boolean,
-    default: false
+    default: false,
   },
   },
   // TODO: c'est quoi?
   // TODO: c'est quoi?
   slotText: {
   slotText: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
 })
 })
 
 
 const { fetchCollection } = useEntityFetch()
 const { fetchCollection } = useEntityFetch()
 
 
 const query: ComputedRef<AnyJson> = computed(() => {
 const query: ComputedRef<AnyJson> = computed(() => {
-  return { ...(props.query.value ?? {}), ...{ 'groups[]': 'access_people_ref' } }
+  return {
+    ...(props.query.value ?? {}),
+    ...{ 'groups[]': 'access_people_ref' },
+  }
 })
 })
 
 
-const { data: collection, pending } = await fetchCollection(props.model, null, query)
+const { data: collection, pending } = await fetchCollection(
+  props.model,
+  null,
+  query,
+)
 
 
-const items: ComputedRef<Array<{ id: number | string, title: string }>> = computed(() => {
-  if (!pending.value && collection.value && collection.value.items) {
-    console.log(collection)
+const items: ComputedRef<Array<{ id: number | string; title: string }>> =
+  computed(() => {
+    if (!pending.value && collection.value && collection.value.items) {
+      console.log(collection)
 
 
-    return collection.value.items.map(props.transformation)
-  }
-  return []
-})
+      return collection.value.items.map(props.transformation)
+    }
+    return []
+  })
 </script>
 </script>

+ 31 - 28
components/Ui/Input/AutocompleteWithEnum.vue

@@ -1,55 +1,61 @@
-
 <template>
 <template>
   <UiInputAutocomplete
   <UiInputAutocomplete
-      :model-value="modelValue"
-      :field="field"
-      :items="items"
-      :is-loading="pending"
-      :return-object="false"
-      item-title="label"
-      item-value="value"
-      :variant="variant"
-      @update:model-value="$emit('update:model-value', $event)"
+    :model-value="modelValue"
+    :field="field"
+    :items="items"
+    :is-loading="pending"
+    :return-object="false"
+    item-title="label"
+    item-value="value"
+    :variant="variant"
+    @update:model-value="$emit('update:model-value', $event)"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-
-import {useEnumFetch} from "~/composables/data/useEnumFetch";
-import ArrayUtils from "~/services/utils/arrayUtils";
-import type {ComputedRef} from "@vue/reactivity";
-import type {Enum} from "~/types/data";
-import type {PropType} from "@vue/runtime-core";
+import { useEnumFetch } from '~/composables/data/useEnumFetch'
+import ArrayUtils from '~/services/utils/arrayUtils'
+import type { ComputedRef } from '@vue/reactivity'
+import type { Enum } from '~/types/data'
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   modelValue: {
   modelValue: {
     type: String as PropType<string | null>,
     type: String as PropType<string | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   enumName: {
   enumName: {
     type: String,
     type: String,
-    required: true
+    required: true,
   },
   },
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    */
    */
   variant: {
   variant: {
-    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    type: String as PropType<
+      | 'filled'
+      | 'outlined'
+      | 'plain'
+      | 'underlined'
+      | 'solo'
+      | 'solo-inverted'
+      | 'solo-filled'
+      | undefined
+    >,
     required: false,
     required: false,
-    default: 'filled'
-  }
+    default: 'filled',
+  },
 })
 })
 
 
 const { fetch } = useEnumFetch()
 const { fetch } = useEnumFetch()
@@ -62,9 +68,6 @@ const items: ComputedRef<Array<Enum>> = computed(() => {
   }
   }
   return ArrayUtils.sortObjectsByProp(enumItems.value, 'label') as Array<Enum>
   return ArrayUtils.sortObjectsByProp(enumItems.value, 'label') as Array<Enum>
 })
 })
-
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 11 - 13
components/Ui/Input/Checkbox.vue

@@ -17,7 +17,7 @@ Case à cocher, à placer dans un composant `UiForm`
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -25,7 +25,7 @@ const props = defineProps({
    */
    */
   modelValue: {
   modelValue: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -35,7 +35,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -44,7 +44,7 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
@@ -52,7 +52,7 @@ const props = defineProps({
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -61,14 +61,14 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
    */
    */
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur?
    * Si le champ est en état d'erreur, quel est le message d'erreur?
@@ -76,11 +76,11 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
-const {fieldViolations, updateViolationState} = useFieldViolation(props.field)
+const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
 
 
 const fieldLabel: string = props.label ?? props.field
 const fieldLabel: string = props.label ?? props.field
 
 
@@ -90,8 +90,6 @@ const onUpdate = (event: boolean) => {
   updateViolationState(event)
   updateViolationState(event)
   emit('update:model-value', event)
   emit('update:model-value', event)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 16 - 19
components/Ui/Input/Combobox.vue

@@ -5,10 +5,7 @@ Liste déroulante, à placer dans un composant `UiForm`
 -->
 -->
 
 
 <template>
 <template>
-  <v-container
-    class="px-0"
-    fluid
-  >
+  <v-container class="px-0" fluid>
     <v-combobox
     <v-combobox
       :model-value="modelValue"
       :model-value="modelValue"
       :value="modelValue"
       :value="modelValue"
@@ -16,14 +13,16 @@ Liste déroulante, à placer dans un composant `UiForm`
       :items="items"
       :items="items"
       :disabled="readonly"
       :disabled="readonly"
       :error="error || !!fieldViolations"
       :error="error || !!fieldViolations"
-      :error-messages="errorMessage || fieldViolations ? $t(fieldViolations) : ''"
+      :error-messages="
+        errorMessage || fieldViolations ? $t(fieldViolations) : ''
+      "
       @update:model-value="onUpdate($event)"
       @update:model-value="onUpdate($event)"
     />
     />
   </v-container>
   </v-container>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -31,7 +30,7 @@ const props = defineProps({
    */
    */
   modelValue: {
   modelValue: {
     type: [String, Number],
     type: [String, Number],
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -41,7 +40,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -50,21 +49,21 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Liste des éléments de la liste
    * Liste des éléments de la liste
    */
    */
   items: {
   items: {
     type: Array,
     type: Array,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -73,14 +72,14 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
    */
    */
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur?
    * Si le champ est en état d'erreur, quel est le message d'erreur?
@@ -88,11 +87,11 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
-const {fieldViolations, updateViolationState} = useFieldViolation(props.field)
+const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
 
 
 const fieldLabel: string = props.label ?? props.field
 const fieldLabel: string = props.label ?? props.field
 
 
@@ -102,8 +101,6 @@ const onUpdate = (event: string) => {
   updateViolationState(event)
   updateViolationState(event)
   emit('update:model-value', event)
   emit('update:model-value', event)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 20 - 22
components/Ui/Input/DatePicker.vue

@@ -8,11 +8,11 @@ Sélecteur de dates, à placer dans un composant `UiForm`
       <span>{{ $t(fieldLabel) }}</span>
       <span>{{ $t(fieldLabel) }}</span>
 
 
       <UiDatePicker
       <UiDatePicker
-          v-model="date"
-          :readonly="readonly"
-          :format="format"
-          :position="position"
-          @update:model-value="onUpdate($event)"
+        v-model="date"
+        :readonly="readonly"
+        :format="format"
+        :position="position"
+        @update:model-value="onUpdate($event)"
       />
       />
 
 
       <span v-if="error || !!fieldViolations" class="theme-danger">
       <span v-if="error || !!fieldViolations" class="theme-danger">
@@ -23,9 +23,9 @@ Sélecteur de dates, à placer dans un composant `UiForm`
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import {formatISO} from "date-fns";
-import type {PropType} from "@vue/runtime-core";
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import { formatISO } from 'date-fns'
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -34,7 +34,7 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: String as PropType<string | null>,
     type: String as PropType<string | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -44,7 +44,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -53,14 +53,14 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Format d'affichage des dates
    * Format d'affichage des dates
@@ -69,7 +69,7 @@ const props = defineProps({
   format: {
   format: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -78,14 +78,14 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
    */
    */
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur?
    * Si le champ est en état d'erreur, quel est le message d'erreur?
@@ -93,7 +93,7 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * @see https://vue3datepicker.com/props/positioning/#position
    * @see https://vue3datepicker.com/props/positioning/#position
@@ -101,13 +101,13 @@ const props = defineProps({
   position: {
   position: {
     type: String as PropType<'left' | 'center' | 'right'>,
     type: String as PropType<'left' | 'center' | 'right'>,
     required: false,
     required: false,
-    default: 'center'
-  }
+    default: 'center',
+  },
 })
 })
 
 
 const input = ref(null)
 const input = ref(null)
 
 
-const {fieldViolations, updateViolationState} = useFieldViolation(props.field)
+const { fieldViolations, updateViolationState } = useFieldViolation(props.field)
 
 
 const fieldLabel = props.label ?? props.field
 const fieldLabel = props.label ?? props.field
 
 
@@ -121,6 +121,4 @@ const onUpdate = (event: string) => {
 }
 }
 </script>
 </script>
 
 
-<style scoped>
-
-</style>
+<style scoped></style>

+ 14 - 18
components/Ui/Input/Email.vue

@@ -15,64 +15,60 @@ Champs de saisie de type Text dédié à la saisie d'emails
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {useNuxtApp} from "#app";
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import {useValidationUtils} from "~/composables/utils/useValidationUtils";
+import { useNuxtApp } from '#app'
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import { useValidationUtils } from '~/composables/utils/useValidationUtils'
 
 
 const props = defineProps({
 const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   data: {
   data: {
     type: [String, Number],
     type: [String, Number],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   required: {
   required: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const { emit, i18n } = useNuxtApp()
 const { emit, i18n } = useNuxtApp()
 
 
 const fieldLabel = props.label ?? props.field
 const fieldLabel = props.label ?? props.field
 
 
-const {violation, onChange} = useFieldViolation(props.field, emit)
+const { violation, onChange } = useFieldViolation(props.field, emit)
 
 
 const validationUtils = useValidationUtils()
 const validationUtils = useValidationUtils()
 
 
 const rules = [
 const rules = [
-  (email: string) => validationUtils.validEmail(email) || i18n.t('email_error')
+  (email: string) => validationUtils.validEmail(email) || i18n.t('email_error'),
 ]
 ]
 
 
 if (props.required) {
 if (props.required) {
-  rules.push(
-    (email: string) => !!email || i18n.t('required')
-  )
+  rules.push((email: string) => !!email || i18n.t('required'))
 }
 }
-
 </script>
 </script>

+ 20 - 21
components/Ui/Input/Enum.vue

@@ -6,11 +6,7 @@ Liste déroulante dédiée à l'affichage d'objets Enum
 
 
 <template>
 <template>
   <main>
   <main>
-    <v-skeleton-loader
-      v-if="pending"
-      type="list-item"
-      loading
-    />
+    <v-skeleton-loader v-if="pending" type="list-item" loading />
 
 
     <v-select
     <v-select
       v-else
       v-else
@@ -23,15 +19,20 @@ Liste déroulante dédiée à l'affichage d'objets Enum
       :rules="rules"
       :rules="rules"
       :disabled="readonly"
       :disabled="readonly"
       :error="error || !!fieldViolations"
       :error="error || !!fieldViolations"
-      :error-messages="errorMessage || (fieldViolations ? $t(fieldViolations) : '')"
-      @update:modelValue="updateViolationState($event); $emit('update:modelValue', $event)"
+      :error-messages="
+        errorMessage || (fieldViolations ? $t(fieldViolations) : '')
+      "
+      @update:modelValue="
+        updateViolationState($event)
+        $emit('update:modelValue', $event)
+      "
     />
     />
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import {useEnumFetch} from "~/composables/data/useEnumFetch";
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import { useEnumFetch } from '~/composables/data/useEnumFetch'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -40,14 +41,14 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     String,
     String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Nom de l'Enum utilisée pour peupler la liste
    * Nom de l'Enum utilisée pour peupler la liste
    */
    */
   enum: {
   enum: {
     type: String,
     type: String,
-    required: true
+    required: true,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -57,7 +58,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -66,14 +67,14 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
    */
    */
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -82,14 +83,14 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
    */
    */
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur ?
    * Si le champ est en état d'erreur, quel est le message d'erreur ?
@@ -97,8 +98,8 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 if (typeof props.enum === 'undefined') {
 if (typeof props.enum === 'undefined') {
@@ -117,8 +118,6 @@ const onModelUpdate = (event: any) => {
   emit('change', event)
   emit('change', event)
   emit('update:modelValue', event)
   emit('update:modelValue', event)
 }
 }
-
 </script>
 </script>
 
 
-<style scoped>
-</style>
+<style scoped></style>

+ 147 - 127
components/Ui/Input/Image.vue

@@ -4,16 +4,16 @@ Assistant de création d'image
 @see https://norserium.github.io/vue-advanced-cropper/
 @see https://norserium.github.io/vue-advanced-cropper/
 -->
 -->
 <template>
 <template>
-  <div class="input-image" >
+  <div class="input-image">
     <UiImage
     <UiImage
-        ref="uiImage"
-        :image-id="modelValue"
-        :default-image="defaultImage"
-        :width="width"
-        :height="height"
-        class="image"
-        overlay-icon="fas fa-upload"
-        @overlay-clicked="openModal()"
+      ref="uiImage"
+      :image-id="modelValue"
+      :default-image="defaultImage"
+      :width="width"
+      :height="height"
+      class="image"
+      overlay-icon="fas fa-upload"
+      @overlay-clicked="openModal()"
     />
     />
 
 
     <LazyLayoutDialog :show="showModal">
     <LazyLayoutDialog :show="showModal">
@@ -27,13 +27,11 @@ Assistant de création d'image
             align="center"
             align="center"
             justify="center"
             justify="center"
           >
           >
-            <v-progress-circular
-              :indeterminate="true"
-              color="neutral">
+            <v-progress-circular :indeterminate="true" color="neutral">
             </v-progress-circular>
             </v-progress-circular>
           </v-row>
           </v-row>
 
 
-          <div v-else >
+          <div v-else>
             <div class="upload__cropper-wrapper">
             <div class="upload__cropper-wrapper">
               <Cropper
               <Cropper
                 ref="cropper"
                 ref="cropper"
@@ -46,10 +44,10 @@ Assistant de création d'image
               />
               />
 
 
               <div
               <div
-                  v-if="currentImage.src"
-                  class="upload__reset-button"
-                  title="Reset Image"
-                  @click="reset()"
+                v-if="currentImage.src"
+                class="upload__reset-button"
+                title="Reset Image"
+                @click="reset()"
               >
               >
                 <v-icon>fas fa-trash</v-icon>
                 <v-icon>fas fa-trash</v-icon>
               </div>
               </div>
@@ -57,21 +55,29 @@ Assistant de création d'image
 
 
             <div class="upload__buttons-wrapper">
             <div class="upload__buttons-wrapper">
               <button class="upload__button" @click="fileInput?.click()">
               <button class="upload__button" @click="fileInput?.click()">
-                <input ref="fileInput" type="file" accept="image/*" @change="uploadImage($event)" />
-                {{$t('upload_image')}}
+                <input
+                  ref="fileInput"
+                  type="file"
+                  accept="image/*"
+                  @change="uploadImage($event)"
+                />
+                {{ $t('upload_image') }}
               </button>
               </button>
             </div>
             </div>
 
 
             <span class="max-size-label">{{ $t('max_size_4_mb') }}</span>
             <span class="max-size-label">{{ $t('max_size_4_mb') }}</span>
           </div>
           </div>
-
         </div>
         </div>
       </template>
       </template>
       <template #dialogBtn>
       <template #dialogBtn>
         <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="cancel">
         <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="cancel">
           {{ $t('cancel') }}
           {{ $t('cancel') }}
         </v-btn>
         </v-btn>
-        <v-btn class="submitBtn theme-primary" @click="save" :disabled="pending">
+        <v-btn
+          class="submitBtn theme-primary"
+          @click="save"
+          :disabled="pending"
+        >
           {{ $t('save') }}
           {{ $t('save') }}
         </v-btn>
         </v-btn>
       </template>
       </template>
@@ -80,16 +86,16 @@ Assistant de création d'image
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import { Cropper } from 'vue-advanced-cropper';
-import 'vue-advanced-cropper/dist/style.css';
-import {type Ref, ref} from "@vue/reactivity";
+import { Cropper } from 'vue-advanced-cropper'
+import 'vue-advanced-cropper/dist/style.css'
+import { type Ref, ref } from '@vue/reactivity'
 import File from '~/models/Core/File'
 import File from '~/models/Core/File'
-import type {PropType} from "@vue/runtime-core";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import {useImageManager} from "~/composables/data/useImageManager";
-import {FILE_VISIBILITY, TYPE_ALERT} from "~/types/enum/enums";
-import {usePageStore} from "~/stores/page";
-import ImageUtils from "~/services/utils/imageUtils";
+import type { PropType } from '@vue/runtime-core'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import { useImageManager } from '~/composables/data/useImageManager'
+import { FILE_VISIBILITY, TYPE_ALERT } from '~/types/enum/enums'
+import { usePageStore } from '~/stores/page'
+import ImageUtils from '~/services/utils/imageUtils'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -98,7 +104,7 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: Number as PropType<number | null>,
     type: Number as PropType<number | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -107,36 +113,36 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Image par défaut en cas d'absence d'une image uploadée
    * Image par défaut en cas d'absence d'une image uploadée
    */
    */
   defaultImage: {
   defaultImage: {
     type: String,
     type: String,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Hauteur de l'image à l'écran (en px)
    * Hauteur de l'image à l'écran (en px)
    */
    */
   height: {
   height: {
     type: Number,
     type: Number,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * Largeur de l'image à l'écran (en px)
    * Largeur de l'image à l'écran (en px)
    */
    */
   width: {
   width: {
     type: Number,
     type: Number,
-    required: false
+    required: false,
   },
   },
   /**
   /**
    * TODO: completer
    * TODO: completer
    */
    */
   ownerId: {
   ownerId: {
     type: Number,
     type: Number,
-    required: false
-  }
+    required: false,
+  },
 })
 })
 
 
 const { em } = useEntityManager()
 const { em } = useEntityManager()
@@ -170,13 +176,16 @@ const file: Ref<File | null> = ref(null)
 /**
 /**
  * Données d'une nouvelle image uploadée par l'utilisateur
  * Données d'une nouvelle image uploadée par l'utilisateur
  */
  */
-const currentImage: Ref<
-    {id: string | number | null, src: string | null, content: string | null, name: string | null}
-> = ref({
+const currentImage: Ref<{
+  id: string | number | null
+  src: string | null
+  content: string | null
+  name: string | null
+}> = ref({
   id: null,
   id: null,
   src: null,
   src: null,
   content: null,
   content: null,
-  name: null
+  name: null,
 })
 })
 
 
 /**
 /**
@@ -187,19 +196,24 @@ const MAX_FILE_SIZE = 4 * 1024 * 1024
 /**
 /**
  * Coordonnées du cropper
  * Coordonnées du cropper
  */
  */
-const cropperConfig: Ref<{ left?: number, top?: number, height?: number, width?: number }> = ref({})
+const cropperConfig: Ref<{
+  left?: number
+  top?: number
+  height?: number
+  width?: number
+}> = ref({})
 
 
 /**
 /**
  * @see https://advanced-cropper.github.io/vue-advanced-cropper/components/cropper.html#defaultposition
  * @see https://advanced-cropper.github.io/vue-advanced-cropper/components/cropper.html#defaultposition
  */
  */
 const defaultPosition = () => {
 const defaultPosition = () => {
-  return { left : cropperConfig.value.left, top : cropperConfig.value.top }
+  return { left: cropperConfig.value.left, top: cropperConfig.value.top }
 }
 }
 
 
 /**
 /**
  * @see https://advanced-cropper.github.io/vue-advanced-cropper/components/cropper.html#defaultsize
  * @see https://advanced-cropper.github.io/vue-advanced-cropper/components/cropper.html#defaultsize
  */
  */
-const defaultSize = (params: any): { width: number, height: number } | null => {
+const defaultSize = (params: any): { width: number; height: number } | null => {
   if (!params) {
   if (!params) {
     return null
     return null
   }
   }
@@ -207,7 +221,7 @@ const defaultSize = (params: any): { width: number, height: number } | null => {
 
 
   return {
   return {
     width: cropperConfig.value.width ?? (visibleArea || imageSize).width,
     width: cropperConfig.value.width ?? (visibleArea || imageSize).width,
-    height: cropperConfig.value.height ?? (visibleArea || imageSize).height
+    height: cropperConfig.value.height ?? (visibleArea || imageSize).height,
   }
   }
 }
 }
 
 
@@ -217,7 +231,7 @@ const defaultSize = (params: any): { width: number, height: number } | null => {
  * @param fileId
  * @param fileId
  */
  */
 const loadImage = async (fileId: number) => {
 const loadImage = async (fileId: number) => {
-  file.value = await em.fetch(File, fileId) as File
+  file.value = (await em.fetch(File, fileId)) as File
 
 
   if (file.value.config) {
   if (file.value.config) {
     const fileConfig = JSON.parse(file.value.config)
     const fileConfig = JSON.parse(file.value.config)
@@ -229,7 +243,7 @@ const loadImage = async (fileId: number) => {
 
 
   currentImage.value.name = file.value.name
   currentImage.value.name = file.value.name
   currentImage.value.id = file.value.id
   currentImage.value.id = file.value.id
-  currentImage.value.src = await imageManager.get(fileId) as string
+  currentImage.value.src = (await imageManager.get(fileId)) as string
 }
 }
 
 
 /**
 /**
@@ -242,7 +256,6 @@ const openModal = async () => {
   if (props.modelValue !== null) {
   if (props.modelValue !== null) {
     // Un objet File existe déjà: on le récupère
     // Un objet File existe déjà: on le récupère
     await loadImage(props.modelValue)
     await loadImage(props.modelValue)
-
   } else {
   } else {
     // Nouveau File
     // Nouveau File
     file.value = em.newInstance(File) as File
     file.value = em.newInstance(File) as File
@@ -260,11 +273,17 @@ const reset = () => {
   }
   }
 
 
   currentImage.value = {
   currentImage.value = {
-    src: null, content: null, name: null, id: null
+    src: null,
+    content: null,
+    name: null,
+    id: null,
   }
   }
 
 
   cropperConfig.value = {
   cropperConfig.value = {
-    left: undefined, height: undefined, top: undefined, width: undefined
+    left: undefined,
+    height: undefined,
+    top: undefined,
+    width: undefined,
   }
   }
 }
 }
 
 
@@ -282,7 +301,10 @@ const uploadImage = async (event: any) => {
   const uploadedFile = files[0]
   const uploadedFile = files[0]
 
 
   if (uploadedFile.size > MAX_FILE_SIZE) {
   if (uploadedFile.size > MAX_FILE_SIZE) {
-    pageStore.alerts.push({type: TYPE_ALERT.ALERT, messages: ['file_too_large'] })
+    pageStore.alerts.push({
+      type: TYPE_ALERT.ALERT,
+      messages: ['file_too_large'],
+    })
     return
     return
   }
   }
 
 
@@ -303,8 +325,8 @@ const uploadImage = async (event: any) => {
  * Lorsque le cropper change de position / taille, on met à jour les coordonnées
  * Lorsque le cropper change de position / taille, on met à jour les coordonnées
  * @param newCoordinates
  * @param newCoordinates
  */
  */
-const onCropperChange = ({ coordinates: newCoordinates } : any) => {
-  cropperConfig.value = newCoordinates;
+const onCropperChange = ({ coordinates: newCoordinates }: any) => {
+  cropperConfig.value = newCoordinates
 }
 }
 
 
 /**
 /**
@@ -333,15 +355,15 @@ const saveNewImage = async (): Promise<number> => {
     x: cropperConfig.value.left,
     x: cropperConfig.value.left,
     y: cropperConfig.value.top,
     y: cropperConfig.value.top,
     height: cropperConfig.value.height,
     height: cropperConfig.value.height,
-    width: cropperConfig.value.width
+    width: cropperConfig.value.width,
   })
   })
 
 
-  const response = await imageManager.upload(
-      currentImage.value.name,
-      currentImage.value.content,
-      FILE_VISIBILITY.EVERYBODY,
-      config
-  ) as any
+  const response = (await imageManager.upload(
+    currentImage.value.name,
+    currentImage.value.content,
+    FILE_VISIBILITY.EVERYBODY,
+    config,
+  )) as any
 
 
   return response.fileId
   return response.fileId
 }
 }
@@ -358,7 +380,7 @@ const saveExistingImage = async () => {
     x: cropperConfig.value.left,
     x: cropperConfig.value.left,
     y: cropperConfig.value.top,
     y: cropperConfig.value.top,
     height: cropperConfig.value.height,
     height: cropperConfig.value.height,
-    width: cropperConfig.value.width
+    width: cropperConfig.value.width,
   })
   })
 
 
   await em.persist(File, file.value)
   await em.persist(File, file.value)
@@ -375,12 +397,10 @@ const save = async () => {
     // Une nouvelle image a été uploadée
     // Une nouvelle image a été uploadée
     const fileId = await saveNewImage()
     const fileId = await saveNewImage()
     emit('update:modelValue', fileId)
     emit('update:modelValue', fileId)
-
   } else if (currentImage.value.id) {
   } else if (currentImage.value.id) {
     // L'image existante a été modifiée
     // L'image existante a été modifiée
     await saveExistingImage()
     await saveExistingImage()
     uiImage.value.refresh()
     uiImage.value.refresh()
-
   } else {
   } else {
     // On a reset l'image
     // On a reset l'image
     emit('update:modelValue', null)
     emit('update:modelValue', null)
@@ -401,74 +421,74 @@ onUnmounted(() => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  :deep(.vue-advanced-cropper__stretcher) {
-    height: auto !important;
-    width: auto !important;
-  }
+:deep(.vue-advanced-cropper__stretcher) {
+  height: auto !important;
+  width: auto !important;
+}
 
 
-  .loading{
-    height: 300px;
-  }
+.loading {
+  height: 300px;
+}
 
 
-  .upload {
-    user-select: none;
-    padding: 20px;
-    display: block;
-    &__cropper {
-       border: solid 1px rgb(var(--v-theme-on-neutral-strong));;
-       min-height: 300px;
-       max-height: 300px;
-     }
-    &__cropper-wrapper {
-       position: relative;
-     }
-    &__reset-button {
-      position: absolute;
-      right: 20px;
-      bottom: 20px;
-      cursor: pointer;
-      display: flex;
-      align-items: center;
-      justify-content: center;
-      height: 42px;
-      width: 42px;
-      background: rgb(var(--v-theme-neutral));
-      transition: background 0.5s;
-      &:hover {
-        background: rgb(var(--v-theme-primary-alt));
-      }
+.upload {
+  user-select: none;
+  padding: 20px;
+  display: block;
+  &__cropper {
+    border: solid 1px rgb(var(--v-theme-on-neutral-strong));
+    min-height: 300px;
+    max-height: 300px;
+  }
+  &__cropper-wrapper {
+    position: relative;
+  }
+  &__reset-button {
+    position: absolute;
+    right: 20px;
+    bottom: 20px;
+    cursor: pointer;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 42px;
+    width: 42px;
+    background: rgb(var(--v-theme-neutral));
+    transition: background 0.5s;
+    &:hover {
+      background: rgb(var(--v-theme-primary-alt));
     }
     }
-    &__buttons-wrapper {
-       display: flex;
-       justify-content: center;
-       margin-top: 17px;
-     }
-    &__button {
-       border: none;
-       outline: solid transparent;
-       color: rgb(var(--v-theme-on-neutral));
-       font-size: 16px;
-       padding: 10px 20px;
-       background: rgb(var(--v-theme-neutral));
-       cursor: pointer;
-       transition: background 0.5s;
-       margin: 0 16px;
-      &:hover,
-      &:focus {
-         background: rgb(var(--v-theme-primary-alt));
-       }
-      input {
-        display: none;
-      }
+  }
+  &__buttons-wrapper {
+    display: flex;
+    justify-content: center;
+    margin-top: 17px;
+  }
+  &__button {
+    border: none;
+    outline: solid transparent;
+    color: rgb(var(--v-theme-on-neutral));
+    font-size: 16px;
+    padding: 10px 20px;
+    background: rgb(var(--v-theme-neutral));
+    cursor: pointer;
+    transition: background 0.5s;
+    margin: 0 16px;
+    &:hover,
+    &:focus {
+      background: rgb(var(--v-theme-primary-alt));
+    }
+    input {
+      display: none;
     }
     }
   }
   }
+}
 
 
-  .max-size-label {
-    display: block;
-    width: 100%;
-    text-align: center;
-    font-size: 13px;
-    color: rgb(var(--v-theme-on-neutral-soft));
-    margin-top: 6px;
-  }
+.max-size-label {
+  display: block;
+  width: 100%;
+  text-align: center;
+  font-size: 13px;
+  color: rgb(var(--v-theme-on-neutral-soft));
+  margin-top: 6px;
+}
 </style>
 </style>

+ 34 - 21
components/Ui/Input/Number.vue

@@ -4,26 +4,25 @@ An input for numeric values
 
 
 <template>
 <template>
   <v-text-field
   <v-text-field
-      ref="input"
-      :modelValue.number="modelValue"
-      :label="(label || field) ? $t(label ?? field) : undefined"
-      hide-details
-      :density="density"
-      type="number"
-      :variant="variant"
-      @update:modelValue="modelValue = keepInRange(cast($event)); emitUpdate()"
+    ref="input"
+    :modelValue.number="modelValue"
+    :label="label || field ? $t(label ?? field) : undefined"
+    hide-details
+    :density="density"
+    type="number"
+    :variant="variant"
+    @update:modelValue="onModelUpdate($event)"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
+import type { PropType } from 'vue'
 
 
-import type {PropType} from "@vue/runtime-core";
-
-type Density = null | 'default' | 'comfortable' | 'compact';
+type Density = null | 'default' | 'comfortable' | 'compact'
 
 
 const props = defineProps({
 const props = defineProps({
   modelValue: {
   modelValue: {
-    type: Number
+    type: Number,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -33,7 +32,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -42,36 +41,45 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   default: {
   default: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: 0
+    default: 0,
   },
   },
   min: {
   min: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   max: {
   max: {
     type: Number,
     type: Number,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   density: {
   density: {
     type: String as PropType<Density>,
     type: String as PropType<Density>,
     required: false,
     required: false,
-    default: 'default'
+    default: 'default',
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    */
    */
   variant: {
   variant: {
-    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    type: String as PropType<
+      | 'filled'
+      | 'outlined'
+      | 'plain'
+      | 'underlined'
+      | 'solo'
+      | 'solo-inverted'
+      | 'solo-filled'
+      | undefined
+    >,
     required: false,
     required: false,
-    default: 'filled'
-  }
+    default: 'filled',
+  },
 })
 })
 
 
 /**
 /**
@@ -109,6 +117,11 @@ const keepInRange = (val: number) => {
   return val
   return val
 }
 }
 
 
+const onModelUpdate = (event: string) => {
+  // eslint-disable-next-line vue/no-mutating-props
+  props.modelValue = keepInRange(cast(event))
+  emitUpdate()
+}
 
 
 const emit = defineEmits(['update:modelValue'])
 const emit = defineEmits(['update:modelValue'])
 
 

+ 28 - 24
components/Ui/Input/Phone.vue

@@ -26,40 +26,39 @@ Champs de saisie d'un numéro de téléphone
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {useNuxtApp} from "#app";
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import type {Ref} from "@vue/reactivity";
+import { useNuxtApp } from '#app'
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import type { Ref } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: ''
+    default: '',
   },
   },
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   data: {
   data: {
     type: [String, Number],
     type: [String, Number],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const { emit, i18n } = useNuxtApp()
 const { emit, i18n } = useNuxtApp()
@@ -71,7 +70,14 @@ const internationalNumber: Ref<string | number> = ref('')
 const isValid: Ref<boolean> = ref(false)
 const isValid: Ref<boolean> = ref(false)
 const onInit: Ref<boolean> = ref(true)
 const onInit: Ref<boolean> = ref(true)
 
 
-const onInput = (_: any, { number, valid, countryChoice }: { number: any, valid: boolean, countryChoice: any }) => {
+const onInput = (
+  _: any,
+  {
+    number,
+    valid,
+    countryChoice,
+  }: { number: any; valid: boolean; countryChoice: any },
+) => {
   isValid.value = valid
   isValid.value = valid
   nationalNumber.value = number.national
   nationalNumber.value = number.national
   internationalNumber.value = number.international
   internationalNumber.value = number.international
@@ -84,24 +90,22 @@ const onChangeValue = () => {
   }
   }
 }
 }
 
 
-const myPhone = computed(
-  {
-    get:()=>{
-      return onInit.value ? props.data : nationalNumber.value
-    },
-    set:(value)=>{
-      return props.data
-    }
-  }
-)
+const myPhone = computed({
+  get: () => {
+    return onInit.value ? props.data : nationalNumber.value
+  },
+  set: (value) => {
+    return props.data
+  },
+})
 
 
 const rules = [
 const rules = [
-  (phone: string) => (!phone || isValid.value) || i18n.t('phone_error')
+  (phone: string) => !phone || isValid.value || i18n.t('phone_error'),
 ]
 ]
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
-input:read-only{
+input:read-only {
   color: rgb(var(--v-theme-on-neutral));
   color: rgb(var(--v-theme-on-neutral));
 }
 }
 </style>
 </style>

+ 33 - 23
components/Ui/Input/Text.vue

@@ -8,12 +8,14 @@ Champs de saisie de texte, à placer dans un composant `UiForm`
   <v-text-field
   <v-text-field
     ref="input"
     ref="input"
     :model-value="modelValue"
     :model-value="modelValue"
-    :label="(label || field) ? $t(label ?? field) : undefined"
+    :label="label || field ? $t(label ?? field) : undefined"
     :rules="rules"
     :rules="rules"
     :disabled="readonly"
     :disabled="readonly"
-    :type="(type === 'password' && show) ? 'text' : type"
+    :type="type === 'password' && show ? 'text' : type"
     :error="error || !!fieldViolations"
     :error="error || !!fieldViolations"
-    :error-messages="errorMessage || (fieldViolations ? $t(fieldViolations) : '')"
+    :error-messages="
+      errorMessage || (fieldViolations ? $t(fieldViolations) : '')
+    "
     :append-icon="type === 'password' ? (show ? 'mdi-eye' : 'mdi-eye-off') : ''"
     :append-icon="type === 'password' ? (show ? 'mdi-eye' : 'mdi-eye-off') : ''"
     :variant="variant"
     :variant="variant"
     @click:append="show = !show"
     @click:append="show = !show"
@@ -21,14 +23,13 @@ Champs de saisie de texte, à placer dans un composant `UiForm`
     @change="onChange($event)"
     @change="onChange($event)"
   />
   />
 
 
-
-<!--  v-cleave="mask"-->
+  <!--  v-cleave="mask"-->
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {type Ref, ref} from "@vue/reactivity";
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
-import type {PropType} from "@vue/runtime-core";
+import { type Ref, ref } from '@vue/reactivity'
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -37,7 +38,7 @@ const props = defineProps({
   modelValue: {
   modelValue: {
     type: [String, Number] as PropType<string | number | null>,
     type: [String, Number] as PropType<string | number | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
    * Nom de la propriété d'une entité lorsque l'input concerne cette propriété
@@ -47,7 +48,7 @@ const props = defineProps({
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Label du champ
    * Label du champ
@@ -56,7 +57,7 @@ const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Type d'input HTML
    * Type d'input HTML
@@ -65,7 +66,7 @@ const props = defineProps({
   type: {
   type: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Définit si le champ est en lecture seule
    * Définit si le champ est en lecture seule
@@ -73,7 +74,7 @@ const props = defineProps({
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Règles de validation
    * Règles de validation
@@ -82,7 +83,7 @@ const props = defineProps({
   rules: {
   rules: {
     type: Array as PropType<any[]>,
     type: Array as PropType<any[]>,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   /**
   /**
    * Le champ est-il actuellement en état d'erreur
    * Le champ est-il actuellement en état d'erreur
@@ -90,7 +91,7 @@ const props = defineProps({
   error: {
   error: {
     type: Boolean,
     type: Boolean,
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * Si le champ est en état d'erreur, quel est le message d'erreur?
    * Si le champ est en état d'erreur, quel est le message d'erreur?
@@ -98,7 +99,7 @@ const props = defineProps({
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * Masque de saisie
    * Masque de saisie
@@ -107,16 +108,25 @@ const props = defineProps({
   mask: {
   mask: {
     type: [Array, Boolean],
     type: [Array, Boolean],
     required: false,
     required: false,
-    default: false
+    default: false,
   },
   },
   /**
   /**
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
    */
    */
   variant: {
   variant: {
-    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    type: String as PropType<
+      | 'filled'
+      | 'outlined'
+      | 'plain'
+      | 'underlined'
+      | 'solo'
+      | 'solo-inverted'
+      | 'solo-filled'
+      | undefined
+    >,
     required: false,
     required: false,
-    default: 'filled'
-  }
+    default: 'filled',
+  },
 })
 })
 
 
 const input = ref(null)
 const input = ref(null)
@@ -138,7 +148,7 @@ const onChange = (event: Event | undefined) => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  input:read-only{
-    color: rgb(var(--v-theme-neutral));
-  }
+input:read-only {
+  color: rgb(var(--v-theme-neutral));
+}
 </style>
 </style>

+ 23 - 25
components/Ui/Input/TextArea.vue

@@ -6,68 +6,66 @@ Champs de saisie de bloc texte
 
 
 <template>
 <template>
   <v-textarea
   <v-textarea
-      outlined
-      :value="data"
-      :label="$t(fieldLabel)"
-      :rules="rules"
-      :disabled="readonly"
-      :error="error || !!violation"
-      :error-messages="errorMessage || violation ? $t(violation) : ''"
-      @change="onChange($event)"
-    />
+    outlined
+    :value="data"
+    :label="$t(fieldLabel)"
+    :rules="rules"
+    :disabled="readonly"
+    :error="error || !!violation"
+    :error-messages="errorMessage || violation ? $t(violation) : ''"
+    @change="onChange($event)"
+  />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {useNuxtApp} from "#app";
-import {useFieldViolation} from "~/composables/form/useFieldViolation";
+import { useNuxtApp } from '#app'
+import { useFieldViolation } from '~/composables/form/useFieldViolation'
 
 
 const props = defineProps({
 const props = defineProps({
   label: {
   label: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   field: {
   field: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   data: {
   data: {
     type: [String, Number],
     type: [String, Number],
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   readonly: {
   readonly: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   rules: {
   rules: {
     type: Array,
     type: Array,
     required: false,
     required: false,
-    default: () => []
+    default: () => [],
   },
   },
   error: {
   error: {
     type: Boolean,
     type: Boolean,
-    required: false
+    required: false,
   },
   },
   errorMessage: {
   errorMessage: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const { emit } = useNuxtApp()
 const { emit } = useNuxtApp()
 
 
 const fieldLabel = props.label ?? props.field
 const fieldLabel = props.label ?? props.field
 
 
-const {violation, onChange} = useFieldViolation(props.field, emit)
-
+const { violation, onChange } = useFieldViolation(props.field, emit)
 </script>
 </script>
 
 
 <style lang="scss">
 <style lang="scss">
-  input:read-only{
-    color: rgb(var(--v-theme-on-neutral));
-  }
+input:read-only {
+  color: rgb(var(--v-theme-on-neutral));
+}
 </style>
 </style>

+ 14 - 17
components/Ui/ItemFromUri.vue

@@ -3,12 +3,9 @@ Espace permettant de récupérer un item via une uri et de gérer son affichage
 -->
 -->
 <template>
 <template>
   <main>
   <main>
-    <v-skeleton-loader
-      v-if="pending"
-      :type="loaderType"
-    />
+    <v-skeleton-loader v-if="pending" :type="loaderType" />
     <div v-else>
     <div v-else>
-      <slot name="item.text" v-bind="{item}" />
+      <slot name="item.text" v-bind="{ item }" />
     </div>
     </div>
     <slot />
     <slot />
   </main>
   </main>
@@ -17,32 +14,32 @@ Espace permettant de récupérer un item via une uri et de gérer son affichage
 <script setup lang="ts">
 <script setup lang="ts">
 // TODO: renommer en EntityFromUri? voir si ce component est encore nécessaire, ou si ça ne peut pas être une méthode de l'entity manager
 // TODO: renommer en EntityFromUri? voir si ce component est encore nécessaire, ou si ça ne peut pas être une méthode de l'entity manager
 
 
-import {Query} from "pinia-orm";
-import UrlUtils from "~/services/utils/urlUtils";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {computed} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
-import ApiResource from "~/models/ApiResource";
+import { Query } from 'pinia-orm'
+import UrlUtils from '~/services/utils/urlUtils'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { computed } from '@vue/reactivity'
+import type { ComputedRef } from '@vue/reactivity'
+import ApiResource from '~/models/ApiResource'
 
 
 const props = defineProps({
 const props = defineProps({
   uri: {
   uri: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   model: {
   model: {
     type: Object,
     type: Object,
-    required: true
+    required: true,
   },
   },
   query: {
   query: {
     type: Object as () => Query,
     type: Object as () => Query,
-    required: true
+    required: true,
   },
   },
   loaderType: {
   loaderType: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: 'text'
-  }
+    default: 'text',
+  },
 })
 })
 
 
 const id = UrlUtils.extractIdFromUri(props.uri)
 const id = UrlUtils.extractIdFromUri(props.uri)
@@ -54,7 +51,7 @@ const { fetch } = useEntityFetch()
 
 
 const { data, pending } = fetch(props.model, id)
 const { data, pending } = fetch(props.model, id)
 
 
-const item: ComputedRef<ApiResource|null> = computed(() => {
+const item: ComputedRef<ApiResource | null> = computed(() => {
   return data.value
   return data.value
 })
 })
 </script>
 </script>

+ 4 - 14
components/Ui/LoadingPanel.vue

@@ -1,19 +1,9 @@
 <template>
 <template>
-  <v-row
-      class="fill-height ma-0"
-      align="center"
-      justify="center"
-  >
-    <v-progress-circular
-        :indeterminate="true"
-        color="neutral"
-    />
+  <v-row class="fill-height ma-0" align="center" justify="center">
+    <v-progress-circular :indeterminate="true" color="neutral" />
   </v-row>
   </v-row>
 </template>
 </template>
 
 
-<script setup lang="ts">
-</script>
+<script setup lang="ts"></script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 38 - 36
components/Ui/SystemBar.vue

@@ -4,9 +4,11 @@ System bars
 
 
 <template>
 <template>
   <v-system-bar
   <v-system-bar
-      height="50"
-      :class="'d-flex flex-row justify-center align-center text-center ' + classes"
-      @click="onClick !== undefined ? onClick() : null"
+    height="50"
+    :class="
+      'd-flex flex-row justify-center align-center text-center ' + classes
+    "
+    @click="onClick !== undefined ? onClick() : null"
   >
   >
     <slot>
     <slot>
       <v-icon v-if="icon" small :icon="icon" />
       <v-icon v-if="icon" small :icon="icon" />
@@ -16,40 +18,40 @@ System bars
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-  const props = defineProps({
-    text: {
-      type: String,
-      required: false,
-      default: ''
-    },
-    icon: {
-      type: String,
-      required: false,
-      default: undefined
-    },
-    backgroundColor: {
-      type: String,
-      required: false,
-      default: 'neutral-soft'
-    },
-    textColor: {
-      type: String,
-      required: false,
-      default: 'on-neutral-soft'
-    },
-    onClick: {
-      type: Function,
-      required: false,
-      default: undefined
-    },
-  })
+const props = defineProps({
+  text: {
+    type: String,
+    required: false,
+    default: '',
+  },
+  icon: {
+    type: String,
+    required: false,
+    default: undefined,
+  },
+  backgroundColor: {
+    type: String,
+    required: false,
+    default: 'neutral-soft',
+  },
+  textColor: {
+    type: String,
+    required: false,
+    default: 'on-neutral-soft',
+  },
+  onClick: {
+    type: Function,
+    required: false,
+    default: undefined,
+  },
+})
 
 
-  // TODO: voir si possible d'utiliser les variables sass à la place?
-  const classes = [
-    'bg-' + props.backgroundColor,
-    'text-' + props.textColor,
-    (props.onClick !== undefined ? 'clickable' : '')
-  ].join(' ')
+// TODO: voir si possible d'utiliser les variables sass à la place?
+const classes = [
+  'bg-' + props.backgroundColor,
+  'text-' + props.textColor,
+  props.onClick !== undefined ? 'clickable' : '',
+].join(' ')
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 6 - 15
components/Ui/Template/DataTable.vue

@@ -5,42 +5,33 @@ Template de base d'un tableau interactif
 -->
 -->
 
 
 <template>
 <template>
-  <v-col
-    cols="12"
-    sm="12"
-  >
-    <v-data-table
-      :headers="headersWithItem"
-      :items="items"
-      class="elevation-1"
-    >
+  <v-col cols="12" sm="12">
+    <v-data-table :headers="headersWithItem" :items="items" class="elevation-1">
       <template v-for="header in headersWithItem" #[header.item]="props">
       <template v-for="header in headersWithItem" #[header.item]="props">
         <slot :name="header.item" v-bind="props">
         <slot :name="header.item" v-bind="props">
           {{ props.item[header.value] }}
           {{ props.item[header.value] }}
         </slot>
         </slot>
       </template>
       </template>
-
     </v-data-table>
     </v-data-table>
   </v-col>
   </v-col>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
 const props = defineProps({
 const props = defineProps({
   items: {
   items: {
     type: Array,
     type: Array,
-    required: true
+    required: true,
   },
   },
   headers: {
   headers: {
     type: Array,
     type: Array,
-    required: true
-  }
+    required: true,
+  },
 })
 })
 
 
 const { headers } = toRefs(props)
 const { headers } = toRefs(props)
 
 
 const headersWithItem = computed(() => {
 const headersWithItem = computed(() => {
-  return headers.value.map((header:any) => {
+  return headers.value.map((header: any) => {
     header.item = 'item.' + header.value
     header.item = 'item.' + header.value
     return header
     return header
   })
   })

+ 5 - 5
components/Ui/Template/Date.vue

@@ -7,16 +7,16 @@ Date formatée
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import DateUtils from "~/services/utils/dateUtils";
-import {computed} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
+import DateUtils from '~/services/utils/dateUtils'
+import { computed } from '@vue/reactivity'
+import type { ComputedRef } from '@vue/reactivity'
 
 
 const props = defineProps({
 const props = defineProps({
   data: {
   data: {
     type: [String, Array],
     type: [String, Array],
     required: false,
     required: false,
-    default: null
-  }
+    default: null,
+  },
 })
 })
 
 
 const datesFormatted: ComputedRef<string> = computed(() => {
 const datesFormatted: ComputedRef<string> = computed(() => {

+ 39 - 44
components/Ui/Xeditable/Text.vue

@@ -8,69 +8,64 @@ Utilisé par exemple pour le choix de l'année active
   <main>
   <main>
     <!-- Mode édition activé -->
     <!-- Mode édition activé -->
     <div v-if="edit" class="d-flex align-center x-editable-input">
     <div v-if="edit" class="d-flex align-center x-editable-input">
-      <UiInputText
-          class="ma-0 pa-0"
-          :type="type"
-          v-model="inputValue"
-      />
+      <UiInputText class="ma-0 pa-0" :type="type" v-model="inputValue" />
 
 
       <v-icon
       <v-icon
-          icon="fas fa-check"
-          aria-hidden="false"
-          class="valid icons text-primary"
-          size="small"
-          @click="update"
+        icon="fas fa-check"
+        aria-hidden="false"
+        class="valid icons text-primary"
+        size="small"
+        @click="update"
       />
       />
       <v-icon
       <v-icon
-          icon="fas fa-times"
-          aria-hidden="false"
-          class="cancel icons text-neutral-strong"
-          size="small"
-          @click="close"
+        icon="fas fa-times"
+        aria-hidden="false"
+        class="cancel icons text-neutral-strong"
+        size="small"
+        @click="close"
       />
       />
     </div>
     </div>
 
 
     <!-- Mode édition désactivé -->
     <!-- Mode édition désactivé -->
-    <div v-else class="edit-link d-flex align-center" @click="edit=true">
-      <slot name="xeditable.read" v-bind="{inputValue}" />
+    <div v-else class="edit-link d-flex align-center" @click="edit = true">
+      <slot name="xeditable.read" v-bind="{ inputValue }" />
     </div>
     </div>
   </main>
   </main>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {ref} from "@vue/reactivity";
-import type {Ref} from "@vue/reactivity";
-
-  const props = defineProps({
-    type: {
-      type: String,
-      required: false,
-      default: null
-    },
-    data: {
-      type: [String, Number],
-      required: false,
-      default: null
-    }
-  })
+import { ref } from '@vue/reactivity'
+import type { Ref } from '@vue/reactivity'
 
 
-  const emit = defineEmits(['update'])
+const props = defineProps({
+  type: {
+    type: String,
+    required: false,
+    default: null,
+  },
+  data: {
+    type: [String, Number],
+    required: false,
+    default: null,
+  },
+})
 
 
-  const edit: Ref<boolean> = ref(false)
-  const inputValue: Ref<string|number|null> = ref(props.data)
+const emit = defineEmits(['update'])
 
 
-  const update = () => {
-    edit.value = false
-    if (inputValue.value !== props.data) {
-        emit('update', inputValue.value)
-    }
-  }
+const edit: Ref<boolean> = ref(false)
+const inputValue: Ref<string | number | null> = ref(props.data)
 
 
-  const close = () => {
-    edit.value = false
-    inputValue.value = props.data
+const update = () => {
+  edit.value = false
+  if (inputValue.value !== props.data) {
+    emit('update', inputValue.value)
   }
   }
+}
 
 
+const close = () => {
+  edit.value = false
+  inputValue.value = props.data
+}
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">

+ 2 - 3
config/abilities/config.yaml

@@ -1,3 +1,2 @@
-abilities:
-  !!import/shallow
-    - pages/
+abilities: !!import/shallow
+  - pages/

+ 52 - 31
config/abilities/pages/addressBook.yaml

@@ -1,36 +1,57 @@
-  accesses_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Users']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'users'}]}
+accesses_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Users'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'users' }],
+      }
 
 
-  student_registration_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['UsersSchool']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'student-registration'}]}
+student_registration_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['UsersSchool'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'student-registration' }],
+      }
 
 
-  education_student_next_year_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'educationstudent'}]}
+education_student_next_year_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'educationstudent' }],
+      }
 
 
-  commissions_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Users']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'commissions'}]}
+commissions_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Users'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'commissions' }],
+      }
 
 
-  network_children_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Network']}
-      - {function: organizationHasChildren}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'network'}]}
+network_children_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Network'] }
+    - { function: organizationHasChildren }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'network' }],
+      }
 
 
-  network_parents_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['NetworkOrganization']}
-      - {function: organizationHasChildren, expectedResult: false}
+network_parents_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['NetworkOrganization'],
+      }
+    - { function: organizationHasChildren, expectedResult: false }

+ 40 - 31
config/abilities/pages/admin2ios.yaml

@@ -1,37 +1,46 @@
-  all_accesses_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'user'}]}
+all_accesses_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'user' }],
+      }
 
 
-  all_organizations_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'organization'}]}
+all_organizations_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'organization' }],
+      }
 
 
-  tips_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CorePremium']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'tips'}]}
+tips_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CorePremium'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'tips' }],
+      }
 
 
-  dgv_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
+dgv_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }
 
 
-  cmf_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
+cmf_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }
 
 
-  right_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
+right_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }
 
 
-  tree_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Admin2IOS']}
+tree_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Admin2IOS'] }

+ 88 - 46
config/abilities/pages/billing.yaml

@@ -1,53 +1,95 @@
-  billing_product_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+billing_product_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'billings-administration' }],
+      }
 
 
-  billing_products_by_student_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+billing_products_by_student_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  billing_edition_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+billing_edition_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'billings-administration' }],
+      }
 
 
-  billing_accounting_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+billing_accounting_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'billings-administration' }],
+      }
 
 
-  billing_payment_list_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
+billing_payment_list_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'billings-administration' }],
+      }
 
 
-  pes_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Pes']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+pes_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Pes'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'billings-administration' }],
+      }
 
 
-  berger_levrault_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BergerLevrault']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+berger_levrault_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['BergerLevrault'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'billings-administration' }],
+      }
 
 
-  jvs_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Jvs']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
-      -
-  afi_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['AFI']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'billings-administration'}]}
+jvs_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Jvs'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'billings-administration' }],
+      }
+    -
+afi_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['AFI'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'billings-administration' }],
+      }

+ 27 - 27
config/abilities/pages/communication.yaml

@@ -1,29 +1,29 @@
-  inbox_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['MessagesAdvanced']}
-      - function: accessHasAnyRoleAbility
-        parameters:
-          - {action: 'read', subject: 'mails'}
-          - {action: 'read', subject: 'emails'}
-          - {action: 'read', subject: 'texto'}
+inbox_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['MessagesAdvanced'] }
+    - function: accessHasAnyRoleAbility
+      parameters:
+        - { action: 'read', subject: 'mails' }
+        - { action: 'read', subject: 'emails' }
+        - { action: 'read', subject: 'texto' }
 
 
-  message_send_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['MessagesAdvanced']}
-      - function: accessHasAnyRoleAbility
-        parameters:
-          - {action: 'read', subject: 'mails'}
-          - {action: 'read', subject: 'emails'}
-          - {action: 'read', subject: 'texto'}
+message_send_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['MessagesAdvanced'] }
+    - function: accessHasAnyRoleAbility
+      parameters:
+        - { action: 'read', subject: 'mails' }
+        - { action: 'read', subject: 'emails' }
+        - { action: 'read', subject: 'texto' }
 
 
-  message_templates_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['MessagesAdvanced']}
-      - function: accessHasAnyRoleAbility
-        parameters:
-          - {action: 'read', subject: 'mails'}
-          - {action: 'read', subject: 'emails'}
-          - {action: 'read', subject: 'texto'}
+message_templates_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['MessagesAdvanced'] }
+    - function: accessHasAnyRoleAbility
+      parameters:
+        - { action: 'read', subject: 'mails' }
+        - { action: 'read', subject: 'emails' }
+        - { action: 'read', subject: 'texto' }

+ 176 - 101
config/abilities/pages/cotisations.yaml

@@ -1,101 +1,176 @@
-  rate_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationRate', 'CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  parameters_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  send_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  state_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  pay_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  check_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  ledger_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  magazine_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCMFAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  ventilated_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  pay_erase_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  resume_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationTransmissionState']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  history_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationCall']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  call_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationStructure']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  history_structure_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationStructure']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  insurance_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationStructure', 'CotisationTransmissionState']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  resume_all_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationTransmission']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
-
-  resume_pay_cotisation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['CotisationTransmission']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'cotisation'}]}
+rate_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationRate', 'CotisationCall'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+parameters_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+send_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+state_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+pay_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+check_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+ledger_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+magazine_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationCMFAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+ventilated_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+pay_erase_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+resume_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationTransmissionState'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+history_cotisation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['CotisationCall'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+call_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationStructure'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+history_structure_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationStructure'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+insurance_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationStructure', 'CotisationTransmissionState'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+resume_all_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationTransmission'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }
+
+resume_pay_cotisation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['CotisationTransmission'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'cotisation' }],
+      }

+ 8 - 5
config/abilities/pages/donor.yaml

@@ -1,5 +1,8 @@
-  donors_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Donors']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'donors'}]}
+donors_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Donors'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'donors' }],
+      }

+ 68 - 35
config/abilities/pages/educational.yaml

@@ -1,41 +1,74 @@
-  criteria_notations_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+criteria_notations_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  education_notation_config_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['AdvancedEducationNotation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+education_notation_config_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['AdvancedEducationNotation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  seizure_period_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PeriodValidation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+seizure_period_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['PeriodValidation'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  test_seizure_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsSeizure']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-seizure'}]}
+test_seizure_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['PedagogicsSeizure'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-seizure' }],
+      }
 
 
-  test_validation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+test_validation_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  examen_results_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
+examen_results_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
 
 
-  education_by_student_validation_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsSeizure']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-seizure'}]}
+education_by_student_validation_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['PedagogicsSeizure'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-seizure' }],
+      }

+ 8 - 5
config/abilities/pages/equipment.yaml

@@ -1,5 +1,8 @@
-  equipment_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Equipments']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'equipments'}]}
+equipment_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Equipments'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'equipments' }],
+      }

+ 8 - 5
config/abilities/pages/medals.yaml

@@ -1,5 +1,8 @@
-  medals_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Medals']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'medals'}]}
+medals_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Medals'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'medals' }],
+      }

+ 87 - 68
config/abilities/pages/myAccount.yaml

@@ -1,82 +1,101 @@
-  my_schedule_page:
-    action: 'display'
-    conditions:
-      - { function: accessIsAdminAccount, expectedResult: false }
+my_schedule_page:
+  action: 'display'
+  conditions:
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  attendance_bookings_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['Attendances'] }
-      # TODO: l'action write existe-t-elle?
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'write', subject: 'attendances'}] }
-      - { function: accessIsAdminAccount, expectedResult: false }
+attendance_bookings_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Attendances'] }
+    # TODO: l'action write existe-t-elle?
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'write', subject: 'attendances' }],
+      }
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  my_attendance_page:
-    action: 'display'
-    conditions:
-      - { function: accessIsAdminAccount, expectedResult: false }
+my_attendance_page:
+  action: 'display'
+  conditions:
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  my_invitation_page:
-    action: 'display'
-    conditions:
-      - { function: accessIsAdminAccount, expectedResult: false }
+my_invitation_page:
+  action: 'display'
+  conditions:
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  my_students_page:
-    action: 'display'
-    conditions:
-      - { function: accessHasAnyProfile, parameters: [ 'teacher'] }
+my_students_page:
+  action: 'display'
+  conditions:
+    - { function: accessHasAnyProfile, parameters: ['teacher'] }
 
 
-  my_students_education_students_page:
-    action: 'display'
-    conditions:
-      - { function: accessHasAnyProfile, parameters: [ 'teacher'] }
+my_students_education_students_page:
+  action: 'display'
+  conditions:
+    - { function: accessHasAnyProfile, parameters: ['teacher'] }
 
 
-  criteria_notations_page_from_account_menu:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'criterianotation'}]}
+criteria_notations_page_from_account_menu:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'criterianotation' }],
+      }
 
 
-  my_education_students_page:
-    action: 'display'
-    conditions:
-      - { function: accessHasAnyProfile, parameters: [ 'student'] }
+my_education_students_page:
+  action: 'display'
+  conditions:
+    - { function: accessHasAnyProfile, parameters: ['student'] }
 
 
-  send_an_email_page:
-    action: 'display'
-    conditions:
-      - { function: accessHasAnyProfile, parameters: [ 'admin', 'teacher' ] }
+send_an_email_page:
+  action: 'display'
+  conditions:
+    - { function: accessHasAnyProfile, parameters: ['admin', 'teacher'] }
 
 
-  my_documents_page:
-    action: 'display'
-    conditions:
-      - { function: accessIsAdminAccount, expectedResult: false }
+my_documents_page:
+  action: 'display'
+  conditions:
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  my_profile_page:
-    action: 'display'
-    conditions:
-      - { function: accessIsAdminAccount, expectedResult: false }
+my_profile_page:
+  action: 'display'
+  conditions:
+    - { function: accessIsAdminAccount, expectedResult: false }
 
 
-  adherent_list_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['Users'] }
-      - { function: organizationIsShowAdherentList }
-      - { function: accessHasAnyProfile, parameters: ['member'] }
+adherent_list_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Users'] }
+    - { function: organizationIsShowAdherentList }
+    - { function: accessHasAnyProfile, parameters: ['member'] }
 
 
-  subscription_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
-      - { function: accessHasAnyProfile, parameters: ['admin', 'administratifManager', 'pedagogicManager', 'financialManager', 'caMember'] }
+subscription_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyProfile,
+        parameters:
+          [
+            'admin',
+            'administratifManager',
+            'pedagogicManager',
+            'financialManager',
+            'caMember',
+          ],
+      }
 
 
-  my_bills_page:
-    action: 'display'
-    conditions:
-      - { function: accessHasAnyProfile, parameters: ['guardian', 'payor']}
+my_bills_page:
+  action: 'display'
+  conditions:
+    - { function: accessHasAnyProfile, parameters: ['guardian', 'payor'] }
 
 
-  cmf_licence_person_page:
-    action: 'display'
-    conditions:
-      - { function: organizationIsCmf }
-      - { function: accessIsAdminAccount, expectedResult: false }
+cmf_licence_person_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsCmf }
+    - { function: accessIsAdminAccount, expectedResult: false }

+ 194 - 122
config/abilities/pages/parameters.yaml

@@ -1,124 +1,196 @@
-  organization_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['GeneralConfig']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'organization'}]}
-
-  cmf_licence_page:
-    action: 'display'
-    conditions:
-        - { function: organizationIsCmf}
-        - { function: organizationHasAnyModule, parameters: ['GeneralConfig']}
-        - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'organization'}] }
-
-  parameters_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['GeneralConfig']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'general-config'}] }
-
-  parameters_communication_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: [ 'GeneralConfig' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'general-config' } ] }
-
-  parameters_student_page:
-    action: 'display'
-    conditions:
-      - { function: organizationIsSchool }
-      - { function: organizationHasAnyModule, parameters: [ 'GeneralConfig' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'general-config' } ] }
-
-  parameters_education_page:
-    action: 'display'
-    conditions:
-      - { function: organizationIsSchool }
-      - { function: organizationHasAnyModule, parameters: [ 'GeneralConfig' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'general-config' } ] }
-
-  parameters_bills_page:
-    action: 'display'
-    conditions:
-      - { function: organizationIsSchool }
-      - { function: organizationHasAnyModule, parameters: [ 'GeneralConfig' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'general-config' } ] }
-
-  parameters_secure_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: [ 'GeneralConfig' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'general-config' } ] }
-
-  place_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['GeneralConfig']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'place'}]}
-
-  education_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'pedagogics-administration'}]}
-
-  tag_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['TaggAdvanced']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'tagg'}]}
-
-  activities_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['GeneralConfig']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'activity'}]}
-
-  template_systems_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: ['TemplateMessages']}
-      - { function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'general-config'}]}
-
-  billing_settings_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
-
-  billing_schedules_settings_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['BillingAdministration']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'billings-administration'}]}
-
-  online_registration_settings_page:
-    action: 'display'
-    conditions:
-      - {function: organizationIsSchool}
-      - {function: organizationHasAnyModule, parameters: ['IEL']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'onlineregistration-administration'}]}
-
-  transition_next_year_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'pedagogics-administration'}]}
-
-  course_duplication_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'manage', subject: 'pedagogics-administration'}]}
-
-  import_page:
-    action: 'display'
-    conditions:
-      - function: organizationHasAnyModule
+organization_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'organization' }],
+      }
+
+cmf_licence_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsCmf }
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'organization' }],
+      }
+
+parameters_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+parameters_communication_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+parameters_student_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsSchool }
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+parameters_education_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsSchool }
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+parameters_bills_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsSchool }
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+parameters_secure_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+place_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'place' }],
+      }
+
+education_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'pedagogics-administration' }],
+      }
+
+tag_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['TaggAdvanced'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'manage', subject: 'tagg' }],
+      }
+
+activities_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['GeneralConfig'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'activity' }],
+      }
+
+template_systems_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['TemplateMessages'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'general-config' }],
+      }
+
+billing_settings_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'billings-administration' }],
+      }
+
+billing_schedules_settings_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['BillingAdministration'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'billings-administration' }],
+      }
+
+online_registration_settings_page:
+  action: 'display'
+  conditions:
+    - { function: organizationIsSchool }
+    - { function: organizationHasAnyModule, parameters: ['IEL'] }
+    - {
+        function: accessHasAnyRoleAbility,
         parameters:
         parameters:
-          - 'Users'
-          - 'Equipments'
-      - function: accessHasAnyRoleAbility
+          [{ action: 'read', subject: 'onlineregistration-administration' }],
+      }
+
+transition_next_year_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
         parameters:
         parameters:
-          - {action: 'manage', subject: 'user'}
-          - {action: 'manage', subject: 'equipments'}
+          [{ action: 'manage', subject: 'pedagogics-administration' }],
+      }
+
+course_duplication_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters:
+          [{ action: 'manage', subject: 'pedagogics-administration' }],
+      }
+
+import_page:
+  action: 'display'
+  conditions:
+    - function: organizationHasAnyModule
+      parameters:
+        - 'Users'
+        - 'Equipments'
+    - function: accessHasAnyRoleAbility
+      parameters:
+        - { action: 'manage', subject: 'user' }
+        - { action: 'manage', subject: 'equipments' }

+ 50 - 35
config/abilities/pages/schedule.yaml

@@ -1,39 +1,54 @@
-  agenda_page:
-    action: 'display'
-    conditions:
-      - function: organizationHasAnyModule
-        parameters:
-          - 'Events'
-          - 'Courses'
-          - 'Examens'
-          - 'EducationalProjects'
-      - function: accessHasAnyRoleAbility
-        parameters:
-          - {action: 'read', subject: 'events'}
-          - {action: 'read', subject: 'examens'}
-          - {action: 'read', subject: 'educationalprojects'}
-          - {action: 'read', subject: 'courses'}
+agenda_page:
+  action: 'display'
+  conditions:
+    - function: organizationHasAnyModule
+      parameters:
+        - 'Events'
+        - 'Courses'
+        - 'Examens'
+        - 'EducationalProjects'
+    - function: accessHasAnyRoleAbility
+      parameters:
+        - { action: 'read', subject: 'events' }
+        - { action: 'read', subject: 'examens' }
+        - { action: 'read', subject: 'educationalprojects' }
+        - { action: 'read', subject: 'courses' }
 
 
-  attendance_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Attendances']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'attendances'}]}
+attendance_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Attendances'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'attendances' }],
+      }
 
 
-  course_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: [ 'Courses' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'courses' } ] }
+course_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Courses'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'courses' }],
+      }
 
 
-  exam_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: [ 'Examens' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'examens' } ] }
+exam_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Examens'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'examens' }],
+      }
 
 
-  pedagogics_project_page:
-    action: 'display'
-    conditions:
-      - { function: organizationHasAnyModule, parameters: [ 'EducationalProjects' ] }
-      - { function: accessHasAnyRoleAbility, parameters: [ { action: 'read', subject: 'educationalprojects' } ] }
+pedagogics_project_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['EducationalProjects'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'educationalprojects' }],
+      }

+ 38 - 20
config/abilities/pages/stats.yaml

@@ -1,23 +1,41 @@
-  report_activity_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['Statistic']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+report_activity_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['Statistic'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'statistic' }],
+      }
 
 
-  education_quotas_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['PedagogicsAdministation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'educationstudent'}]}
+education_quotas_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['PedagogicsAdministation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'educationstudent' }],
+      }
 
 
-  fede_stats_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['StatisticFederation']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+fede_stats_page:
+  action: 'display'
+  conditions:
+    - {
+        function: organizationHasAnyModule,
+        parameters: ['StatisticFederation'],
+      }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'statistic' }],
+      }
 
 
-  structure_stats_page:
-    action: 'display'
-    conditions:
-      - {function: organizationHasAnyModule, parameters: ['StatisticStructure']}
-      - {function: accessHasAnyRoleAbility, parameters: [{action: 'read', subject: 'statistic'}]}
+structure_stats_page:
+  action: 'display'
+  conditions:
+    - { function: organizationHasAnyModule, parameters: ['StatisticStructure'] }
+    - {
+        function: accessHasAnyRoleAbility,
+        parameters: [{ action: 'read', subject: 'statistic' }],
+      }

+ 7 - 14
doc/abilities.md

@@ -2,7 +2,7 @@
 
 
 ### Principe de base
 ### Principe de base
 
 
-La gestion des droits se base sur la librairie [@casl/ability](https://casl.js.org/v6/en/) et génère un ensemble de 
+La gestion des droits se base sur la librairie [@casl/ability](https://casl.js.org/v6/en/) et génère un ensemble de
 droits (abilities), par exemple le droit d'afficher une page.
 droits (abilities), par exemple le droit d'afficher une page.
 
 
 Ces droits dépendent de deux choses :
 Ces droits dépendent de deux choses :
@@ -10,13 +10,12 @@ Ces droits dépendent de deux choses :
 - Les modules du logiciel possédés par l'organisation
 - Les modules du logiciel possédés par l'organisation
 - Les rôles de l'utilisateur dans cette organisation (y compris d'éventuels droits personnalisés)
 - Les rôles de l'utilisateur dans cette organisation (y compris d'éventuels droits personnalisés)
 
 
-Par la suite, on pourra tester les droits d'un utilisateur à effectuer une action en faisant par exemple : 
+Par la suite, on pourra tester les droits d'un utilisateur à effectuer une action en faisant par exemple :
 
 
       if(!ability.can('display', 'subscription_page')) {
       if(!ability.can('display', 'subscription_page')) {
           throw new Error('Forbidden')
           throw new Error('Forbidden')
       }
       }
 
 
-
 ### Fonctionnement
 ### Fonctionnement
 
 
 #### Les rôles de l'utilisateur
 #### Les rôles de l'utilisateur
@@ -30,7 +29,7 @@ Les rôles de l'utilisateur sont récupérés auprès de l'API. Ils peuvent êtr
 
 
 L'ensemble des droits est défini dans les fichiers de configuration `./config/abilities`
 L'ensemble des droits est défini dans les fichiers de configuration `./config/abilities`
 
 
-Chaque entrée est constituée : 
+Chaque entrée est constituée :
 
 
 - d'un nom
 - d'un nom
 - d'une action associée (`display` ou `manage`)
 - d'une action associée (`display` ou `manage`)
@@ -45,7 +44,6 @@ Exemple :
         - { function: organizationIsShowAdherentList }
         - { function: organizationIsShowAdherentList }
         - { function: accessHasAnyProfile, parameters: ['member'] }
         - { function: accessHasAnyProfile, parameters: ['member'] }
 
 
-
 #### Le plugin ability.ts
 #### Le plugin ability.ts
 
 
 Le plugin `plugins/ability.ts` :
 Le plugin `plugins/ability.ts` :
@@ -55,7 +53,6 @@ Le plugin `plugins/ability.ts` :
 3. lui injecte les deux profils récupérés plus haut
 3. lui injecte les deux profils récupérés plus haut
 4. puis créé un listener qui appellera la méthode `buildAbilities` de ce service à chaque mise à jour du profil de l'organisation
 4. puis créé un listener qui appellera la méthode `buildAbilities` de ce service à chaque mise à jour du profil de l'organisation
 
 
-
 #### Le service AbilityBuilder
 #### Le service AbilityBuilder
 
 
 Ce service fait appel tour à tour à deux méthodes `buildAbilitiesFromRoles` et `buildAbilitiesFromConfig`
 Ce service fait appel tour à tour à deux méthodes `buildAbilitiesFromRoles` et `buildAbilitiesFromConfig`
@@ -64,19 +61,15 @@ La première fait appel à son tour au service `RoleUtils` pour convertir tous l
 par ex "Membre du CA") en droits. Ainsi un rôle `ROLE_EXAMENS` deviendra un droit `{subject: 'examen', action: 'manage'}`
 par ex "Membre du CA") en droits. Ainsi un rôle `ROLE_EXAMENS` deviendra un droit `{subject: 'examen', action: 'manage'}`
 et un rôle deviendra un droits `{subject: 'billing_administration', action: 'display'}`
 et un rôle deviendra un droits `{subject: 'billing_administration', action: 'display'}`
 
 
-La seconde construit les droits à partir des fichiers de configuration qui définissent les conditions d'accès aux 
-différents droits (voir plus haut). Ces conditions sont testées de diverses manières selon leur nature. Par exemple, 
+La seconde construit les droits à partir des fichiers de configuration qui définissent les conditions d'accès aux
+différents droits (voir plus haut). Ces conditions sont testées de diverses manières selon leur nature. Par exemple,
 la condition `accessIsAdminAccount` deviendra un test sur `accessProfile.isAdminAccount`.
 la condition `accessIsAdminAccount` deviendra un test sur `accessProfile.isAdminAccount`.
 
 
 Une fois construit, ces droits sont passés au service MongoAbility de la librairie Casl.
 Une fois construit, ces droits sont passés au service MongoAbility de la librairie Casl.
 
 
 #### Utilisation
 #### Utilisation
 
 
-A partir de là, on pourra tester les droits d'un utilisateur dans une page ou un composable à l'aide de la commande 
-`ability.can(action, subject)`. 
+A partir de là, on pourra tester les droits d'un utilisateur dans une page ou un composable à l'aide de la commande
+`ability.can(action, subject)`.
 
 
 Cette méthode sera ainsi appelée en en-tête de la section script de chaque page, et pour chaque entrée des menus.
 Cette méthode sera ainsi appelée en en-tête de la section script de chaque page, et pour chaque entrée des menus.
-
-
-
-

+ 8 - 18
doc/colors.md

@@ -1,4 +1,3 @@
-
 ## Définition des couleurs de l'UI
 ## Définition des couleurs de l'UI
 
 
 ### Thèmes vuetify
 ### Thèmes vuetify
@@ -10,7 +9,7 @@
 Les couleurs du ou des thèmes sont enregistrées dans `plugins/vuetify.ts`
 Les couleurs du ou des thèmes sont enregistrées dans `plugins/vuetify.ts`
 
 
 | Nom            | Usage                                                                                                                                         |
 | Nom            | Usage                                                                                                                                         |
-|----------------|-----------------------------------------------------------------------------------------------------------------------------------------------|
+| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
 | background     | Fond par défaut des contenus                                                                                                                  |
 | background     | Fond par défaut des contenus                                                                                                                  |
 | surface        | Fond des components                                                                                                                           |
 | surface        | Fond des components                                                                                                                           |
 | primary        | Couleur principale, utilisée pour le header, les boutons submit, les notifications, les liens de type actions dans les menus, les liens, etc. |
 | primary        | Couleur principale, utilisée pour le header, les boutons submit, les notifications, les liens de type actions dans les menus, les liens, etc. |
@@ -27,7 +26,7 @@ Les couleurs du ou des thèmes sont enregistrées dans `plugins/vuetify.ts`
 
 
 Pour chacune de ces couleurs, on définit une ou plusieurs couleurs complémentaires, comme les couleurs `on-[color]` (voir plus bas)
 Pour chacune de ces couleurs, on définit une ou plusieurs couleurs complémentaires, comme les couleurs `on-[color]` (voir plus bas)
 
 
-On trouvera aussi des couleurs en `x-[color]`: ce sont des couleurs particulières appliquées à un élément précis. Ces 
+On trouvera aussi des couleurs en `x-[color]`: ce sont des couleurs particulières appliquées à un élément précis. Ces
 couleurs sont appelées à disparaitre à terme.
 couleurs sont appelées à disparaitre à terme.
 
 
 ### Règles de nommage
 ### Règles de nommage
@@ -38,35 +37,26 @@ On construit le nom d'une variable de couleur de la manière suivante :
 
 
 Si la partie 'name' comprend plusieurs mots, on les sépare, eux aussi, par des tirets `-`
 Si la partie 'name' comprend plusieurs mots, on les sépare, eux aussi, par des tirets `-`
 
 
-
 ##### Préfixe (optionnel)
 ##### Préfixe (optionnel)
 
 
 Pour chaque couleur du thème, on définira des couleurs nommées `on-[color]` qui correspondent au "négatif" de la couleur,
 Pour chaque couleur du thème, on définira des couleurs nommées `on-[color]` qui correspondent au "négatif" de la couleur,
 et qui permet de rendre du contenu visible sur un fond de la couleur `color`.
 et qui permet de rendre du contenu visible sur un fond de la couleur `color`.
 
 
-
 ##### Suffixe (optionnel)
 ##### Suffixe (optionnel)
 
 
 Un éventuel suffixe pourra aussi permettre de définir des variantes applicable à certains éléments.
 Un éventuel suffixe pourra aussi permettre de définir des variantes applicable à certains éléments.
 
 
-Par exemple, une couleur `on-color--clickable` sera une variante de la couleur `on-color` applicable aux éléments 
+Par exemple, une couleur `on-color--clickable` sera une variante de la couleur `on-color` applicable aux éléments
 cliquables, comme les liens.
 cliquables, comme les liens.
 
 
-
 ### Thèmes SCSS
 ### Thèmes SCSS
 
 
-Pour chaque couleur du thème, on a défini des classes css `theme-[color]` qui appliqueront les règles à la fois sur 
+Pour chaque couleur du thème, on a défini des classes css `theme-[color]` qui appliqueront les règles à la fois sur
 au fond de l'élément et à son contenu.
 au fond de l'élément et à son contenu.
 
 
 Ces classes sont définies dans le fichier `assets/css/theme.scss`
 Ces classes sont définies dans le fichier `assets/css/theme.scss`
 
 
-> Dans la majorité des cas, appliquer la classe vuetify `bg-[color]` aura le même effet que d'appliquer la classe 
-`theme-[color]`. Mais il peut arriver que certains thèmes définis dans cette application implémentent des règles 
-spéciales pour certains éléments, comme la couleur des liens d'un thème par exemple. 
-C'est pourquoi il vaut mieux utiliser les classes `theme-[color]`.
-
-
-
-
-
-
+> Dans la majorité des cas, appliquer la classe vuetify `bg-[color]` aura le même effet que d'appliquer la classe
+> `theme-[color]`. Mais il peut arriver que certains thèmes définis dans cette application implémentent des règles
+> spéciales pour certains éléments, comme la couleur des liens d'un thème par exemple.
+> C'est pourquoi il vaut mieux utiliser les classes `theme-[color]`.

+ 2 - 6
doc/unittests.md

@@ -1,20 +1,16 @@
-
 # Tests unitaires
 # Tests unitaires
 
 
 ## Exécuter les tests
 ## Exécuter les tests
 
 
-Pour exécuter les tests unitaires, lancer : 
+Pour exécuter les tests unitaires, lancer :
 
 
     yarn test
     yarn test
 
 
-
 ## Vitest
 ## Vitest
 
 
-Les tests unitaires sont exécutés avec [Vitest](https://vitest.dev/), qui est recommandé par 
+Les tests unitaires sont exécutés avec [Vitest](https://vitest.dev/), qui est recommandé par
 la documentation [Vue3](https://vuejs.org/guide/scaling-up/testing.html#recommendation-1).
 la documentation [Vue3](https://vuejs.org/guide/scaling-up/testing.html#recommendation-1).
 
 
-
 ## Coverage
 ## Coverage
 
 
 La couverture des tests peut être retrouvée dans le fichier `./coverage/index.html`
 La couverture des tests peut être retrouvée dans le fichier `./coverage/index.html`
-

+ 27 - 33
env/setupEnv.mjs

@@ -7,51 +7,45 @@
  *     HOST=ci node ./env/setupEnv.mjs
  *     HOST=ci node ./env/setupEnv.mjs
  *
  *
  */
  */
-import os from "os";
-import fs from "fs";
-import {fileURLToPath} from "node:url";
-import path from 'path';
-
+import os from 'os'
+import fs from 'fs'
+import { fileURLToPath } from 'node:url'
+import path from 'path'
 
 
 const projectDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
 const projectDir = path.join(path.dirname(fileURLToPath(import.meta.url)), '..')
 
 
 const hostname = process.env.HOST ?? os.hostname()
 const hostname = process.env.HOST ?? os.hostname()
 
 
 const environments = {
 const environments = {
-    'app': '.env.docker',
-    'prod-v2': '.env.prod',
-    'test-v2': '.env.test',
-    'test1': '.env.test1',
-    'test2': '.env.test2',
-    'test3': '.env.test3',
-    'test4': '.env.test4',
-    'test5': '.env.test5',
-    'ci': '.env.ci',
+  app: '.env.docker',
+  'prod-v2': '.env.prod',
+  'test-v2': '.env.test',
+  test1: '.env.test1',
+  test2: '.env.test2',
+  test3: '.env.test3',
+  test4: '.env.test4',
+  test5: '.env.test5',
+  ci: '.env.ci',
 }
 }
 
 
 if (!environments.hasOwnProperty(hostname)) {
 if (!environments.hasOwnProperty(hostname)) {
-    throw Error("Critical : unknown environment [" + hostname + "]")
+  throw Error('Critical : unknown environment [' + hostname + ']')
 }
 }
 
 
 const targetEnvFile = path.join(projectDir, 'env', environments[hostname])
 const targetEnvFile = path.join(projectDir, 'env', environments[hostname])
 const mainEnvFile = path.join(projectDir, '.env')
 const mainEnvFile = path.join(projectDir, '.env')
 
 
 fs.unlink(mainEnvFile, (err) => {
 fs.unlink(mainEnvFile, (err) => {
-    // 'ENOENT' is the error code for "no such file or directory", we ignore this one
-    if (err && err.code !== 'ENOENT') {
-        throw err
-    }
-    console.log(`${mainEnvFile} was deleted`);
-});
-
-fs.symlink(
-    targetEnvFile,
-    '.env',
-    'file',
-    (err) => {
-        if (err) {
-            throw (err);
-        }
-        console.log(`Symlink created : ${mainEnvFile} -> ${targetEnvFile}`)
-    }
-)
+  // 'ENOENT' is the error code for "no such file or directory", we ignore this one
+  if (err && err.code !== 'ENOENT') {
+    throw err
+  }
+  console.log(`${mainEnvFile} was deleted`)
+})
+
+fs.symlink(targetEnvFile, '.env', 'file', (err) => {
+  if (err) {
+    throw err
+  }
+  console.log(`Symlink created : ${mainEnvFile} -> ${targetEnvFile}`)
+})

+ 5 - 5
lang/fr.json

@@ -176,7 +176,7 @@
   "cycle": "Cycle",
   "cycle": "Cycle",
   "timing": "Durée d'un enseignement (en minutes)",
   "timing": "Durée d'un enseignement (en minutes)",
   "educationTiming": "Durée d'un enseignement (en minutes)",
   "educationTiming": "Durée d'un enseignement (en minutes)",
-  "new_education_timings" : "Nouvelle durée d'enseignement",
+  "new_education_timings": "Nouvelle durée d'enseignement",
   "superAdmin": "Compte super-admin",
   "superAdmin": "Compte super-admin",
   "username": "Login de connexion",
   "username": "Login de connexion",
   "residenceArea": "Zones de résidence",
   "residenceArea": "Zones de résidence",
@@ -204,7 +204,7 @@
   "bulletinPrintAddress": "L'adresse postale de l'élève ou son tuteur",
   "bulletinPrintAddress": "L'adresse postale de l'élève ou son tuteur",
   "bulletinWithTeacher": "Le nom du professeur",
   "bulletinWithTeacher": "Le nom du professeur",
   "bulletinCriteriaSort": "Ordre de tri des critères",
   "bulletinCriteriaSort": "Ordre de tri des critères",
-  "superAdminEmail" : "Adresse mail associée",
+  "superAdminEmail": "Adresse mail associée",
   "bulletin_parameters": "Bulletins",
   "bulletin_parameters": "Bulletins",
   "sms": "Sms",
   "sms": "Sms",
   "web_parameters": "Site internet",
   "web_parameters": "Site internet",
@@ -649,11 +649,11 @@
   "allow_teachers_to_consult_colleagues_informations": "Autoriser les professeurs à consulter le listing de leurs collègues (noms, prénoms, et coordonnées)",
   "allow_teachers_to_consult_colleagues_informations": "Autoriser les professeurs à consulter le listing de leurs collègues (noms, prénoms, et coordonnées)",
   "allow_students_to_consult_their_pedagogical_followup": "Autoriser les élèves à consulter leur suivi pédagogique",
   "allow_students_to_consult_their_pedagogical_followup": "Autoriser les élèves à consulter leur suivi pédagogique",
   "allow_teachers_to_create_courses": "Autoriser les professeurs à créer des cours",
   "allow_teachers_to_create_courses": "Autoriser les professeurs à créer des cours",
-  "INITIATION_CYCLE":  "Cycle initiation",
+  "INITIATION_CYCLE": "Cycle initiation",
   "CYCLE_1": "Cycle 1",
   "CYCLE_1": "Cycle 1",
-  "CYCLE_2":  "Cycle 2",
+  "CYCLE_2": "Cycle 2",
   "CYCLE_3": "Cycle 3",
   "CYCLE_3": "Cycle 3",
-  "CYCLE_4":  "Cycle 4",
+  "CYCLE_4": "Cycle 4",
   "OUT_CYCLE": "Hors cycle",
   "OUT_CYCLE": "Hors cycle",
   "originalLabel": "Libellés d'origine",
   "originalLabel": "Libellés d'origine",
   "effectiveLabel": "Libellés actuellement utilisés",
   "effectiveLabel": "Libellés actuellement utilisés",

+ 1 - 1
layouts/.eslintrc.cjs

@@ -2,5 +2,5 @@
 module.exports = {
 module.exports = {
   rules: {
   rules: {
     'vue/multi-word-component-names': 0,
     'vue/multi-word-component-names': 0,
-  }
+  },
 }
 }

+ 5 - 3
package.json

@@ -2,8 +2,8 @@
   "name": "app",
   "name": "app",
   "private": true,
   "private": true,
   "version": "2.5.0",
   "version": "2.5.0",
-  "engines" : {
-    "node" : "18.19"
+  "engines": {
+    "node": "18.19"
   },
   },
   "scripts": {
   "scripts": {
     "postinstall": "node ./env/setupEnv.mjs",
     "postinstall": "node ./env/setupEnv.mjs",
@@ -17,7 +17,9 @@
     "test": "vitest run",
     "test": "vitest run",
     "enable-devtools": "nuxi devtools enable",
     "enable-devtools": "nuxi devtools enable",
     "nuxt-upgrade": "nuxi upgrade --force",
     "nuxt-upgrade": "nuxi upgrade --force",
-    "eslint":  "eslint ."
+    "eslint": "eslint .",
+    "prettier-check": "yarn prettier . --check",
+    "prettier-fix": "yarn prettier . --write"
   },
   },
   "dependencies": {
   "dependencies": {
     "@casl/ability": "^6.5.0",
     "@casl/ability": "^6.5.0",

+ 1 - 1
pages/.eslintrc.cjs

@@ -2,5 +2,5 @@
 module.exports = {
 module.exports = {
   rules: {
   rules: {
     'vue/multi-word-component-names': 0,
     'vue/multi-word-component-names': 0,
-  }
+  },
 }
 }

+ 5 - 5
tests/.eslintrc.cjs

@@ -1,9 +1,9 @@
 /** On désactive cette règle pour les pages et les layouts seulement : https://eslint.vuejs.org/rules/multi-word-component-names.html */
 /** On désactive cette règle pour les pages et les layouts seulement : https://eslint.vuejs.org/rules/multi-word-component-names.html */
 module.exports = {
 module.exports = {
   rules: {
   rules: {
-    "@typescript-eslint/ban-ts-comment": 0,
-    "@typescript-eslint/no-unused-vars": 0,
-    "@typescript-eslint/no-explicit-any": 0,
-    "require-await": 0
-  }
+    '@typescript-eslint/ban-ts-comment': 0,
+    '@typescript-eslint/no-unused-vars': 0,
+    '@typescript-eslint/no-explicit-any': 0,
+    'require-await': 0,
+  },
 }
 }

+ 0 - 1
tests/units/readme.md

@@ -1,4 +1,3 @@
-
 # Tests unitaires
 # Tests unitaires
 
 
 > Voir doc/unittest.md
 > Voir doc/unittest.md

+ 2 - 7
tsconfig.json

@@ -12,12 +12,7 @@
       "@nuxtjs/i18n",
       "@nuxtjs/i18n",
       "vitest/globals"
       "vitest/globals"
     ],
     ],
-    "exclude": [
-      "node_modules",
-      ".nuxt"
-    ],
-    "typeRoots": [
-      "./types"
-    ]
+    "exclude": ["node_modules", ".nuxt"],
+    "typeRoots": ["./types"]
   }
   }
 }
 }

+ 29 - 30
types/data.d.ts

@@ -1,58 +1,57 @@
-import ApiResource from "~/models/ApiResource";
-import type {EnumChoice} from "~/types/interfaces";
+import ApiResource from '~/models/ApiResource'
+import type { EnumChoice } from '~/types/interfaces'
 
 
 type AnyJson = Record<string, any>
 type AnyJson = Record<string, any>
 
 
 interface AssociativeArray {
 interface AssociativeArray {
-    [key: string]: any;
+  [key: string]: any
 }
 }
 
 
 interface Connector {
 interface Connector {
-    request(
-        method: HTTP_METHOD,
-        url: string,
-        body: null | any,
-        params: null | AssociativeArray,
-        query: null | AssociativeArray
-    )
+  request(
+    method: HTTP_METHOD,
+    url: string,
+    body: null | any,
+    params: null | AssociativeArray,
+    query: null | AssociativeArray,
+  )
 }
 }
 
 
 interface HydraMetadata {
 interface HydraMetadata {
-    readonly totalItems?: number
-    firstPage?: number
-    lastPage?: number
-    nextPage?: number
-    previousPage?: number
-    type?: METADATA_TYPE
+  readonly totalItems?: number
+  firstPage?: number
+  lastPage?: number
+  nextPage?: number
+  previousPage?: number
+  type?: METADATA_TYPE
 }
 }
 
 
 interface ApiResponse {
 interface ApiResponse {
-    data: AnyJson
-    metadata: HydraMetadata
+  data: AnyJson
+  metadata: HydraMetadata
 }
 }
 
 
 interface ApiCollection extends ApiResponse {
 interface ApiCollection extends ApiResponse {
-    data: AnyJson
-    metadata: HydraMetadata
+  data: AnyJson
+  metadata: HydraMetadata
 }
 }
 
 
-
 interface Pagination {
 interface Pagination {
-    first?: number
-    last?: number
-    next?: number
-    previous?: number
+  first?: number
+  last?: number
+  next?: number
+  previous?: number
 }
 }
 
 
 interface Collection {
 interface Collection {
-    items: Array<ApiResource>
-    pagination: Pagination
-    totalItems: number | undefined
+  items: Array<ApiResource>
+  pagination: Pagination
+  totalItems: number | undefined
 }
 }
 
 
 interface EnumItem {
 interface EnumItem {
-    value: string
-    label: string
+  value: string
+  label: string
 }
 }
 
 
 type Enum = Array<EnumChoice>
 type Enum = Array<EnumChoice>

+ 9 - 13
types/interfaces.d.ts

@@ -1,12 +1,8 @@
 import { Store } from 'pinia'
 import { Store } from 'pinia'
-import {AnyJson} from "~/types/enum/data";
-import {
-  ABILITIES,
-  GENDER,
-  TYPE_ALERT,
-} from '~/types/enum/enums'
-import {type ComputedRef, Ref} from "@vue/reactivity";
-import type {MenuGroup, MenuItem} from "~/types/layout";
+import { AnyJson } from '~/types/enum/data'
+import { ABILITIES, GENDER, TYPE_ALERT } from '~/types/enum/enums'
+import { type ComputedRef, Ref } from '@vue/reactivity'
+import type { MenuGroup, MenuItem } from '~/types/layout'
 
 
 declare module '@vuex-orm/core' {
 declare module '@vuex-orm/core' {
   interface Query {
   interface Query {
@@ -168,11 +164,11 @@ interface DolibarrAccount {
   socId: number
   socId: number
   clientNumber: string
   clientNumber: string
   product:
   product:
-      | 'PRODUCT_ARTIST'
-      | 'PRODUCT_ARTIST_PREMIUM'
-      | 'PRODUCT_SCHOOL'
-      | 'PRODUCT_SCHOOL_PREMIUM'
-      | 'PRODUCT_MANAGER'
+    | 'PRODUCT_ARTIST'
+    | 'PRODUCT_ARTIST_PREMIUM'
+    | 'PRODUCT_SCHOOL'
+    | 'PRODUCT_SCHOOL_PREMIUM'
+    | 'PRODUCT_MANAGER'
   contract: DolibarrContract
   contract: DolibarrContract
   bills: Array<DolibarrBill>
   bills: Array<DolibarrBill>
 }
 }

+ 18 - 18
types/layout.d.ts

@@ -1,33 +1,33 @@
-import {MENU_LINK_TYPE} from "~/types/enum/layout";
+import { MENU_LINK_TYPE } from '~/types/enum/layout'
 
 
 interface IconItem {
 interface IconItem {
-    name?: string
-    avatarId?: number | null
-    avatarByDefault?: string
+  name?: string
+  avatarId?: number | null
+  avatarByDefault?: string
 }
 }
 
 
 /**
 /**
  * Entrée d'un menu, correspondant à un lien de navigation
  * Entrée d'un menu, correspondant à un lien de navigation
  */
  */
 interface MenuItem {
 interface MenuItem {
-    label: string
-    icon?: IconItem
-    to?: string
-    /** Type de lien (interne, externe, v1)  */
-    type: MENU_LINK_TYPE
-    avatar?: number
-    /** Correspond à la page actuelle */
-    active: boolean
+  label: string
+  icon?: IconItem
+  to?: string
+  /** Type de lien (interne, externe, v1)  */
+  type: MENU_LINK_TYPE
+  avatar?: number
+  /** Correspond à la page actuelle */
+  active: boolean
 }
 }
 
 
 /**
 /**
  * Sous-menu, regroupant d'autres MenuItem et/ou MenuGroup
  * Sous-menu, regroupant d'autres MenuItem et/ou MenuGroup
  */
  */
 interface MenuGroup {
 interface MenuGroup {
-    label: string
-    icon?: IconItem
-    children?: MenuItems
-    actions?: MenuItems
+  label: string
+  icon?: IconItem
+  children?: MenuItems
+  actions?: MenuItems
 }
 }
 
 
 type MenuItems = Array<MenuItem | MenuGroup>
 type MenuItems = Array<MenuItem | MenuGroup>
@@ -36,6 +36,6 @@ type MenuItems = Array<MenuItem | MenuGroup>
  * Service responsable de la génération d'un menu
  * Service responsable de la génération d'un menu
  */
  */
 interface MenuBuilder {
 interface MenuBuilder {
-    getMenuName: () => string
-    build: () => MenuItem | MenuGroup | null
+  getMenuName: () => string
+  build: () => MenuItem | MenuGroup | null
 }
 }

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.