Просмотр исходного кода

Merge tag '2.4.4' into develop

Release 2.4.4
Vincent 8 месяцев назад
Родитель
Сommit
a904e68c4a
35 измененных файлов с 248 добавлено и 229 удалено
  1. 1 1
      .eslintrc.cjs
  2. 18 2
      components/Layout/Dialog/Trial/AlreadyDid.vue
  3. 2 2
      components/Layout/Parameters/EntityTable.vue
  4. 16 21
      components/Layout/Parameters/Table.vue
  5. 1 1
      components/Layout/UpgradePremiumButton.vue
  6. 1 1
      components/Ui/Button/Delete.vue
  7. 0 1
      components/Ui/Form.vue
  8. 12 7
      components/Ui/Form/DeletionConfirmationDialog.vue
  9. 1 5
      components/Ui/Form/Edition.vue
  10. 21 16
      components/Ui/Input/Autocomplete/Accesses.vue
  11. 1 1
      components/Ui/Input/Email.vue
  12. 6 8
      composables/form/useDeleteItem.ts
  13. 71 58
      eslint.config.mjs
  14. 1 0
      i18n/lang/fr.json
  15. 25 25
      nuxt.config.ts
  16. 1 5
      pages/my-settings.vue
  17. 1 4
      pages/parameters/attendances.vue
  18. 1 4
      pages/parameters/bulletin.vue
  19. 1 4
      pages/parameters/education_notation.vue
  20. 1 2
      pages/parameters/education_timings/index.vue
  21. 1 4
      pages/parameters/general_parameters.vue
  22. 3 6
      pages/parameters/intranet.vue
  23. 1 2
      pages/parameters/residence_areas/index.vue
  24. 1 4
      pages/parameters/sms.vue
  25. 1 4
      pages/parameters/subdomains/new.vue
  26. 3 1
      pages/parameters/super_admin.vue
  27. 16 15
      pages/parameters/teaching.vue
  28. 18 6
      pages/subscription.vue
  29. 3 6
      plugins/init.server.ts
  30. 8 2
      services/data/Filters/InArrayFilter.ts
  31. 1 4
      services/data/entityManager.ts
  32. 1 1
      services/rights/abilityBuilder.ts
  33. 7 3
      tests/units/services/data/entityManager.test.ts
  34. 1 2
      tests/units/services/rights/abilityBuilder.test.ts
  35. 1 1
      types/interfaces.d.ts

+ 1 - 1
.eslintrc.cjs

@@ -18,7 +18,7 @@ module.exports = {
     'plugin:@typescript-eslint/recommended',
     'plugin:@typescript-eslint/recommended',
     'plugin:vue/vue3-recommended',
     'plugin:vue/vue3-recommended',
     'plugin:prettier/recommended',
     'plugin:prettier/recommended',
-    'plugin:you-dont-need-lodash-underscore/compatible'
+    'plugin:you-dont-need-lodash-underscore/compatible',
   ],
   ],
   ignorePatterns: [
   ignorePatterns: [
     '.nuxt',
     '.nuxt',

+ 18 - 2
components/Layout/Dialog/Trial/AlreadyDid.vue

@@ -15,7 +15,7 @@
         </p>
         </p>
         <p>
         <p>
           Si toutefois vous souhaitez une réactivation exceptionnelle de
           Si toutefois vous souhaitez une réactivation exceptionnelle de
-          l’essai, n’hésitez pas à contacter notre équipe Opentalent. Nous
+          l’essai, n’hésitez pas à contacter notre équipe Opentalent. Nous
           serons ravis d’évaluer votre demande et de vous accompagner dans vos
           serons ravis d’évaluer votre demande et de vous accompagner dans vos
           projets.
           projets.
         </p>
         </p>
@@ -25,7 +25,10 @@
       <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="closeDialog">
       <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="closeDialog">
         {{ $t('cancel') }}
         {{ $t('cancel') }}
       </v-btn>
       </v-btn>
-      <v-btn class="mr-4 submitBtn theme-warning" @click="contactOpentalent">
+      <v-btn class="mr-4 submitBtn theme-warning" @click="goSubscribe">
+        {{ $t('i_subscribe') }}
+      </v-btn>
+      <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="contactOpentalent">
         {{ $t('opentalent_contact') }}
         {{ $t('opentalent_contact') }}
       </v-btn>
       </v-btn>
     </template>
     </template>
@@ -33,6 +36,10 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
+import UrlUtils from "~/services/utils/urlUtils";
+
+const runtimeConfig = useRuntimeConfig()
+
 const props = defineProps({
 const props = defineProps({
   show: {
   show: {
     type: Boolean,
     type: Boolean,
@@ -46,6 +53,15 @@ const closeDialog = () => {
   emit('closeDialog')
   emit('closeDialog')
 }
 }
 
 
+const goSubscribe = async () => {
+  const v1BaseURL =
+    runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
+
+  await navigateTo(UrlUtils.join(v1BaseURL, '#', 'subscribe'), {
+    external: true,
+  })
+}
+
 const contactOpentalent = async () => {
 const contactOpentalent = async () => {
   emit('closeDialog')
   emit('closeDialog')
 
 

+ 2 - 2
components/Layout/Parameters/EntityTable.vue

@@ -31,8 +31,8 @@ import type ApiResource from '~/models/ApiResource'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import type { AssociativeArray } from '~/types/data'
 import type { AssociativeArray } from '~/types/data'
 import type { ColumnDefinition } from '~/types/interfaces'
 import type { ColumnDefinition } from '~/types/interfaces'
-import {useDeleteItem} from '~/composables/form/useDeleteItem'
-import {useEntityManager} from '~/composables/data/useEntityManager';
+import { useDeleteItem } from '~/composables/form/useDeleteItem'
+import { useEntityManager } from '~/composables/data/useEntityManager'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**

+ 16 - 21
components/Layout/Parameters/Table.vue

@@ -13,14 +13,8 @@ A data table for the parameters page
         </tr>
         </tr>
       </thead>
       </thead>
       <tbody v-if="items">
       <tbody v-if="items">
-        <tr
-          v-for="(item, i) in items"
-          :key="i"
-        >
-          <td
-            v-for="col in columnsDefinitions"
-            class="cycle-editable-cell"
-          >
+        <tr v-for="(item, i) in items" :key="i">
+          <td v-for="col in columnsDefinitions" class="cycle-editable-cell">
             {{ item[col.property] }}
             {{ item[col.property] }}
           </td>
           </td>
 
 
@@ -66,9 +60,9 @@ A data table for the parameters page
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {TABLE_ACTION} from '~/types/enum/enums';
-import UrlUtils from '~/services/utils/urlUtils';
-import type {ColumnDefinition} from '~/types/interfaces';
+import { TABLE_ACTION } from '~/types/enum/enums'
+import UrlUtils from '~/services/utils/urlUtils'
+import type { ColumnDefinition } from '~/types/interfaces'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -90,7 +84,7 @@ const props = defineProps({
   columnsDefinitions: {
   columnsDefinitions: {
     type: Array as PropType<Array<ColumnDefinition> | null>,
     type: Array as PropType<Array<ColumnDefinition> | null>,
     required: false,
     required: false,
-    default: null
+    default: null,
   },
   },
   /**
   /**
    * The property used as identifier (required by 'edition' link)
    * The property used as identifier (required by 'edition' link)
@@ -98,7 +92,7 @@ const props = defineProps({
   identifier: {
   identifier: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: 'id'
+    default: 'id',
   },
   },
   /**
   /**
    * List of the actions available for each record
    * List of the actions available for each record
@@ -106,7 +100,7 @@ const props = defineProps({
   actions: {
   actions: {
     type: Array as PropType<Array<TABLE_ACTION>>,
     type: Array as PropType<Array<TABLE_ACTION>>,
     required: false,
     required: false,
-    default: [TABLE_ACTION.EDIT, TABLE_ACTION.DELETE, TABLE_ACTION.ADD]
+    default: [TABLE_ACTION.EDIT, TABLE_ACTION.DELETE, TABLE_ACTION.ADD],
   },
   },
   /**
   /**
    * The URL for the edit / create pages
    * The URL for the edit / create pages
@@ -118,8 +112,8 @@ const props = defineProps({
   actionsRoute: {
   actionsRoute: {
     type: String,
     type: String,
     required: false,
     required: false,
-    default: '/parameters'
-  }
+    default: '/parameters',
+  },
 })
 })
 
 
 const i18n = useI18n()
 const i18n = useI18n()
@@ -131,14 +125,13 @@ const getId = (item: object) => {
 }
 }
 
 
 const columns: ComputedRef<Array<ColumnDefinition>> = computed(() => {
 const columns: ComputedRef<Array<ColumnDefinition>> = computed(() => {
-  return props.columnsDefinitions.map(col => {
+  return props.columnsDefinitions.map((col) => {
     return {
     return {
       property: col.property,
       property: col.property,
-      label: col.label ?? i18n.t(col.property)
+      label: col.label ?? i18n.t(col.property),
     }
     }
   })
   })
 })
 })
-
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
@@ -154,7 +147,8 @@ const columns: ComputedRef<Array<ColumnDefinition>> = computed(() => {
     font-weight: 600;
     font-weight: 600;
 
 
     td {
     td {
-      border-bottom: thin solid rgba(var(--v-border-color), var(--v-border-opacity));
+      border-bottom: thin solid
+        rgba(var(--v-border-color), var(--v-border-opacity));
     }
     }
 
 
     td:last-of-type {
     td:last-of-type {
@@ -162,7 +156,8 @@ const columns: ComputedRef<Array<ColumnDefinition>> = computed(() => {
     }
     }
   }
   }
 
 
-  th, td {
+  th,
+  td {
     padding: 10px;
     padding: 10px;
     text-align: left;
     text-align: left;
   }
   }

+ 1 - 1
components/Layout/UpgradePremiumButton.vue

@@ -86,7 +86,7 @@ const trialAction = async () => {
   padding: 5px 10px;
   padding: 5px 10px;
   cursor: pointer;
   cursor: pointer;
   white-space: pre-line;
   white-space: pre-line;
-  font-size: 13px;
+  font-size: 12px;
 
 
   .v-icon {
   .v-icon {
     font-size: 16px;
     font-size: 16px;

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

@@ -18,7 +18,7 @@ Bouton Delete avec modale de confirmation de la suppression
 <script setup lang="ts">
 <script setup lang="ts">
 import type { Ref, PropType } from 'vue'
 import type { Ref, PropType } from 'vue'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
-import {useDeleteItem} from '~/composables/form/useDeleteItem'
+import { useDeleteItem } from '~/composables/form/useDeleteItem'
 
 
 const props = defineProps({
 const props = defineProps({
   entity: {
   entity: {

+ 0 - 1
components/Ui/Form.vue

@@ -204,7 +204,6 @@ const closeConfirmationDialog = () => {
 
 
 const emit = defineEmits(['update:model-value'])
 const emit = defineEmits(['update:model-value'])
 
 
-
 // ### Actions du formulaire
 // ### Actions du formulaire
 /**
 /**
  * Soumet le formulaire
  * Soumet le formulaire

+ 12 - 7
components/Ui/Form/DeletionConfirmationDialog.vue

@@ -15,7 +15,10 @@
     </template>
     </template>
 
 
     <template #dialogBtn>
     <template #dialogBtn>
-      <v-btn class="mr-4 submitBtn theme-neutral-strong" @click="onCancelClicked">
+      <v-btn
+        class="mr-4 submitBtn theme-neutral-strong"
+        @click="onCancelClicked"
+      >
         {{ $t('cancel') }}
         {{ $t('cancel') }}
       </v-btn>
       </v-btn>
       <v-btn class="mr-4 submitBtn theme-danger" @click="onDeleteClicked">
       <v-btn class="mr-4 submitBtn theme-danger" @click="onDeleteClicked">
@@ -28,11 +31,15 @@
 <script setup lang="ts">
 <script setup lang="ts">
 const props = defineProps({
 const props = defineProps({
   modelValue: {
   modelValue: {
-    type: Boolean
-  }
+    type: Boolean,
+  },
 })
 })
 
 
-const emit = defineEmits(['cancelClicked', 'deleteClicked', 'update:modelValue'])
+const emit = defineEmits([
+  'cancelClicked',
+  'deleteClicked',
+  'update:modelValue',
+])
 
 
 const onCancelClicked = () => {
 const onCancelClicked = () => {
   emit('cancelClicked')
   emit('cancelClicked')
@@ -45,6 +52,4 @@ const onDeleteClicked = () => {
 }
 }
 </script>
 </script>
 
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 1 - 5
components/Ui/Form/Edition.vue

@@ -1,11 +1,7 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else
-      v-model="entity"
-      :submitActions="submitActions"
-    >
+    <UiForm v-else v-model="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') }}

+ 21 - 16
components/Ui/Input/Autocomplete/Accesses.vue

@@ -36,11 +36,11 @@ import Access from '~/models/Access/Access'
 import * as _ from 'lodash-es'
 import * as _ from 'lodash-es'
 import Query from '~/services/data/Query'
 import Query from '~/services/data/Query'
 import OrderBy from '~/services/data/Filters/OrderBy'
 import OrderBy from '~/services/data/Filters/OrderBy'
-import {ORDER_BY_DIRECTION, SEARCH_STRATEGY} from '~/types/enum/data'
-import PageFilter from '~/services/data/Filters/PageFilter';
-import InArrayFilter from '~/services/data/Filters/InArrayFilter';
-import SearchFilter from '~/services/data/Filters/SearchFilter';
-import UserSearchItem from '~/models/Custom/Search/UserSearchItem';
+import { ORDER_BY_DIRECTION, SEARCH_STRATEGY } from '~/types/enum/data'
+import PageFilter from '~/services/data/Filters/PageFilter'
+import InArrayFilter from '~/services/data/Filters/InArrayFilter'
+import SearchFilter from '~/services/data/Filters/SearchFilter'
+import UserSearchItem from '~/models/Custom/Search/UserSearchItem'
 
 
 const props = defineProps({
 const props = defineProps({
   /**
   /**
@@ -147,7 +147,9 @@ const i18n = useI18n()
 const accessToItem = (userSearchItem: UserSearchItem): UserListItem => {
 const accessToItem = (userSearchItem: UserSearchItem): UserListItem => {
   return {
   return {
     id: userSearchItem.id,
     id: userSearchItem.id,
-    title: userSearchItem.fullName ? userSearchItem.fullName : `(${i18n.t('missing_name')})`,
+    title: userSearchItem.fullName
+      ? userSearchItem.fullName
+      : `(${i18n.t('missing_name')})`,
   }
   }
 }
 }
 
 
@@ -169,14 +171,14 @@ const activeIds = computed(() => {
 const queryActive = new Query(
 const queryActive = new Query(
   new OrderBy('fullName', ORDER_BY_DIRECTION.ASC),
   new OrderBy('fullName', ORDER_BY_DIRECTION.ASC),
   new PageFilter(ref(1), ref(20)),
   new PageFilter(ref(1), ref(20)),
-  new InArrayFilter('id', activeIds)
+  new InArrayFilter('id', activeIds),
 )
 )
 
 
-const {
-  data: collectionActive,
-  pending: pendingActive,
-} = fetchCollection(UserSearchItem, null, queryActive)
-
+const { data: collectionActive, pending: pendingActive } = fetchCollection(
+  UserSearchItem,
+  null,
+  queryActive,
+)
 
 
 /**
 /**
  * Query transmise à l'API lors des changements de filtre de recherche
  * Query transmise à l'API lors des changements de filtre de recherche
@@ -184,7 +186,7 @@ const {
 const querySearch = new Query(
 const querySearch = new Query(
   new OrderBy('fullName', ORDER_BY_DIRECTION.ASC),
   new OrderBy('fullName', ORDER_BY_DIRECTION.ASC),
   new PageFilter(ref(1), ref(20)),
   new PageFilter(ref(1), ref(20)),
-  new SearchFilter('fullName', nameFilter, SEARCH_STRATEGY.IPARTIAL)
+  new SearchFilter('fullName', nameFilter, SEARCH_STRATEGY.IPARTIAL),
 )
 )
 
 
 /**
 /**
@@ -198,7 +200,6 @@ const {
 
 
 const pending = computed(() => pendingSearch.value || pendingActive.value)
 const pending = computed(() => pendingSearch.value || pendingActive.value)
 
 
-
 /**
 /**
  * Contenu de la liste autocomplete
  * Contenu de la liste autocomplete
  */
  */
@@ -207,10 +208,14 @@ const items: ComputedRef<Array<UserListItem>> = computed(() => {
     return []
     return []
   }
   }
 
 
-  const activeItems: UserListItem[] = collectionActive.value.items.map(accessToItem)
+  const activeItems: UserListItem[] =
+    collectionActive.value.items.map(accessToItem)
   const searchedItems: UserListItem[] = collectionSearch.value.items
   const searchedItems: UserListItem[] = collectionSearch.value.items
     .map(accessToItem)
     .map(accessToItem)
-    .filter(item => !collectionActive.value!.items.find(other => other.id === item.id))
+    .filter(
+      (item) =>
+        !collectionActive.value!.items.find((other) => other.id === item.id),
+    )
 
 
   return activeItems.concat(searchedItems)
   return activeItems.concat(searchedItems)
 })
 })

+ 1 - 1
components/Ui/Input/Email.vue

@@ -18,7 +18,7 @@ Champs de saisie de type Text dédié à la saisie d'emails
 import { useNuxtApp } from '#app'
 import { useNuxtApp } from '#app'
 import { useFieldViolation } from '~/composables/form/useFieldViolation'
 import { useFieldViolation } from '~/composables/form/useFieldViolation'
 import { useValidationUtils } from '~/composables/utils/useValidationUtils'
 import { useValidationUtils } from '~/composables/utils/useValidationUtils'
-import type {PropType} from '@vue/runtime-core';
+import type { PropType } from '@vue/runtime-core'
 
 
 const props = defineProps({
 const props = defineProps({
   label: {
   label: {

+ 6 - 8
composables/form/useDeleteItem.ts

@@ -1,13 +1,11 @@
-import type ApiResource from '~/models/ApiResource';
-import {usePageStore} from '~/stores/page';
-import {TYPE_ALERT} from '~/types/enum/enums';
-import {useEntityManager} from '~/composables/data/useEntityManager';
-
+import type ApiResource from '~/models/ApiResource'
+import { usePageStore } from '~/stores/page'
+import { TYPE_ALERT } from '~/types/enum/enums'
+import { useEntityManager } from '~/composables/data/useEntityManager'
 
 
 export function useDeleteItem() {
 export function useDeleteItem() {
-
   async function deleteItem(item: ApiResource) {
   async function deleteItem(item: ApiResource) {
-    const {em} = useEntityManager()
+    const { em } = useEntityManager()
 
 
     try {
     try {
       await em.delete(item)
       await em.delete(item)
@@ -20,6 +18,6 @@ export function useDeleteItem() {
   }
   }
 
 
   return {
   return {
-    deleteItem
+    deleteItem,
   }
   }
 }
 }

+ 71 - 58
eslint.config.mjs

@@ -1,73 +1,86 @@
-import vue from "eslint-plugin-vue";
-import typescriptEslint from "@typescript-eslint/eslint-plugin";
-import globals from "globals";
-import parser from "vue-eslint-parser";
-import path from "node:path";
-import { fileURLToPath } from "node:url";
-import js from "@eslint/js";
-import { FlatCompat } from "@eslint/eslintrc";
+import vue from 'eslint-plugin-vue'
+import typescriptEslint from '@typescript-eslint/eslint-plugin'
+import globals from 'globals'
+import parser from 'vue-eslint-parser'
+import path from 'node:path'
+import { fileURLToPath } from 'node:url'
+import js from '@eslint/js'
+import { FlatCompat } from '@eslint/eslintrc'
 
 
-const __filename = fileURLToPath(import.meta.url);
-const __dirname = path.dirname(__filename);
+const __filename = fileURLToPath(import.meta.url)
+const __dirname = path.dirname(__filename)
 const compat = new FlatCompat({
 const compat = new FlatCompat({
-    baseDirectory: __dirname,
-    recommendedConfig: js.configs.recommended,
-    allConfig: js.configs.all
-});
+  baseDirectory: __dirname,
+  recommendedConfig: js.configs.recommended,
+  allConfig: js.configs.all,
+})
 
 
-export default [{
-    ignores: ["**/.nuxt", "coverage/*", "vendor/*", "dist/*", "models/models.ts"],
-}, ...compat.extends(
-    "@nuxtjs/eslint-config-typescript",
-    "plugin:nuxt/recommended",
-    "eslint:recommended",
-    "plugin:@typescript-eslint/recommended",
-    "plugin:vue/vue3-recommended",
-    "plugin:prettier/recommended",
-    "plugin:you-dont-need-lodash-underscore/compatible",
-), {
+export default [
+  {
+    ignores: [
+      '**/.nuxt',
+      'coverage/*',
+      'vendor/*',
+      'dist/*',
+      'models/models.ts',
+    ],
+  },
+  ...compat.extends(
+    '@nuxtjs/eslint-config-typescript',
+    'plugin:nuxt/recommended',
+    'eslint:recommended',
+    'plugin:@typescript-eslint/recommended',
+    'plugin:vue/vue3-recommended',
+    'plugin:prettier/recommended',
+    'plugin:you-dont-need-lodash-underscore/compatible',
+  ),
+  {
     plugins: {
     plugins: {
-        vue,
-        "@typescript-eslint": typescriptEslint,
+      vue,
+      '@typescript-eslint': typescriptEslint,
     },
     },
 
 
     languageOptions: {
     languageOptions: {
-        globals: {
-            ...globals.browser,
-            ...globals.node,
-            useRuntimeConfig: "readonly",
-            useAsyncData: "readonly",
-            navigateTo: "readonly",
-            computed: "readonly",
-            ref: "readonly",
-            definePageMeta: "readonly",
-            useRouter: "readonly",
-            useRoute: "readonly",
-            useI18n: "readonly",
-            onMounted: "readonly",
-            onUnmounted: "readonly",
-            watch: "readonly",
-            useRepo: "readonly",
-        },
+      globals: {
+        ...globals.browser,
+        ...globals.node,
+        useRuntimeConfig: 'readonly',
+        useAsyncData: 'readonly',
+        navigateTo: 'readonly',
+        computed: 'readonly',
+        ref: 'readonly',
+        definePageMeta: 'readonly',
+        useRouter: 'readonly',
+        useRoute: 'readonly',
+        useI18n: 'readonly',
+        onMounted: 'readonly',
+        onUnmounted: 'readonly',
+        watch: 'readonly',
+        useRepo: 'readonly',
+      },
 
 
-        parser: parser,
-        ecmaVersion: 2020,
-        sourceType: "module",
+      parser: parser,
+      ecmaVersion: 2020,
+      sourceType: 'module',
 
 
-        parserOptions: {
-            parser: "@typescript-eslint/parser",
-            tsconfigRootDir: "/home/workspace",
-        },
+      parserOptions: {
+        parser: '@typescript-eslint/parser',
+        tsconfigRootDir: '/home/workspace',
+      },
     },
     },
 
 
     rules: {
     rules: {
-        "no-console": 0,
+      'no-console': 0,
 
 
-        "vue/valid-v-slot": ["error", {
-            allowModifiers: true,
-        }],
+      'vue/valid-v-slot': [
+        'error',
+        {
+          allowModifiers: true,
+        },
+      ],
 
 
-        "vue/multi-word-component-names": 0,
-        "@typescript-eslint/no-inferrable-types": 0,
+      'vue/multi-word-component-names': 0,
+      '@typescript-eslint/no-inferrable-types': 0,
     },
     },
-}];
+  },
+]

+ 1 - 0
i18n/lang/fr.json

@@ -1,4 +1,5 @@
 {
 {
+  "i_subscribe": "Je m'abonne",
   "price_include_cmf": "Inclus avec votre adhésion CMF",
   "price_include_cmf": "Inclus avec votre adhésion CMF",
   "artist": "Artist Standard",
   "artist": "Artist Standard",
   "trial_started": "Sur votre période d'essai.\nSouscrire à l'offre Premium.",
   "trial_started": "Sur votre période d'essai.\nSouscrire à l'offre Premium.",

+ 25 - 25
nuxt.config.ts

@@ -38,30 +38,30 @@ export default defineNuxtConfig({
     renderJsonPayloads: false,
     renderJsonPayloads: false,
   },
   },
 
 
- runtimeConfig: {
-   // Private config that is only available on the server
-   env: '',
-   baseUrl: '',
-   baseUrlLegacy: '',
-   baseUrlAdminLegacy: '',
-   baseUrlTypo3: '',
-   baseUrlMercure: '',
-   fileStorageBaseUrl: '',
-   supportUrl: '',
-   basicomptaUrl: '',
-   // Config within public will be also exposed to the client
-   public: {
-     env: '',
-     baseUrl: '',
-     baseUrlLegacy: '',
-     baseUrlAdminLegacy: '',
-     baseUrlTypo3: '',
-     baseUrlMercure: '',
-     fileStorageBaseUrl: '',
-     supportUrl: '',
-     basicomptaUrl: '',
-   },
- },
+  runtimeConfig: {
+    // Private config that is only available on the server
+    env: '',
+    baseUrl: '',
+    baseUrlLegacy: '',
+    baseUrlAdminLegacy: '',
+    baseUrlTypo3: '',
+    baseUrlMercure: '',
+    fileStorageBaseUrl: '',
+    supportUrl: '',
+    basicomptaUrl: '',
+    // Config within public will be also exposed to the client
+    public: {
+      env: '',
+      baseUrl: '',
+      baseUrlLegacy: '',
+      baseUrlAdminLegacy: '',
+      baseUrlTypo3: '',
+      baseUrlMercure: '',
+      fileStorageBaseUrl: '',
+      supportUrl: '',
+      basicomptaUrl: '',
+    },
+  },
 
 
   hooks: {
   hooks: {
     'builder:watch': console.log,
     'builder:watch': console.log,
@@ -241,5 +241,5 @@ export default defineNuxtConfig({
     scripts: ['prepare/buildIndex.ts'],
     scripts: ['prepare/buildIndex.ts'],
   },
   },
 
 
- compatibilityDate: '2025-03-10'
+  compatibilityDate: '2025-03-10',
 })
 })

+ 1 - 5
pages/my-settings.vue

@@ -9,11 +9,7 @@ Page 'Mes préférences'
           <v-container fluid class="container">
           <v-container fluid class="container">
             <v-row>
             <v-row>
               <UiLoadingPanel v-if="pending" />
               <UiLoadingPanel v-if="pending" />
-              <UiForm
-                v-else
-                v-model="preferences"
-                action-position="bottom"
-              >
+              <UiForm v-else v-model="preferences" action-position="bottom">
                 <v-row>
                 <v-row>
                   <v-col cols="12">
                   <v-col cols="12">
                     <UiInputCheckbox
                     <UiInputCheckbox

+ 1 - 4
pages/parameters/attendances.vue

@@ -2,10 +2,7 @@
   <LayoutContainer>
   <LayoutContainer>
     <div v-if="organizationProfile.isSchool">
     <div v-if="organizationProfile.isSchool">
       <UiLoadingPanel v-if="pending" />
       <UiLoadingPanel v-if="pending" />
-      <UiForm
-        v-else-if="parameters !== null"
-        v-model="parameters"
-      >
+      <UiForm v-else-if="parameters !== null" v-model="parameters">
         <v-row>
         <v-row>
           <v-col cols="12">
           <v-col cols="12">
             <UiInputCheckbox
             <UiInputCheckbox

+ 1 - 4
pages/parameters/bulletin.vue

@@ -1,10 +1,7 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else
-      v-model="parameters"
-    >
+    <UiForm v-else v-model="parameters">
       <v-row>
       <v-row>
         <v-col cols="12">
         <v-col cols="12">
           <UiInputCheckbox
           <UiInputCheckbox

+ 1 - 4
pages/parameters/education_notation.vue

@@ -1,10 +1,7 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else
-      v-model="parameters"
-    >
+    <UiForm v-else v-model="parameters">
       <v-row>
       <v-row>
         <v-col cols="12">
         <v-col cols="12">
           <UiInputCheckbox
           <UiInputCheckbox

+ 1 - 2
pages/parameters/education_timings/index.vue

@@ -22,5 +22,4 @@ if (organizationProfile.parametersId === null) {
 }
 }
 </script>
 </script>
 
 
-<style scoped lang="scss">
-</style>
+<style scoped lang="scss"></style>

+ 1 - 4
pages/parameters/general_parameters.vue

@@ -1,10 +1,7 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else-if="parameters !== null"
-      v-model="parameters"
-    >
+    <UiForm v-else-if="parameters !== null" v-model="parameters">
       <v-row>
       <v-row>
         <v-col cols="12">
         <v-col cols="12">
           <UiInputDatePicker
           <UiInputDatePicker

+ 3 - 6
pages/parameters/intranet.vue

@@ -1,13 +1,10 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else
-      v-model="parameters"
-    >
+    <UiForm v-else v-model="parameters">
       <v-row>
       <v-row>
         <v-col cols="12">
         <v-col cols="12">
-          <h3>{{ $t('teachers')}}</h3>
+          <h3>{{ $t('teachers') }}</h3>
           <UiInputCheckbox
           <UiInputCheckbox
             v-model="parameters.createCourse"
             v-model="parameters.createCourse"
             field="createCourse"
             field="createCourse"
@@ -32,7 +29,7 @@
             label="allow_teachers_to_generate_attendance_reports"
             label="allow_teachers_to_generate_attendance_reports"
           />
           />
 
 
-          <h3>{{ $t('pupils-members')}}</h3>
+          <h3>{{ $t('pupils-members') }}</h3>
           <UiInputCheckbox
           <UiInputCheckbox
             v-model="parameters.administrationCc"
             v-model="parameters.administrationCc"
             field="administrationCc"
             field="administrationCc"

+ 1 - 2
pages/parameters/residence_areas/index.vue

@@ -15,5 +15,4 @@ definePageMeta({
 })
 })
 </script>
 </script>
 
 
-<style scoped lang="scss">
-</style>
+<style scoped lang="scss"></style>

+ 1 - 4
pages/parameters/sms.vue

@@ -1,9 +1,6 @@
 <template>
 <template>
   <div>
   <div>
-    <UiForm
-      v-if="parameters"
-      v-model="parameters"
-    >
+    <UiForm v-if="parameters" v-model="parameters">
       <v-row>
       <v-row>
         <v-col cols="12">
         <v-col cols="12">
           <UiInputText
           <UiInputText

+ 1 - 4
pages/parameters/subdomains/new.vue

@@ -69,10 +69,7 @@ const organizationProfileStore = useOrganizationProfileStore()
 
 
 // @ts-expect-error TODO à résoudre quand l'EM pourra gérer les types génériques
 // @ts-expect-error TODO à résoudre quand l'EM pourra gérer les types génériques
 const subdomain: Ref<Subdomain> = ref(
 const subdomain: Ref<Subdomain> = ref(
-  em.newInstance(
-    Subdomain,
-    { organization: organizationProfileStore.id }
-  )
+  em.newInstance(Subdomain, { organization: organizationProfileStore.id }),
 )
 )
 
 
 const submitActions = computed(() => {
 const submitActions = computed(() => {

+ 3 - 1
pages/parameters/super_admin.vue

@@ -20,7 +20,9 @@
         <tbody>
         <tbody>
           <tr>
           <tr>
             <td>{{ $t('username') }} :</td>
             <td>{{ $t('username') }} :</td>
-            <td><b>{{ adminAccess.username }}</b></td>
+            <td>
+              <b>{{ adminAccess.username }}</b>
+            </td>
           </tr>
           </tr>
         </tbody>
         </tbody>
       </v-table>
       </v-table>

+ 16 - 15
pages/parameters/teaching.vue

@@ -1,13 +1,13 @@
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiLoadingPanel v-if="pending" />
-    <UiForm
-      v-else-if="parameters !== null"
-      v-model="parameters"
-    >
+    <UiForm v-else-if="parameters !== null" v-model="parameters">
       <LayoutParametersTable
       <LayoutParametersTable
         :items="tableItems"
         :items="tableItems"
-        :columns-definitions="[{ property: 'originalLabel' }, { property: 'effectiveLabel' }]"
+        :columns-definitions="[
+          { property: 'originalLabel' },
+          { property: 'effectiveLabel' },
+        ]"
         identifier="value"
         identifier="value"
         :actions="[TABLE_ACTION.EDIT]"
         :actions="[TABLE_ACTION.EDIT]"
         @editClicked="goToCycleEditPage"
         @editClicked="goToCycleEditPage"
@@ -32,7 +32,7 @@ import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 import type { AnyJson } from '~/types/data'
 import type { AnyJson } from '~/types/data'
 import { useEnumFetch } from '~/composables/data/useEnumFetch'
 import { useEnumFetch } from '~/composables/data/useEnumFetch'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
-import {TABLE_ACTION} from '~/types/enum/enums';
+import { TABLE_ACTION } from '~/types/enum/enums'
 
 
 definePageMeta({
 definePageMeta({
   name: 'parameters_teaching_page',
   name: 'parameters_teaching_page',
@@ -84,13 +84,15 @@ const orderedCycles: ComputedRef<AnyJson> = computed(() => {
 })
 })
 
 
 const tableItems = computed(() => {
 const tableItems = computed(() => {
-  return cycleEnum.value?.map((item) => {
-    return {
-      value: item.value,
-      originalLabel: item.label,
-      effectiveLabel: (orderedCycles.value[item.value] ?? item).label,
-    }
-  }) || []
+  return (
+    cycleEnum.value?.map((item) => {
+      return {
+        value: item.value,
+        originalLabel: item.label,
+        effectiveLabel: (orderedCycles.value[item.value] ?? item).label,
+      }
+    }) || []
+  )
 })
 })
 
 
 const goToCycleEditPage = (item: object) => {
 const goToCycleEditPage = (item: object) => {
@@ -99,5 +101,4 @@ const goToCycleEditPage = (item: object) => {
 }
 }
 </script>
 </script>
 
 
-<style scoped lang="scss">
-</style>
+<style scoped lang="scss"></style>

+ 18 - 6
pages/subscription.vue

@@ -291,12 +291,20 @@ Page 'Mon abonnement'
                         *Convient aux petites écoles sans besoins spécifiques de
                         *Convient aux petites écoles sans besoins spécifiques de
                         gestion pédagogique, de facturation, etc. Pour une
                         gestion pédagogique, de facturation, etc. Pour une
                         solution complète optez pour Opentalent School
                         solution complète optez pour Opentalent School
-                        <br />
-                        **Tarif
-                        <span v-if="organizationProfile.isCmf"
-                          >adhérent CMF</span
-                        ><span v-else>public</span> 2025. Abonnement payable
-                        annuellement.
+                        <span
+                          v-if="
+                            !organizationProfile.isArtistPremiumProduct ||
+                            organizationProfile.isTrialActive
+                          "
+                        >
+                          <br />
+                            **Tarif
+                            <span v-if="organizationProfile.isCmf"
+                            >adhérent CMF</span
+                            ><span v-else>public</span> 2025. Abonnement payable
+                            annuellement.
+                        </span>
+
                       </span>
                       </span>
                     </v-col>
                     </v-col>
                   </v-row>
                   </v-row>
@@ -677,6 +685,10 @@ const downloadDolibarrBill = (ref: string): void => {
   border: 1px solid rgb(var(--v-theme-artist));
   border: 1px solid rgb(var(--v-theme-artist));
 }
 }
 
 
+.btn{
+  font-size: 12px;
+}
+
 .optionsCard {
 .optionsCard {
   :deep(.margin-sup) {
   :deep(.margin-sup) {
     margin-top: 0;
     margin-top: 0;

+ 3 - 6
plugins/init.server.ts

@@ -8,7 +8,8 @@ export default defineNuxtPlugin(async () => {
 
 
   const bearer: CookieRef<string | null> = useCookie('BEARER') ?? null
   const bearer: CookieRef<string | null> = useCookie('BEARER') ?? null
   const accessCookieId: CookieRef<string | null> = useCookie('AccessId') ?? null
   const accessCookieId: CookieRef<string | null> = useCookie('AccessId') ?? null
-  const switchCookieId: CookieRef<string | null> = useCookie('SwitchAccessId') ?? null
+  const switchCookieId: CookieRef<string | null> =
+    useCookie('SwitchAccessId') ?? null
 
 
   if (accessCookieId.value === null || Number.isNaN(accessCookieId.value)) {
   if (accessCookieId.value === null || Number.isNaN(accessCookieId.value)) {
     redirectToLogout()
     redirectToLogout()
@@ -29,11 +30,7 @@ export default defineNuxtPlugin(async () => {
   const { initiateProfile } = useRefreshProfile()
   const { initiateProfile } = useRefreshProfile()
 
 
   try {
   try {
-    await initiateProfile(
-      accessId,
-      bearer.value ?? '',
-      switchId,
-    )
+    await initiateProfile(accessId, bearer.value ?? '', switchId)
   } catch (error) {
   } catch (error) {
     if (error instanceof UnauthorizedError) {
     if (error instanceof UnauthorizedError) {
       redirectToLogout()
       redirectToLogout()

+ 8 - 2
services/data/Filters/InArrayFilter.ts

@@ -7,7 +7,10 @@ import RefUtils from '~/services/utils/refUtils'
 
 
 export default class InArrayFilter extends AbstractFilter implements ApiFilter {
 export default class InArrayFilter extends AbstractFilter implements ApiFilter {
   field: string
   field: string
-  filterValue: Array<string | number | null> | Ref<Array<string | number | null>> | null
+  filterValue:
+    | Array<string | number | null>
+    | Ref<Array<string | number | null>>
+    | null
 
 
   /**
   /**
    * @param field
    * @param field
@@ -16,7 +19,10 @@ export default class InArrayFilter extends AbstractFilter implements ApiFilter {
    */
    */
   constructor(
   constructor(
     field: string,
     field: string,
-    value: Array<string | number | null> | Ref<Array<string | number | null>> | null,
+    value:
+      | Array<string | number | null>
+      | Ref<Array<string | number | null>>
+      | null,
     reactiveFilter: boolean = false,
     reactiveFilter: boolean = false,
   ) {
   ) {
     super(reactiveFilter)
     super(reactiveFilter)

+ 1 - 4
services/data/entityManager.ts

@@ -77,10 +77,7 @@ class EntityManager {
    * @param instance
    * @param instance
    */
    */
   // noinspection JSMethodCanBeStatic
   // noinspection JSMethodCanBeStatic
-  public cast(
-    model: typeof ApiResource,
-    instance: ApiResource,
-  ): ApiResource {
+  public cast(model: typeof ApiResource, instance: ApiResource): ApiResource {
     // eslint-disable-next-line new-cap
     // eslint-disable-next-line new-cap
     return new model(instance)
     return new model(instance)
   }
   }

+ 1 - 1
services/rights/abilityBuilder.ts

@@ -5,7 +5,7 @@ import RoleUtils from '~/services/rights/roleUtils'
 import type { AbilitiesType, AccessProfile } from '~/types/interfaces'
 import type { AbilitiesType, AccessProfile } from '~/types/interfaces'
 import { ABILITIES } from '~/types/enum/enums'
 import { ABILITIES } from '~/types/enum/enums'
 import type OrganizationProfile from '~/models/Organization/OrganizationProfile'
 import type OrganizationProfile from '~/models/Organization/OrganizationProfile'
-import type { MongoAbility } from '@casl/ability/dist/types/Ability';
+import type { MongoAbility } from '@casl/ability/dist/types/Ability'
 
 
 interface ConditionParameters {
 interface ConditionParameters {
   action: string
   action: string

+ 7 - 3
tests/units/services/data/entityManager.test.ts

@@ -732,7 +732,7 @@ describe('reset', () => {
 
 
     const initialEntity = new DummyApiModel()
     const initialEntity = new DummyApiModel()
     initialEntity.id = 1
     initialEntity.id = 1
-    initialEntity.name = 'serges'
+    initialEntity.name = 'serge'
 
 
     // @ts-ignore
     // @ts-ignore
     entityManager.getInitialStateOf = vi.fn(
     entityManager.getInitialStateOf = vi.fn(
@@ -750,7 +750,7 @@ describe('reset', () => {
     // @ts-ignore
     // @ts-ignore
     repo.save = vi.fn((data: any) => null)
     repo.save = vi.fn((data: any) => null)
 
 
-    const result = entityManager.reset(DummyApiModel, entity)
+    const result = entityManager.reset(initialEntity)
 
 
     // @ts-ignore
     // @ts-ignore
     expect(entityManager.getInitialStateOf).toHaveBeenCalledWith(
     expect(entityManager.getInitialStateOf).toHaveBeenCalledWith(
@@ -769,8 +769,12 @@ describe('reset', () => {
     entityManager.getInitialStateOf = vi.fn(
     entityManager.getInitialStateOf = vi.fn(
       (model: typeof ApiResource, id: string | number) => null,
       (model: typeof ApiResource, id: string | number) => null,
     )
     )
+    // @ts-ignore
+    entityManager.getModel = vi.fn(
+      (instance: ApiResource) => DummyApiModel,
+    )
 
 
-    expect(() => entityManager.reset(DummyApiModel, entity)).toThrowError(
+    expect(() => entityManager.reset(entity)).toThrowError(
       'no initial state recorded for this object - abort [dummyModel/1]',
       'no initial state recorded for this object - abort [dummyModel/1]',
     )
     )
   })
   })

+ 1 - 2
tests/units/services/rights/abilityBuilder.test.ts

@@ -40,11 +40,10 @@ const doc = {
 
 
 vi.mock('yaml-import', () => {
 vi.mock('yaml-import', () => {
   return {
   return {
-    read: vi.fn((data: string) => doc)
+    read: vi.fn((data: string) => doc),
   }
   }
 })
 })
 
 
-
 beforeEach(() => {
 beforeEach(() => {
   ability = vi.fn() as any as MongoAbility
   ability = vi.fn() as any as MongoAbility
   accessProfile = vi.fn() as any as AccessProfile
   accessProfile = vi.fn() as any as AccessProfile

+ 1 - 1
types/interfaces.d.ts

@@ -205,7 +205,7 @@ interface ColumnDefinition {
   /**
   /**
    * The entity's property to display in this column
    * The entity's property to display in this column
    */
    */
-  property: string,
+  property: string
   /**
   /**
    * Label of the column.
    * Label of the column.
    * If not provided, a translation of the property's name will be looked for.
    * If not provided, a translation of the property's name will be looked for.