Olivier Massot пре 11 месеци
родитељ
комит
9b3eae5708

+ 2 - 2
assets/css/theme.scss

@@ -52,12 +52,12 @@
   color: rgb(var(--v-theme-on-x-create-btn)) !important;
   color: rgb(var(--v-theme-on-x-create-btn)) !important;
 }
 }
 
 
-.theme-artist{
+.theme-artist {
   background-color: rgb(var(--v-theme-artist)) !important;
   background-color: rgb(var(--v-theme-artist)) !important;
   color: rgb(var(--v-theme-on-surface)) !important;
   color: rgb(var(--v-theme-on-surface)) !important;
 }
 }
 
 
-.theme-school{
+.theme-school {
   background-color: rgb(var(--v-theme-school)) !important;
   background-color: rgb(var(--v-theme-school)) !important;
   color: rgb(var(--v-theme-on-primary)) !important;
   color: rgb(var(--v-theme-on-primary)) !important;
 }
 }

+ 1 - 1
components/Layout/Dialog.vue

@@ -85,7 +85,7 @@ const _show = computed(() => props.show) as boolean
     transform: rotate(-180deg);
     transform: rotate(-180deg);
   }
   }
 
 
-  .v-icon{
+  .v-icon {
     font-size: 25px;
     font-size: 25px;
     transform: rotate(90deg);
     transform: rotate(90deg);
     padding-right: 20px;
     padding-right: 20px;

+ 21 - 10
components/Layout/Dialog/Trial/AlreadyDid.vue

@@ -4,9 +4,21 @@
     <template #dialogTitle>{{ $t('trial_all_ready_did') }}</template>
     <template #dialogTitle>{{ $t('trial_all_ready_did') }}</template>
     <template #dialogText>
     <template #dialogText>
       <v-card-text class="text">
       <v-card-text class="text">
-        <p>Au cours des 6 derniers mois, vous avez bénéficié d’un essai gratuit de 30 jours du logiciel Opentalent Artist Premium.</p>
-        <p>Pour continuer à explorer toutes les fonctionnalités de notre solution et optimiser la gestion de votre structure, nous vous invitons à souscrire à l’une de nos offres adaptées à vos besoins.</p>
-        <p>Si toutefois vous souhaitez une réactivation exceptionnelle de l’essai, n’hésitez pas à contacter notre équipe Opentalent. 
Nous serons ravis d’évaluer votre demande et de vous accompagner dans vos projets.</p>
+        <p>
+          Au cours des 6 derniers mois, vous avez bénéficié d’un essai gratuit
+          de 30 jours du logiciel Opentalent Artist Premium.
+        </p>
+        <p>
+          Pour continuer à explorer toutes les fonctionnalités de notre solution
+          et optimiser la gestion de votre structure, nous vous invitons à
+          souscrire à l’une de nos offres adaptées à vos besoins.
+        </p>
+        <p>
+          Si toutefois vous souhaitez une réactivation exceptionnelle de
+          l’essai, n’hésitez pas à contacter notre équipe Opentalent. 
Nous
+          serons ravis d’évaluer votre demande et de vous accompagner dans vos
+          projets.
+        </p>
       </v-card-text>
       </v-card-text>
     </template>
     </template>
     <template #dialogBtn>
     <template #dialogBtn>
@@ -21,7 +33,6 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
 const props = defineProps({
 const props = defineProps({
   show: {
   show: {
     type: Boolean,
     type: Boolean,
@@ -42,16 +53,16 @@ const contactOpentalent = async () => {
     open: {
     open: {
       target: '_blank',
       target: '_blank',
     },
     },
-    external: true
+    external: true,
   })
   })
 }
 }
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .text{
-    font-size: 13px;
-    p{
-      margin-bottom: 10px;
-    }
+.text {
+  font-size: 13px;
+  p {
+    margin-bottom: 10px;
   }
   }
+}
 </style>
 </style>

+ 24 - 13
components/Layout/Dialog/Trial/StopConfirmation.vue

@@ -1,11 +1,23 @@
 <template>
 <template>
   <LazyLayoutDialog :show="show" theme="danger">
   <LazyLayoutDialog :show="show" theme="danger">
     <template #dialogType>{{ $t('important') }}</template>
     <template #dialogType>{{ $t('important') }}</template>
-    <template #dialogTitle>{{ $t('you_want_to_stop_your_premium_trial_period') }} ? </template>
+    <template #dialogTitle
+      >{{ $t('you_want_to_stop_your_premium_trial_period') }} ?
+    </template>
     <template #dialogText>
     <template #dialogText>
       <v-card-text class="text">
       <v-card-text class="text">
-        <p>{{ $t('stop_trial_period_warning_1a') }} {{ $t(organizationProfile.productBeforeTrial ?? 'stop_trial_missing_version_label') }}, {{ $t('stop_trial_period_warning_1b') }}</p>
-        <p><strong>{{ $t('stop_trial_period_warning_2') }}</strong></p>
+        <p>
+          {{ $t('stop_trial_period_warning_1a') }}
+          {{
+            $t(
+              organizationProfile.productBeforeTrial ??
+                'stop_trial_missing_version_label',
+            )
+          }}, {{ $t('stop_trial_period_warning_1b') }}
+        </p>
+        <p>
+          <strong>{{ $t('stop_trial_period_warning_2') }}</strong>
+        </p>
         <ul>
         <ul>
           <li>{{ $t('stop_trial_period_warning_3') }}</li>
           <li>{{ $t('stop_trial_period_warning_3') }}</li>
           <li>{{ $t('stop_trial_period_warning_4') }}</li>
           <li>{{ $t('stop_trial_period_warning_4') }}</li>
@@ -26,8 +38,7 @@
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
 
 defineProps({
 defineProps({
   show: {
   show: {
@@ -50,14 +61,14 @@ const stopTrial = () => {
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  .text {
-    p {
-      margin-bottom: 10px;
-    }
+.text {
+  p {
+    margin-bottom: 10px;
+  }
 
 
-    ul {
-      padding-left: 20px;
-      margin-bottom: 10px;
-    }
+  ul {
+    padding-left: 20px;
+    margin-bottom: 10px;
   }
   }
+}
 </style>
 </style>

+ 1 - 4
components/Layout/Header/Menu.vue

@@ -16,10 +16,7 @@ header principal (configuration, paramètres du compte...)
           :defaultImage="menu.icon.avatarByDefault"
           :defaultImage="menu.icon.avatarByDefault"
           :width="30"
           :width="30"
         /> -->
         /> -->
-        <UiImage
-          :defaultImage="menu.icon.avatarByDefault"
-          :width="30"
-        />
+        <UiImage :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" />

+ 79 - 75
components/Layout/Pages/Subscription/Card.vue

@@ -1,36 +1,41 @@
 <template>
 <template>
-    <v-card elevation="2" outlined shaped class="card" :class="{['border-'+color] : true}">
-      <span v-if="extraHeader" class="extraBorder" :class="'extraBorder-'+color">{{extraHeader}}</span>
+  <v-card
+    elevation="2"
+    outlined
+    shaped
+    class="card"
+    :class="{ ['border-' + color]: true }"
+  >
+    <span
+      v-if="extraHeader"
+      class="extraBorder"
+      :class="'extraBorder-' + color"
+      >{{ extraHeader }}</span
+    >
 
 
-      <!-- Titre -->
-      <v-card-title class="title" :class="{['margin-sup'] : !extraHeader}">
-        {{title}}
-      </v-card-title>
+    <!-- Titre -->
+    <v-card-title class="title" :class="{ ['margin-sup']: !extraHeader }">
+      {{ title }}
+    </v-card-title>
 
 
-      <v-card-subtitle class="subtitle">
-        {{subTitle}}
-        <slot name="card.subTitle" />
-      </v-card-subtitle>
-
-      <!-- Texte -->
-      <v-card-text>
-        <LayoutPagesSubscriptionList
-          :elements="list"
-          :color="color" />
-      </v-card-text>
-
-      <!-- Actions -->
-      <v-card-actions class="mb-3">
-        <slot name="card.action" />
-      </v-card-actions>
-
-    </v-card>
+    <v-card-subtitle class="subtitle">
+      {{ subTitle }}
+      <slot name="card.subTitle" />
+    </v-card-subtitle>
 
 
+    <!-- Texte -->
+    <v-card-text>
+      <LayoutPagesSubscriptionList :elements="list" :color="color" />
+    </v-card-text>
 
 
+    <!-- Actions -->
+    <v-card-actions class="mb-3">
+      <slot name="card.action" />
+    </v-card-actions>
+  </v-card>
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-
 const props = defineProps({
 const props = defineProps({
   title: {
   title: {
     type: String,
     type: String,
@@ -56,61 +61,60 @@ const props = defineProps({
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
+.card {
+  border-width: 1px;
+  border-top-width: 4px;
+  height: 100%;
 
 
-  .card{
-    border-width: 1px;
-    border-top-width: 4px;
-    height: 100%;
-
-    .title{
-      padding-top: 10px;
-      white-space: normal;
-    }
-    .subtitle{
-      text-transform: uppercase;
-      font-weight: bold;
-      font-size: 1.25rem;
-      opacity: unset;
-    }
-//1280 1670
-    :deep(.v-btn){
-      padding: 10px;
-      width: 100%;
-      @media (min-width:1280px) and (max-width: 1670px) {
-        letter-spacing: 0px;
-        font-size: 11px;
-      }
-    }
+  .title {
+    padding-top: 10px;
+    white-space: normal;
   }
   }
-
-  .extraBorder{
-    text-align: center;
+  .subtitle {
     text-transform: uppercase;
     text-transform: uppercase;
-    margin: auto;
-    border-radius: 0px 0px 5px 5px;
-    width: 90%;
-    padding: 2px 10px 2px 10px;
     font-weight: bold;
     font-weight: bold;
-    display: block;
-  }
-  .card.border-primary{
-    border-color: rgb(var(--v-theme-primary));
-  }
-  .card.border-artist{
-    border-color: rgb(var(--v-theme-artist));
-  }
-  .card.border-school{
-    border-color: rgb(var(--v-theme-school));
+    font-size: 1.25rem;
+    opacity: unset;
   }
   }
-  .extraBorder.extraBorder-artist{
-    background: rgb(var(--v-theme-artist));
-  }
-  .extraBorder.extraBorder-school{
-    background: rgb(var(--v-theme-school));
-    color:#fff;
+  //1280 1670
+  :deep(.v-btn) {
+    padding: 10px;
+    width: 100%;
+    @media (min-width: 1280px) and (max-width: 1670px) {
+      letter-spacing: 0px;
+      font-size: 11px;
+    }
   }
   }
+}
 
 
-  .margin-sup{
-    margin-top: 30px;
-  }
+.extraBorder {
+  text-align: center;
+  text-transform: uppercase;
+  margin: auto;
+  border-radius: 0px 0px 5px 5px;
+  width: 90%;
+  padding: 2px 10px 2px 10px;
+  font-weight: bold;
+  display: block;
+}
+.card.border-primary {
+  border-color: rgb(var(--v-theme-primary));
+}
+.card.border-artist {
+  border-color: rgb(var(--v-theme-artist));
+}
+.card.border-school {
+  border-color: rgb(var(--v-theme-school));
+}
+.extraBorder.extraBorder-artist {
+  background: rgb(var(--v-theme-artist));
+}
+.extraBorder.extraBorder-school {
+  background: rgb(var(--v-theme-school));
+  color: #fff;
+}
+
+.margin-sup {
+  margin-top: 30px;
+}
 </style>
 </style>

+ 7 - 7
components/Layout/Pages/Subscription/List.vue

@@ -7,7 +7,7 @@
         icon="fa-solid fa-check"
         icon="fa-solid fa-check"
         size="large"
         size="large"
       ></v-icon>
       ></v-icon>
-      <span class="pl-2">{{li}}</span>
+      <span class="pl-2">{{ li }}</span>
     </li>
     </li>
   </ul>
   </ul>
 </template>
 </template>
@@ -26,11 +26,11 @@ const props = defineProps({
 </script>
 </script>
 
 
 <style scoped lang="scss">
 <style scoped lang="scss">
-  ul{
-    list-style: none;
-    .check{
-      font-size: 15px;
-      font-weight: bold;
-    }
+ul {
+  list-style: none;
+  .check {
+    font-size: 15px;
+    font-weight: bold;
   }
   }
+}
 </style>
 </style>

+ 2 - 2
components/Ui/SystemBar.vue

@@ -8,10 +8,10 @@ System bars
     :class="
     :class="
       'd-flex flex-row justify-center align-center text-center ' + classes
       'd-flex flex-row justify-center align-center text-center ' + classes
     "
     "
-    style="z-index: 1006;"
+    style="z-index: 1006"
     @click="onClick !== undefined ? onClick() : null"
     @click="onClick !== undefined ? onClick() : null"
   >
   >
-  <!-- Forcing z-index to avoid this : https://github.com/vuetifyjs/nuxt-module/issues/205 -->
+    <!-- Forcing z-index to avoid this : https://github.com/vuetifyjs/nuxt-module/issues/205 -->
     <slot>
     <slot>
       <v-icon v-if="icon" small :icon="icon" />
       <v-icon v-if="icon" small :icon="icon" />
       {{ text }}
       {{ text }}

+ 5 - 2
composables/data/useApiLegacyRequestService.ts

@@ -5,7 +5,7 @@ import ApiRequestService from '~/services/data/apiRequestService'
 import { usePageStore } from '~/stores/page'
 import { usePageStore } from '~/stores/page'
 import UnauthorizedError from '~/services/error/UnauthorizedError'
 import UnauthorizedError from '~/services/error/UnauthorizedError'
 import { useAccessProfileStore } from '~/stores/accessProfile'
 import { useAccessProfileStore } from '~/stores/accessProfile'
-import UrlUtils from "~/services/utils/urlUtils";
+import UrlUtils from '~/services/utils/urlUtils'
 
 
 /**
 /**
  * Retourne une instance de ApiRequestService configurée pour interroger l'api legacy
  * Retourne une instance de ApiRequestService configurée pour interroger l'api legacy
@@ -16,7 +16,10 @@ let apiRequestServiceClass: null | ApiRequestService = null
 export const useApiLegacyRequestService = () => {
 export const useApiLegacyRequestService = () => {
   const runtimeConfig = useRuntimeConfig()
   const runtimeConfig = useRuntimeConfig()
 
 
-  const baseURL = UrlUtils.join(runtimeConfig.baseUrlLegacy || runtimeConfig.public.baseUrlLegacy, 'api')
+  const baseURL = UrlUtils.join(
+    runtimeConfig.baseUrlLegacy || runtimeConfig.public.baseUrlLegacy,
+    'api',
+  )
 
 
   const pending: Ref<boolean> = ref(false)
   const pending: Ref<boolean> = ref(false)
 
 

+ 1 - 1
composables/utils/useDownloadFromRoute.ts

@@ -11,7 +11,7 @@ export const useDownloadFromRoute = async (route: string, filename: string) => {
   const { apiRequestService } = useAp2iRequestService()
   const { apiRequestService } = useAp2iRequestService()
 
 
   // @ts-expect-error La méthode get renvoie bien un blob dans ce cas là
   // @ts-expect-error La méthode get renvoie bien un blob dans ce cas là
-  const response = await apiRequestService.get(route) as Blob
+  const response = (await apiRequestService.get(route)) as Blob
 
 
   if (!response || response.size === 0) {
   if (!response || response.size === 0) {
     console.error('Error: no file found at ' + route)
     console.error('Error: no file found at ' + route)

+ 2 - 2
config/theme.ts

@@ -34,8 +34,8 @@ interface Theme {
     'on-warning': string
     'on-warning': string
     info: string
     info: string
     'on-info': string
     'on-info': string
-    artist:string
-    school:string
+    artist: string
+    school: string
 
 
     // Special cases
     // Special cases
     // TODO: voir ceux dont on peut se passer
     // TODO: voir ceux dont on peut se passer

+ 1 - 1
models/OnlineRegistration/RegistrationStatus.ts

@@ -1,5 +1,5 @@
 import { Str, Uid, Num } from 'pinia-orm/dist/decorators'
 import { Str, Uid, Num } from 'pinia-orm/dist/decorators'
-import {IdField} from "~/models/decorators";
+import { IdField } from '~/models/decorators'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
 
 
 /**
 /**

+ 1 - 1
models/Organization/Cotisation.ts

@@ -1,6 +1,6 @@
 import { Num, Str, Uid } from 'pinia-orm/dist/decorators'
 import { Num, Str, Uid } from 'pinia-orm/dist/decorators'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
-import {IdField} from "~/models/decorators";
+import { IdField } from '~/models/decorators'
 
 
 /**
 /**
  * Ap2i ApiResource : Cotisation
  * Ap2i ApiResource : Cotisation

+ 1 - 1
models/Organization/DolibarrAccount.ts

@@ -1,6 +1,6 @@
 import { Attr, Str, Uid, Num } from 'pinia-orm/dist/decorators'
 import { Attr, Str, Uid, Num } from 'pinia-orm/dist/decorators'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
-import {IdField} from "~/models/decorators";
+import { IdField } from '~/models/decorators'
 
 
 /**
 /**
  * The Dolibarr account of an organization
  * The Dolibarr account of an organization

+ 1 - 1
models/Organization/MobytUserStatus.ts

@@ -1,6 +1,6 @@
 import { Bool, Num, Uid } from 'pinia-orm/dist/decorators'
 import { Bool, Num, Uid } from 'pinia-orm/dist/decorators'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
-import {IdField} from "~/models/decorators";
+import { IdField } from '~/models/decorators'
 
 
 /**
 /**
  * The Mobyt user status of an organization
  * The Mobyt user status of an organization

+ 213 - 213
nuxt.config.ts

@@ -30,216 +30,216 @@ if (process.env.NUXT_ENV === 'dev') {
  * @see https://nuxt.com/docs/api/configuration/nuxt-config
  * @see https://nuxt.com/docs/api/configuration/nuxt-config
  */
  */
 export default defineNuxtConfig({
 export default defineNuxtConfig({
- ssr: true,
-
- experimental: {
-   // Fix the 'Cannot stringify non POJO' bug
-   // @see https://github.com/nuxt/nuxt/issues/20787
-   renderJsonPayloads: false,
- },
-
- runtimeConfig: {
-   // Private config that is only available on the server
-   env: '',
-   baseUrl: '',
-   baseUrlLegacy: '',
-   baseUrlAdminLegacy: '',
-   baseUrlTypo3: '',
-   baseUrlMercure: '',
-   fileStorageBaseUrl: '',
-   supportUrl: '',
-   basicomptaUrl: 'https://app.basicompta.fr/',
-   // Config within public will be also exposed to the client
-   public: {
-     env: '',
-     baseUrl: '',
-     baseUrlLegacy: '',
-     baseUrlAdminLegacy: '',
-     baseUrlTypo3: '',
-     baseUrlMercure: '',
-     fileStorageBaseUrl: '',
-     supportUrl: '',
-     basicomptaUrl: 'https://app.basicompta.fr/',
-   },
- },
-
- hooks: {
-   'builder:watch': console.log,
- },
-
- app: {
-   head: {
-     title: 'Opentalent',
-     meta: [
-       { charset: 'utf-8' },
-       { name: 'viewport', content: 'width=device-width, initial-scale=1' },
-       { name: 'msapplication-TileColor', content: '#324250' },
-       {
-         name: 'msapplication-TileImage',
-         content: '/favicon/favicon-144x144.png',
-       },
-     ],
-     link: [
-       { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '57x57',
-         href: '/favicon/apple-touch-icon-57x57.png',
-       },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '114x114',
-         href: '/favicon/apple-touch-icon-114x114.png',
-       },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '72x72',
-         href: '/favicon/apple-touch-icon-72x72.png',
-       },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '144x144',
-         href: '/favicon/apple-touch-icon-144x144.png',
-       },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '120x120',
-         href: '/favicon/apple-touch-icon-120x120.png',
-       },
-       {
-         rel: 'apple-touch-icon-precomposed',
-         sizes: '152x152',
-         href: '/favicon/apple-touch-icon-152x152.png',
-       },
-       {
-         rel: 'icon',
-         sizes: '32x32',
-         type: 'image/x-icon',
-         href: '/favicon/favicon-32x32.png',
-       },
-       {
-         rel: 'icon',
-         sizes: '16x16',
-         type: 'image/x-icon',
-         href: '/favicon/favicon-16x16.png',
-       },
-     ],
-   },
- },
-
- css: [
-   '@/assets/css/global.scss',
-   '@/assets/css/theme.scss',
-   '@/assets/css/import.scss',
-   '@vuepic/vue-datepicker/dist/main.css',
- ],
-
- typescript: {
-   strict: true,
- },
-
- modules: [
-   // eslint-disable-next-line require-await
-   async (_, nuxt) => {
-     nuxt.hooks.hook('vite:extendConfig', (config) =>
-       // @ts-expect-error A revoir après que les lignes aient été décommentées
-       (config.plugins ?? []).push(
-         vuetify(),
-         // Remplacer par cela quand l'issue https://github.com/vuetifyjs/vuetify-loader/issues/273 sera règlée..
-         // voir aussi : https://github.com/nuxt/nuxt/issues/15412 et https://github.com/vuetifyjs/vuetify-loader/issues/290
-         // voir aussi : https://github.com/jrutila/nuxt3-vuetify3-bug
-         // vuetify({
-         //     styles: { configFile: './assets/css/settings.scss' }
-         // })
-       ),
-     )
-   },
-   [
-     '@pinia/nuxt',
-     {
-       autoImports: [
-         // automatically imports `usePinia()`
-         'defineStore',
-         // automatically imports `usePinia()` as `usePiniaStore()`
-         ['defineStore', 'definePiniaStore'],
-       ],
-     },
-   ],
-   '@pinia-orm/nuxt',
-   '@nuxtjs/i18n',
-   '@nuxt/devtools',
-   '@nuxt/image',
-   'nuxt-prepare',
-   'nuxt-vitalizer',
- ],
-
- vite: {
-   esbuild: {
-     drop: process.env.DEBUG ? [] : ['console', 'debugger'],
-     tsconfigRaw: {
-       compilerOptions: {
-         experimentalDecorators: true,
-       },
-     },
-   },
-   ssr: {
-     // with ssr enabled, this config is required to load vuetify properly
-     noExternal: ['vuetify'],
-   },
-   server: {
-     https,
-     // @ts-expect-error J'ignore pourquoi cette erreur TS se produit, cette propriété est valide
-     port: 443,
-     hmr: {
-       protocol: 'wss',
-       port: 24678,
-     },
-   },
- },
-
- // Hide the sourcemaps warnings with vuetify
- // @see https://github.com/vuetifyjs/vuetify-loader/issues/290#issuecomment-1435702713
- sourcemap: {
-   server: false,
-   client: false,
- },
-
- i18n: {
-   langDir: 'lang',
-   lazy: true,
-   strategy: 'no_prefix',
-   locales: [
-     {
-       code: 'en',
-       iso: 'en-US',
-       file: 'en.json',
-       name: 'English',
-     },
-     {
-       code: 'fr',
-       iso: 'fr-FR',
-       file: 'fr.json',
-       name: 'Français',
-     },
-   ],
-   defaultLocale: 'fr',
-   detectBrowserLanguage: false,
-   vueI18n: './i18n.config.ts',
- },
-
- image: {
-   provider: 'none',
- },
-
- build: {
-   transpile,
- },
-
- ignore: [process.env.NUXT_ENV === 'prod' ? 'pages/dev/*' : ''],
-
- prepare: {
-   scripts: ['prepare/buildIndex.ts'],
- },
-
- compatibilityDate: '2025-02-07'
-})
+  ssr: true,
+
+  experimental: {
+    // Fix the 'Cannot stringify non POJO' bug
+    // @see https://github.com/nuxt/nuxt/issues/20787
+    renderJsonPayloads: false,
+  },
+
+  runtimeConfig: {
+    // Private config that is only available on the server
+    env: '',
+    baseUrl: '',
+    baseUrlLegacy: '',
+    baseUrlAdminLegacy: '',
+    baseUrlTypo3: '',
+    baseUrlMercure: '',
+    fileStorageBaseUrl: '',
+    supportUrl: '',
+    basicomptaUrl: 'https://app.basicompta.fr/',
+    // Config within public will be also exposed to the client
+    public: {
+      env: '',
+      baseUrl: '',
+      baseUrlLegacy: '',
+      baseUrlAdminLegacy: '',
+      baseUrlTypo3: '',
+      baseUrlMercure: '',
+      fileStorageBaseUrl: '',
+      supportUrl: '',
+      basicomptaUrl: 'https://app.basicompta.fr/',
+    },
+  },
+
+  hooks: {
+    'builder:watch': console.log,
+  },
+
+  app: {
+    head: {
+      title: 'Opentalent',
+      meta: [
+        { charset: 'utf-8' },
+        { name: 'viewport', content: 'width=device-width, initial-scale=1' },
+        { name: 'msapplication-TileColor', content: '#324250' },
+        {
+          name: 'msapplication-TileImage',
+          content: '/favicon/favicon-144x144.png',
+        },
+      ],
+      link: [
+        { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '57x57',
+          href: '/favicon/apple-touch-icon-57x57.png',
+        },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '114x114',
+          href: '/favicon/apple-touch-icon-114x114.png',
+        },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '72x72',
+          href: '/favicon/apple-touch-icon-72x72.png',
+        },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '144x144',
+          href: '/favicon/apple-touch-icon-144x144.png',
+        },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '120x120',
+          href: '/favicon/apple-touch-icon-120x120.png',
+        },
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '152x152',
+          href: '/favicon/apple-touch-icon-152x152.png',
+        },
+        {
+          rel: 'icon',
+          sizes: '32x32',
+          type: 'image/x-icon',
+          href: '/favicon/favicon-32x32.png',
+        },
+        {
+          rel: 'icon',
+          sizes: '16x16',
+          type: 'image/x-icon',
+          href: '/favicon/favicon-16x16.png',
+        },
+      ],
+    },
+  },
+
+  css: [
+    '@/assets/css/global.scss',
+    '@/assets/css/theme.scss',
+    '@/assets/css/import.scss',
+    '@vuepic/vue-datepicker/dist/main.css',
+  ],
+
+  typescript: {
+    strict: true,
+  },
+
+  modules: [
+    // eslint-disable-next-line require-await
+    async (_, nuxt) => {
+      nuxt.hooks.hook('vite:extendConfig', (config) =>
+        // @ts-expect-error A revoir après que les lignes aient été décommentées
+        (config.plugins ?? []).push(
+          vuetify(),
+          // Remplacer par cela quand l'issue https://github.com/vuetifyjs/vuetify-loader/issues/273 sera règlée..
+          // voir aussi : https://github.com/nuxt/nuxt/issues/15412 et https://github.com/vuetifyjs/vuetify-loader/issues/290
+          // voir aussi : https://github.com/jrutila/nuxt3-vuetify3-bug
+          // vuetify({
+          //     styles: { configFile: './assets/css/settings.scss' }
+          // })
+        ),
+      )
+    },
+    [
+      '@pinia/nuxt',
+      {
+        autoImports: [
+          // automatically imports `usePinia()`
+          'defineStore',
+          // automatically imports `usePinia()` as `usePiniaStore()`
+          ['defineStore', 'definePiniaStore'],
+        ],
+      },
+    ],
+    '@pinia-orm/nuxt',
+    '@nuxtjs/i18n',
+    '@nuxt/devtools',
+    '@nuxt/image',
+    'nuxt-prepare',
+    'nuxt-vitalizer',
+  ],
+
+  vite: {
+    esbuild: {
+      drop: process.env.DEBUG ? [] : ['console', 'debugger'],
+      tsconfigRaw: {
+        compilerOptions: {
+          experimentalDecorators: true,
+        },
+      },
+    },
+    ssr: {
+      // with ssr enabled, this config is required to load vuetify properly
+      noExternal: ['vuetify'],
+    },
+    server: {
+      https,
+      // @ts-expect-error J'ignore pourquoi cette erreur TS se produit, cette propriété est valide
+      port: 443,
+      hmr: {
+        protocol: 'wss',
+        port: 24678,
+      },
+    },
+  },
+
+  // Hide the sourcemaps warnings with vuetify
+  // @see https://github.com/vuetifyjs/vuetify-loader/issues/290#issuecomment-1435702713
+  sourcemap: {
+    server: false,
+    client: false,
+  },
+
+  i18n: {
+    langDir: 'lang',
+    lazy: true,
+    strategy: 'no_prefix',
+    locales: [
+      {
+        code: 'en',
+        iso: 'en-US',
+        file: 'en.json',
+        name: 'English',
+      },
+      {
+        code: 'fr',
+        iso: 'fr-FR',
+        file: 'fr.json',
+        name: 'Français',
+      },
+    ],
+    defaultLocale: 'fr',
+    detectBrowserLanguage: false,
+    vueI18n: './i18n.config.ts',
+  },
+
+  image: {
+    provider: 'none',
+  },
+
+  build: {
+    transpile,
+  },
+
+  ignore: [process.env.NUXT_ENV === 'prod' ? 'pages/dev/*' : ''],
+
+  prepare: {
+    scripts: ['prepare/buildIndex.ts'],
+  },
+
+  compatibilityDate: '2025-02-07',
+})

+ 412 - 305
pages/subscription.vue

@@ -5,120 +5,166 @@ Page 'Mon abonnement'
 -->
 -->
 <template>
 <template>
   <LayoutContainer>
   <LayoutContainer>
-      <v-expansion-panels v-model="openedPanels" :multiple="true">
-        <UiExpansionPanel title="subscription_page" icon="fas fa-info">
-          <v-container fluid class="container">
-            <v-row>
-              <v-col cols="12" lg="6" sm="12">
-                {{ $t('version') }} : <strong>{{ dolibarrAccount ? $t(dolibarrAccount.product) : '-' }}</strong>
-              </v-col>
-              <v-col cols="12" lg="6" sm="12">
-                {{ $t('client_id') }} : {{ dolibarrAccount ? dolibarrAccount.clientNumber : '-' }}
-              </v-col>
-            </v-row>
-          </v-container>
-        </UiExpansionPanel>
-
-        <UiExpansionPanel title="service_detail" icon="fas fa-info"
-                          v-if="dolibarrAccount && dolibarrAccount.contract"
-        >
-          <v-container fluid class="container">
-            <v-row>
-              <v-col cols="12"
-                     lg="12"
-                     v-for="line in dolibarrAccount.contract.lines"
-                     :key="line.id"
+    <v-expansion-panels v-model="openedPanels" :multiple="true">
+      <UiExpansionPanel title="subscription_page" icon="fas fa-info">
+        <v-container fluid class="container">
+          <v-row>
+            <v-col cols="12" lg="6" sm="12">
+              {{ $t('version') }} :
+              <strong>{{
+                dolibarrAccount ? $t(dolibarrAccount.product) : '-'
+              }}</strong>
+            </v-col>
+            <v-col cols="12" lg="6" sm="12">
+              {{ $t('client_id') }} :
+              {{ dolibarrAccount ? dolibarrAccount.clientNumber : '-' }}
+            </v-col>
+          </v-row>
+        </v-container>
+      </UiExpansionPanel>
+
+      <UiExpansionPanel
+        title="service_detail"
+        icon="fas fa-info"
+        v-if="dolibarrAccount && dolibarrAccount.contract"
+      >
+        <v-container fluid class="container">
+          <v-row>
+            <v-col
+              cols="12"
+              lg="12"
+              v-for="line in dolibarrAccount.contract.lines"
+              :key="line.id"
+            >
+              <strong>{{ line.serviceLabel }}</strong> - {{ $t('until') }} :
+              {{ $d(line.dateEnd) }}
+            </v-col>
+
+            <v-col cols="12" lg="12" v-if="ability.can('manage', 'texto')">
+              <strong>{{ $t('remaining_sms_credit') }}</strong> -
+              <span
+                v-if="
+                  !mobytPending && mobytStatus !== null && mobytStatus.active
+                "
               >
               >
-                <strong>{{ line.serviceLabel }}</strong> - {{ $t('until') }} : {{ $d(line.dateEnd) }}
-              </v-col>
-
-              <v-col cols="12" lg="12" v-if="ability.can('manage', 'texto')">
-                <strong>{{ $t('remaining_sms_credit') }}</strong> -
-                <span v-if="!mobytPending && mobytStatus !== null && mobytStatus.active">
-                        {{
-                    mobytStatus.money.toLocaleString($i18n.locale, {
-                      style: 'currency',
-                      currency: 'EUR',
-                    })
-                  }}
-                        {{
-                    i18n.t('convert_price_to_sms', {
-                      nb_sms: mobytStatus.amount,
-                    })
-                  }}
-                </span>
-              </v-col>
-            </v-row>
-          </v-container>
-        </UiExpansionPanel>
-
-        <UiExpansionPanel
-          v-if="showDolibarrPanel"
-          title="bills"
-          icon="fas fa-file"
-        >
-          <v-container :fluid="true" class="container">
-            <v-row>
-              <v-table v-if="dolibarrAccount !== null">
-                <thead>
-                  <tr>
-                    <th>{{ $t('reference') }}</th>
-                    <th>{{ $t('date') }}</th>
-                    <th>{{ $t('taxExcludedAmount') }}</th>
-                    <th>{{ $t('status') }}</th>
-                    <th></th>
-                  </tr>
-                </thead>
-                <tbody>
-                  <tr v-for="bill in dolibarrAccount.bills" :key="bill.id">
-                    <td>{{ bill.ref }}</td>
-                    <td>{{ $d(bill.date) }}</td>
-                    <td>
-                      {{
-                        bill.taxExcludedAmount.toLocaleString($i18n.locale, {
-                          style: 'currency',
-                          currency: 'EUR',
-                        })
-                      }}
-                    </td>
-                    <td>
-                      {{ bill.paid === true ? $t('paid') : $t('unpaid') }}
-                    </td>
-                    <td>
-                      <a @click="downloadDolibarrBill(bill.ref)" class="clickable">
-                        {{ $t('download') }}
-                      </a>
-                    </td>
-                  </tr>
-                </tbody>
-              </v-table>
-            </v-row>
-          </v-container>
-        </UiExpansionPanel>
-
-        <UiExpansionPanel title="opentalent_offers" icon="fas fa-plus" v-if="!organizationProfile.isManagerProduct">
-          <v-container fluid class="container">
-            <v-row class="offer_title" v-if="!md && mdAndUp && !organizationProfile.isSchool">
-              <v-col cols="12" :lg="organizationProfile.isArtistPremiumProduct ? 3 : 6" sm="12">
-                <span class="theme-artist">Pour les orchestres, chorales, compagnies et troupes artistiques</span>
-              </v-col>
-              <v-col cols="12" :lg="organizationProfile.isArtistPremiumProduct ? 3 : 5" sm="12" :offset="organizationProfile.isArtistPremiumProduct ? 0 : 1">
-                <span class="theme-school">Pour les établissements d'enseignements artistiques*</span>
-              </v-col>
-            </v-row>
-
-            <v-row class="card-container">
-              <v-col lg="3" sm="12" md="6" v-if="organizationProfile.isArtistProduct">
-                <LayoutPagesSubscriptionCard
+                {{
+                  mobytStatus.money.toLocaleString($i18n.locale, {
+                    style: 'currency',
+                    currency: 'EUR',
+                  })
+                }}
+                {{
+                  i18n.t('convert_price_to_sms', {
+                    nb_sms: mobytStatus.amount,
+                  })
+                }}
+              </span>
+            </v-col>
+          </v-row>
+        </v-container>
+      </UiExpansionPanel>
+
+      <UiExpansionPanel
+        v-if="showDolibarrPanel"
+        title="bills"
+        icon="fas fa-file"
+      >
+        <v-container :fluid="true" class="container">
+          <v-row>
+            <v-table v-if="dolibarrAccount !== null">
+              <thead>
+                <tr>
+                  <th>{{ $t('reference') }}</th>
+                  <th>{{ $t('date') }}</th>
+                  <th>{{ $t('taxExcludedAmount') }}</th>
+                  <th>{{ $t('status') }}</th>
+                  <th></th>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-for="bill in dolibarrAccount.bills" :key="bill.id">
+                  <td>{{ bill.ref }}</td>
+                  <td>{{ $d(bill.date) }}</td>
+                  <td>
+                    {{
+                      bill.taxExcludedAmount.toLocaleString($i18n.locale, {
+                        style: 'currency',
+                        currency: 'EUR',
+                      })
+                    }}
+                  </td>
+                  <td>
+                    {{ bill.paid === true ? $t('paid') : $t('unpaid') }}
+                  </td>
+                  <td>
+                    <a
+                      @click="downloadDolibarrBill(bill.ref)"
+                      class="clickable"
+                    >
+                      {{ $t('download') }}
+                    </a>
+                  </td>
+                </tr>
+              </tbody>
+            </v-table>
+          </v-row>
+        </v-container>
+      </UiExpansionPanel>
+
+      <UiExpansionPanel
+        title="opentalent_offers"
+        icon="fas fa-plus"
+        v-if="!organizationProfile.isManagerProduct"
+      >
+        <v-container fluid class="container">
+          <v-row
+            class="offer_title"
+            v-if="!md && mdAndUp && !organizationProfile.isSchool"
+          >
+            <v-col
+              cols="12"
+              :lg="organizationProfile.isArtistPremiumProduct ? 3 : 6"
+              sm="12"
+            >
+              <span class="theme-artist"
+                >Pour les orchestres, chorales, compagnies et troupes
+                artistiques</span
+              >
+            </v-col>
+            <v-col
+              cols="12"
+              :lg="organizationProfile.isArtistPremiumProduct ? 3 : 5"
+              sm="12"
+              :offset="organizationProfile.isArtistPremiumProduct ? 0 : 1"
+            >
+              <span class="theme-school"
+                >Pour les établissements d'enseignements artistiques*</span
+              >
+            </v-col>
+          </v-row>
+
+          <v-row class="card-container">
+            <v-col
+              lg="3"
+              sm="12"
+              md="6"
+              v-if="organizationProfile.isArtistProduct"
+            >
+              <LayoutPagesSubscriptionCard
                 class="artistCard"
                 class="artistCard"
                 title="Logiciel Artist Standard"
                 title="Logiciel Artist Standard"
-                :extraHeader="organizationProfile.isArtistProduct ? 'Votre version' : undefined"
+                :extraHeader="
+                  organizationProfile.isArtistProduct
+                    ? 'Votre version'
+                    : undefined
+                "
                 color="artist"
                 color="artist"
                 :list="listCheck.artist"
                 :list="listCheck.artist"
               >
               >
                 <template #card.subTitle>
                 <template #card.subTitle>
                   <div class="priceBlock">
                   <div class="priceBlock">
-                    <span class="price">{{formatCurrency(11, 'EUR')}}</span> TTC/mois
+                    <span class="price">{{ formatCurrency(11, 'EUR') }}</span>
+                    TTC/mois
                   </div>
                   </div>
                 </template>
                 </template>
                 <template #card.action>
                 <template #card.action>
@@ -128,180 +174,231 @@ Page 'Mon abonnement'
                     href="https://logiciels.opentalent.fr/opentalent-artist"
                     href="https://logiciels.opentalent.fr/opentalent-artist"
                     target="_blank"
                     target="_blank"
                   >
                   >
-                    {{ $t('to_know_more') }} <i class="fa-solid fa-greater-than small"></i>
+                    {{ $t('to_know_more') }}
+                    <i class="fa-solid fa-greater-than small"></i>
                   </v-btn>
                   </v-btn>
                 </template>
                 </template>
               </LayoutPagesSubscriptionCard>
               </LayoutPagesSubscriptionCard>
-              </v-col>
-
-              <v-col lg="3" sm="12" md="6" v-if="organizationProfile.isArtist">
-                <LayoutPagesSubscriptionCard
-                  class="artistCard"
-                  title="Logiciel Artist Premium*"
-                  :extraHeader="organizationProfile.isArtistPremiumProduct ? (organizationProfile.isTrialActive ? `Version en cours d'essai J-${organizationProfile.trialCountDown}` : 'Votre version') : '1 mois d\'essai offert'"
-                  color="artist"
-                  :list="listCheck.artistPremium"
-                >
-                  <template #card.subTitle>
-                    <div class="priceBlock">
-                      <span class="price">{{organizationProfile.isCmf ? formatCurrency(7.5, 'EUR') : formatCurrency(18.0, 'EUR')}}</span> TTC/mois
-                    </div>
-                  </template>
-                  <template #card.action>
-                    <v-row >
-                      <v-col cols="12" v-if="!organizationProfile.isArtistPremiumProduct && (accessProfileStore.isAdmin || accessProfileStore.isCaMember)">
-                        <v-btn
-                          class="btn trialBtn"
-                          @click="startTrial"
-                        >
-                          {{ $t('try_premium_version') }} <i class="fa-solid fa-greater-than small"></i>
-                        </v-btn>
-                      </v-col>
-                      <v-col cols="12" v-if="(!organizationProfile.isArtistPremiumProduct || organizationProfile.isTrialActive) && (accessProfileStore.isAdmin || accessProfileStore.isCaMember)">
-                        <v-btn
-                          class="theme-artist btn"
-                          @click="subscription"
-                        >
-                          {{ $t('subscribe_to_the_offer') }} <i class="fa-solid fa-greater-than small"></i>
-                        </v-btn>
-                      </v-col>
-
-                      <v-col cols="12" v-if="organizationProfile.isTrialActive && (accessProfileStore.isAdmin || accessProfileStore.isCaMember)">
-                        <v-btn
-                          class="stop_btn"
-                          @click="showStopTrialDialog"
-                        >
-                          {{ $t('stop_trial') }}
-                        </v-btn>
-                      </v-col>
-                      <v-col cols="12">
-                          <span class="special_conditions">
-                          *Convient aux petites écoles sans besoins spécifiques de gestion pédagogique, de facturation, etc.
-                          Pour une solution complète optez pour Opentalent School
-                        </span>
-                      </v-col>
-                    </v-row>
-                  </template>
-                </LayoutPagesSubscriptionCard>
-              </v-col>
-
-              <v-col lg="3" sm="12" md="6" :offset="!md && mdAndUp ? (organizationProfile.isSchool ? 4 : (organizationProfile.isArtistPremiumProduct ? 0 : 2)) : 0">
-                <LayoutPagesSubscriptionCard
-                  class="schoolCard"
-                  :title="!organizationProfile.isSchool ? 'Logiciel School Standard / Premium' : (
-                    organizationProfile.isSchoolPremiumProduct ? 'Logiciel School Premium' : 'Logiciel School Standard'
-                  )"
-                  :subTitle="!organizationProfile.isSchool ? 'Sur devis' : ''"
-                  :extraHeader="organizationProfile.isSchool ? 'Votre version' : undefined"
-                  color="school"
-                  :list="listCheck.school"
-                >
-                  <template #card.action>
-                    <v-row>
-                      <v-col cols="12">
-                        <v-btn
-                          v-if="!organizationProfile.isSchool"
-                          class="theme-school btn"
-                          href="https://logiciels.opentalent.fr/opentalent-school"
-                          target="_blank"
-                        >
-                          {{ $t('to_know_more') }} <i class="fa-solid fa-greater-than small"></i>
-                        </v-btn>
-                      </v-col>
-                      <v-col cols="12">
-                          <span class="special_conditions">
-                          *Extranet disponible uniquement dans la version Opentalent School Premium
-                        </span>
-                      </v-col>
-                    </v-row>
-                  </template>
-                </LayoutPagesSubscriptionCard>
-              </v-col>
-            </v-row>
-          </v-container>
-        </UiExpansionPanel>
-
-        <UiExpansionPanel title="opentalent_options" icon="fas fa-plus">
-          <v-container fluid class="container card-container">
-            <v-row cols="12" >
-              <v-col lg="3" sm="12" md="6">
-                <LayoutPagesSubscriptionCard
-                  class="optionsCard"
-                  title="SMS"
-                  sub-title="Option payante"
-                  color="primary"
-                  :list="listCheck.sms"
-                >
-                  <template #card.action>
-                    <v-btn
-                      class="theme-primary btn"
-                      :href="
-                          runtimeConfig.public.fileStorageBaseUrl +
-                          (organizationProfile.isCmf ? '/Bon_de_commande/Achat_SMS_CMF.pdf' : '/Bon_de_commande/Achat_SMS_Public.pdf')
-                        "
-                      target="_blank"
+            </v-col>
+
+            <v-col lg="3" sm="12" md="6" v-if="organizationProfile.isArtist">
+              <LayoutPagesSubscriptionCard
+                class="artistCard"
+                title="Logiciel Artist Premium*"
+                :extraHeader="
+                  organizationProfile.isArtistPremiumProduct
+                    ? organizationProfile.isTrialActive
+                      ? `Version en cours d'essai J-${organizationProfile.trialCountDown}`
+                      : 'Votre version'
+                    : '1 mois d\'essai offert'
+                "
+                color="artist"
+                :list="listCheck.artistPremium"
+              >
+                <template #card.subTitle>
+                  <div class="priceBlock">
+                    <span class="price">{{
+                      organizationProfile.isCmf
+                        ? formatCurrency(7.5, 'EUR')
+                        : formatCurrency(18.0, 'EUR')
+                    }}</span>
+                    TTC/mois
+                  </div>
+                </template>
+                <template #card.action>
+                  <v-row>
+                    <v-col
+                      cols="12"
+                      v-if="
+                        !organizationProfile.isArtistPremiumProduct &&
+                        (accessProfileStore.isAdmin ||
+                          accessProfileStore.isCaMember)
+                      "
                     >
                     >
-                      acheter des credits SMS <i class="fa-solid fa-greater-than small"></i>
-                    </v-btn>
-                  </template>
-                </LayoutPagesSubscriptionCard>
-              </v-col>
-
-              <v-col lg="3" sm="12" md="6">
-                <LayoutPagesSubscriptionCard
-                  class="optionsCard"
-                  title="Nom de domaine"
-                  sub-title="Option payante"
-                  color="primary"
-                  :list="listCheck.domain"
-                >
-                  <template #card.action>
-                    <v-btn
-                      class="theme-primary btn"
-                      :href="
-                          runtimeConfig.public.fileStorageBaseUrl +
-                          '/Bon_de_commande/Nom_de_domaine.pdf'
-                        "
-                      target="_blank"
+                      <v-btn class="btn trialBtn" @click="startTrial">
+                        {{ $t('try_premium_version') }}
+                        <i class="fa-solid fa-greater-than small"></i>
+                      </v-btn>
+                    </v-col>
+                    <v-col
+                      cols="12"
+                      v-if="
+                        (!organizationProfile.isArtistPremiumProduct ||
+                          organizationProfile.isTrialActive) &&
+                        (accessProfileStore.isAdmin ||
+                          accessProfileStore.isCaMember)
+                      "
                     >
                     >
-                      souscrire à l'option <i class="fa-solid fa-greater-than small"></i>
-                    </v-btn>
-                  </template>
-                </LayoutPagesSubscriptionCard>
-              </v-col>
-            </v-row>
-          </v-container>
-        </UiExpansionPanel>
-
-      </v-expansion-panels>
-
+                      <v-btn class="theme-artist btn" @click="subscription">
+                        {{ $t('subscribe_to_the_offer') }}
+                        <i class="fa-solid fa-greater-than small"></i>
+                      </v-btn>
+                    </v-col>
+
+                    <v-col
+                      cols="12"
+                      v-if="
+                        organizationProfile.isTrialActive &&
+                        (accessProfileStore.isAdmin ||
+                          accessProfileStore.isCaMember)
+                      "
+                    >
+                      <v-btn class="stop_btn" @click="showStopTrialDialog">
+                        {{ $t('stop_trial') }}
+                      </v-btn>
+                    </v-col>
+                    <v-col cols="12">
+                      <span class="special_conditions">
+                        *Convient aux petites écoles sans besoins spécifiques de
+                        gestion pédagogique, de facturation, etc. Pour une
+                        solution complète optez pour Opentalent School
+                      </span>
+                    </v-col>
+                  </v-row>
+                </template>
+              </LayoutPagesSubscriptionCard>
+            </v-col>
+
+            <v-col
+              lg="3"
+              sm="12"
+              md="6"
+              :offset="
+                !md && mdAndUp
+                  ? organizationProfile.isSchool
+                    ? 4
+                    : organizationProfile.isArtistPremiumProduct
+                      ? 0
+                      : 2
+                  : 0
+              "
+            >
+              <LayoutPagesSubscriptionCard
+                class="schoolCard"
+                :title="
+                  !organizationProfile.isSchool
+                    ? 'Logiciel School Standard / Premium'
+                    : organizationProfile.isSchoolPremiumProduct
+                      ? 'Logiciel School Premium'
+                      : 'Logiciel School Standard'
+                "
+                :subTitle="!organizationProfile.isSchool ? 'Sur devis' : ''"
+                :extraHeader="
+                  organizationProfile.isSchool ? 'Votre version' : undefined
+                "
+                color="school"
+                :list="listCheck.school"
+              >
+                <template #card.action>
+                  <v-row>
+                    <v-col cols="12">
+                      <v-btn
+                        v-if="!organizationProfile.isSchool"
+                        class="theme-school btn"
+                        href="https://logiciels.opentalent.fr/opentalent-school"
+                        target="_blank"
+                      >
+                        {{ $t('to_know_more') }}
+                        <i class="fa-solid fa-greater-than small"></i>
+                      </v-btn>
+                    </v-col>
+                    <v-col cols="12">
+                      <span class="special_conditions">
+                        *Extranet disponible uniquement dans la version
+                        Opentalent School Premium
+                      </span>
+                    </v-col>
+                  </v-row>
+                </template>
+              </LayoutPagesSubscriptionCard>
+            </v-col>
+          </v-row>
+        </v-container>
+      </UiExpansionPanel>
+
+      <UiExpansionPanel title="opentalent_options" icon="fas fa-plus">
+        <v-container fluid class="container card-container">
+          <v-row cols="12">
+            <v-col lg="3" sm="12" md="6">
+              <LayoutPagesSubscriptionCard
+                class="optionsCard"
+                title="SMS"
+                sub-title="Option payante"
+                color="primary"
+                :list="listCheck.sms"
+              >
+                <template #card.action>
+                  <v-btn
+                    class="theme-primary btn"
+                    :href="
+                      runtimeConfig.public.fileStorageBaseUrl +
+                      (organizationProfile.isCmf
+                        ? '/Bon_de_commande/Achat_SMS_CMF.pdf'
+                        : '/Bon_de_commande/Achat_SMS_Public.pdf')
+                    "
+                    target="_blank"
+                  >
+                    acheter des credits SMS
+                    <i class="fa-solid fa-greater-than small"></i>
+                  </v-btn>
+                </template>
+              </LayoutPagesSubscriptionCard>
+            </v-col>
+
+            <v-col lg="3" sm="12" md="6">
+              <LayoutPagesSubscriptionCard
+                class="optionsCard"
+                title="Nom de domaine"
+                sub-title="Option payante"
+                color="primary"
+                :list="listCheck.domain"
+              >
+                <template #card.action>
+                  <v-btn
+                    class="theme-primary btn"
+                    :href="
+                      runtimeConfig.public.fileStorageBaseUrl +
+                      '/Bon_de_commande/Nom_de_domaine.pdf'
+                    "
+                    target="_blank"
+                  >
+                    souscrire à l'option
+                    <i class="fa-solid fa-greater-than small"></i>
+                  </v-btn>
+                </template>
+              </LayoutPagesSubscriptionCard>
+            </v-col>
+          </v-row>
+        </v-container>
+      </UiExpansionPanel>
+    </v-expansion-panels>
   </LayoutContainer>
   </LayoutContainer>
 
 
   <LayoutDialogTrialAlreadyDid
   <LayoutDialogTrialAlreadyDid
     :show="showDialogTrialAllReadyDid"
     :show="showDialogTrialAllReadyDid"
-    @closeDialog = "showDialogTrialAllReadyDid = false"
+    @closeDialog="showDialogTrialAllReadyDid = false"
   />
   />
 
 
   <LayoutDialogTrialStopConfirmation
   <LayoutDialogTrialStopConfirmation
     :show="showDialogTrialStopConfirmation"
     :show="showDialogTrialStopConfirmation"
-    @closeDialog = "showDialogTrialStopConfirmation = false"
-    @stopTrial = "stopTrial"
+    @closeDialog="showDialogTrialStopConfirmation = false"
+    @stopTrial="stopTrial"
   />
   />
 </template>
 </template>
 
 
 <script setup lang="ts">
 <script setup lang="ts">
-import {useAbility} from '@casl/vue'
-import type {Ref} from 'vue'
-import {useDisplay} from 'vuetify'
-import type {AsyncData} from '#app'
-import {useOrganizationProfileStore} from '~/stores/organizationProfile'
-import {useEntityFetch} from '~/composables/data/useEntityFetch'
+import { useAbility } from '@casl/vue'
+import type { Ref } from 'vue'
+import { useDisplay } from 'vuetify'
+import type { AsyncData } from '#app'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import DolibarrAccount from '~/models/Organization/DolibarrAccount'
 import DolibarrAccount from '~/models/Organization/DolibarrAccount'
 import MobytUserStatus from '~/models/Organization/MobytUserStatus'
 import MobytUserStatus from '~/models/Organization/MobytUserStatus'
-import UrlUtils from '~/services/utils/urlUtils';
-import {useDownloadFromRoute} from '~/composables/utils/useDownloadFromRoute';
-import {useApiLegacyRequestService} from "~/composables/data/useApiLegacyRequestService";
-import {usePageStore} from "~/stores/page";
+import UrlUtils from '~/services/utils/urlUtils'
+import { useDownloadFromRoute } from '~/composables/utils/useDownloadFromRoute'
+import { useApiLegacyRequestService } from '~/composables/data/useApiLegacyRequestService'
+import { usePageStore } from '~/stores/page'
 
 
 //meta
 //meta
 definePageMeta({
 definePageMeta({
@@ -311,8 +408,8 @@ definePageMeta({
 //Get composables
 //Get composables
 const ability = useAbility()
 const ability = useAbility()
 const runtimeConfig = useRuntimeConfig()
 const runtimeConfig = useRuntimeConfig()
-const {mdAndUp, md} = useDisplay()
-const {fetch} = useEntityFetch()
+const { mdAndUp, md } = useDisplay()
+const { fetch } = useEntityFetch()
 const i18n = useI18n()
 const i18n = useI18n()
 const { apiRequestService } = useApiLegacyRequestService()
 const { apiRequestService } = useApiLegacyRequestService()
 
 
@@ -324,7 +421,7 @@ const organizationProfile = getOrganizationProfile()
 const accessProfileStore = useAccessProfileStore()
 const accessProfileStore = useAccessProfileStore()
 const { mobytStatus, mobytPending } = getMobytInformations()
 const { mobytStatus, mobytPending } = getMobytInformations()
 
 
-const {data: dolibarrAccount, pending: dolibarrPending} = fetch(
+const { data: dolibarrAccount, pending: dolibarrPending } = fetch(
   DolibarrAccount,
   DolibarrAccount,
   organizationProfile.id,
   organizationProfile.id,
 )
 )
@@ -343,14 +440,14 @@ const formatCurrency = (value: number, currency: string): string => {
 }
 }
 
 
 const listCheck: Record<string, Array<string>> = {
 const listCheck: Record<string, Array<string>> = {
-  'artist' : [
+  artist: [
     '100 Mo de stockage',
     '100 Mo de stockage',
     '75 comptes utilisateurs',
     '75 comptes utilisateurs',
     'Gestion de la structure',
     'Gestion de la structure',
     'Site internet restreint',
     'Site internet restreint',
     'Options disponibles',
     'Options disponibles',
   ],
   ],
-  'artistPremium' : [
+  artistPremium: [
     '1Go de stockage',
     '1Go de stockage',
     '150 comptes utilisateurs',
     '150 comptes utilisateurs',
     'Gestion de la structure',
     'Gestion de la structure',
@@ -358,7 +455,7 @@ const listCheck: Record<string, Array<string>> = {
     'Site internet illimité',
     'Site internet illimité',
     'Options disponibles',
     'Options disponibles',
   ],
   ],
-  'school' : [
+  school: [
     '500 Mo ou 1Go de stockage',
     '500 Mo ou 1Go de stockage',
     '3 comptes administrateurs',
     '3 comptes administrateurs',
     'Extranet élèves, tuteurs, professeurs*',
     'Extranet élèves, tuteurs, professeurs*',
@@ -367,14 +464,14 @@ const listCheck: Record<string, Array<string>> = {
     'Site internet complet',
     'Site internet complet',
     'Options adaptées à chaque structure',
     'Options adaptées à chaque structure',
   ],
   ],
-  'sms': [
+  sms: [
     'Envoyez des SMS depuis votre logiciel',
     'Envoyez des SMS depuis votre logiciel',
-    'Choisissez le nombre de crédits'
+    'Choisissez le nombre de crédits',
   ],
   ],
-  'domain':[
+  domain: [
     'Bénéficiez de votre propre nom de domaine',
     'Bénéficiez de votre propre nom de domaine',
-    'Et d\'une adresse mail personnalisée'
-  ]
+    "Et d'une adresse mail personnalisée",
+  ],
 }
 }
 
 
 /**
 /**
@@ -385,7 +482,13 @@ function initPanel(): Ref<Array<string>> {
   // TODO: quand le bug ci dessus est résolu, remplacer par `const openedPanels: Ref<Array<string>> = ref([...])`
   // TODO: quand le bug ci dessus est résolu, remplacer par `const openedPanels: Ref<Array<string>> = ref([...])`
   const openedPanels: Ref<Array<string>> = ref([])
   const openedPanels: Ref<Array<string>> = ref([])
   onMounted(() => {
   onMounted(() => {
-    openedPanels.value = ['subscription_page', 'service_detail', 'bills', 'opentalent_offers', 'opentalent_options']
+    openedPanels.value = [
+      'subscription_page',
+      'service_detail',
+      'bills',
+      'opentalent_offers',
+      'opentalent_options',
+    ]
   })
   })
   return openedPanels
   return openedPanels
 }
 }
@@ -404,12 +507,15 @@ function getOrganizationProfile() {
 /**
 /**
  * Récupération des informations Mobyt
  * Récupération des informations Mobyt
  */
  */
-function getMobytInformations(): { mobytStatus: Ref<MobytUserStatus | null>; mobytPending: Ref<boolean> } {
+function getMobytInformations(): {
+  mobytStatus: Ref<MobytUserStatus | null>
+  mobytPending: Ref<boolean>
+} {
   let mobytStatus: Ref<MobytUserStatus | null> = ref(null)
   let mobytStatus: Ref<MobytUserStatus | null> = ref(null)
   let mobytPending: Ref<boolean> = ref(false)
   let mobytPending: Ref<boolean> = ref(false)
 
 
   if (ability.can('manage', 'texto')) {
   if (ability.can('manage', 'texto')) {
-    const {data, pending} = fetch(
+    const { data, pending } = fetch(
       MobytUserStatus,
       MobytUserStatus,
       organizationProfile!.id!,
       organizationProfile!.id!,
     ) as AsyncData<MobytUserStatus | null, Error | null>
     ) as AsyncData<MobytUserStatus | null, Error | null>
@@ -420,18 +526,19 @@ function getMobytInformations(): { mobytStatus: Ref<MobytUserStatus | null>; mob
   return { mobytStatus, mobytPending }
   return { mobytStatus, mobytPending }
 }
 }
 
 
-
 /**
 /**
  * Action lorsque l'on souhaite démarrer l'essai
  * Action lorsque l'on souhaite démarrer l'essai
  */
  */
-async function startTrial(){
-  try{
+async function startTrial() {
+  try {
     await apiRequestService.get('/trial/is_available')
     await apiRequestService.get('/trial/is_available')
-    const v1BaseURL = runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
+    const v1BaseURL =
+      runtimeConfig.baseUrlAdminLegacy ||
+      runtimeConfig.public.baseUrlAdminLegacy
     await navigateTo(UrlUtils.join(v1BaseURL, '#', 'trial'), {
     await navigateTo(UrlUtils.join(v1BaseURL, '#', 'trial'), {
-      external: true
+      external: true,
     })
     })
-  }catch(error){
+  } catch (error) {
     showDialogTrialAllReadyDid.value = true
     showDialogTrialAllReadyDid.value = true
   }
   }
 }
 }
@@ -439,17 +546,18 @@ async function startTrial(){
 /**
 /**
  * Action lorsque l'on souhaite souscrire à artist premium
  * Action lorsque l'on souhaite souscrire à artist premium
  */
  */
-async function subscription(){
-  const v1BaseURL = runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
+async function subscription() {
+  const v1BaseURL =
+    runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
   await navigateTo(UrlUtils.join(v1BaseURL, '#', 'subscribe'), {
   await navigateTo(UrlUtils.join(v1BaseURL, '#', 'subscribe'), {
-    external: true
+    external: true,
   })
   })
 }
 }
 
 
 /**
 /**
  * Action lorsque l'on souhaite afficher la modal de confirmation pour stopper
  * Action lorsque l'on souhaite afficher la modal de confirmation pour stopper
  */
  */
-function showStopTrialDialog(){
+function showStopTrialDialog() {
   showDialogTrialStopConfirmation.value = true
   showDialogTrialStopConfirmation.value = true
 }
 }
 
 
@@ -459,13 +567,13 @@ function showStopTrialDialog(){
 async function stopTrial() {
 async function stopTrial() {
   usePageStore().loading = true
   usePageStore().loading = true
   await apiRequestService.post('/trial/stop')
   await apiRequestService.post('/trial/stop')
-  const v1BaseURL = runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
+  const v1BaseURL =
+    runtimeConfig.baseUrlAdminLegacy || runtimeConfig.public.baseUrlAdminLegacy
   await navigateTo(UrlUtils.join(v1BaseURL, '#', 'dashboard'), {
   await navigateTo(UrlUtils.join(v1BaseURL, '#', 'dashboard'), {
-    external: true
+    external: true,
   })
   })
 }
 }
 
 
-
 const downloadDolibarrBill = (ref: string): void => {
 const downloadDolibarrBill = (ref: string): void => {
   const route = UrlUtils.join('api/dolibarr/download/invoice', ref)
   const route = UrlUtils.join('api/dolibarr/download/invoice', ref)
 
 
@@ -473,15 +581,14 @@ const downloadDolibarrBill = (ref: string): void => {
 }
 }
 </script>
 </script>
 
 
-
 <style scoped lang="scss">
 <style scoped lang="scss">
 .clickable {
 .clickable {
   cursor: pointer;
   cursor: pointer;
   text-decoration: underline;
   text-decoration: underline;
 }
 }
 
 
-.offer_title{
-  span{
+.offer_title {
+  span {
     border-radius: 5px;
     border-radius: 5px;
     display: block;
     display: block;
     font-weight: bold;
     font-weight: bold;
@@ -490,60 +597,60 @@ const downloadDolibarrBill = (ref: string): void => {
   }
   }
 }
 }
 
 
-.card-container{
+.card-container {
   .v-row {
   .v-row {
     display: -webkit-box;
     display: -webkit-box;
     display: -webkit-flex;
     display: -webkit-flex;
     display: -ms-flexbox;
     display: -ms-flexbox;
-    display:  flex;
+    display: flex;
     flex-wrap: wrap;
     flex-wrap: wrap;
   }
   }
   .v-row > [class*='v-col-'] {
   .v-row > [class*='v-col-'] {
     display: flex;
     display: flex;
     flex-direction: column;
     flex-direction: column;
   }
   }
-  .small{
-    font-size:6px;
+  .small {
+    font-size: 6px;
     padding-top: 5px;
     padding-top: 5px;
     padding-left: 5px;
     padding-left: 5px;
   }
   }
-  .priceBlock{
+  .priceBlock {
     text-align: center;
     text-align: center;
     font-size: 15px;
     font-size: 15px;
     font-weight: normal;
     font-weight: normal;
-    .price{
+    .price {
       font-size: 30px;
       font-size: 30px;
       font-weight: bold;
       font-weight: bold;
     }
     }
   }
   }
 }
 }
 
 
-.artistCard{
-  :deep(.v-card-title){
+.artistCard {
+  :deep(.v-card-title) {
     text-align: center;
     text-align: center;
   }
   }
 }
 }
 
 
-.special_conditions{
+.special_conditions {
   font-size: 10px;
   font-size: 10px;
 }
 }
 
 
-.trialBtn{
+.trialBtn {
   color: #000;
   color: #000;
   border: 1px solid rgb(var(--v-theme-artist));
   border: 1px solid rgb(var(--v-theme-artist));
 }
 }
 
 
-.optionsCard{
-  :deep(.margin-sup){
+.optionsCard {
+  :deep(.margin-sup) {
     margin-top: 0;
     margin-top: 0;
   }
   }
 }
 }
 
 
-.stop_btn{
+.stop_btn {
   color: rgb(var(--v-theme-danger));
   color: rgb(var(--v-theme-danger));
 }
 }
 
 
-.plus_btn{
+.plus_btn {
   color: rgb(var(--v-theme-on-neutral));
   color: rgb(var(--v-theme-on-neutral));
 }
 }
 </style>
 </style>

+ 16 - 14
services/data/normalizer/hydraNormalizer.ts

@@ -1,7 +1,7 @@
 import * as _ from 'lodash-es'
 import * as _ from 'lodash-es'
-import type {AnyJson, ApiResponse, HydraMetadata} from '~/types/data'
+import type { AnyJson, ApiResponse, HydraMetadata } from '~/types/data'
 import UrlUtils from '~/services/utils/urlUtils'
 import UrlUtils from '~/services/utils/urlUtils'
-import {METADATA_TYPE} from '~/types/enum/data'
+import { METADATA_TYPE } from '~/types/enum/data'
 import models from '~/models/models'
 import models from '~/models/models'
 import ApiResource from '~/models/ApiResource'
 import ApiResource from '~/models/ApiResource'
 
 
@@ -76,7 +76,7 @@ class HydraNormalizer {
   protected static getMetadata(data: AnyJson): HydraMetadata {
   protected static getMetadata(data: AnyJson): HydraMetadata {
     if (data['@type'] !== 'hydra:Collection') {
     if (data['@type'] !== 'hydra:Collection') {
       // A single item, no metadata
       // A single item, no metadata
-      return {type: METADATA_TYPE.ITEM}
+      return { type: METADATA_TYPE.ITEM }
     }
     }
 
 
     const metadata: HydraMetadata = {
     const metadata: HydraMetadata = {
@@ -163,15 +163,14 @@ class HydraNormalizer {
 
 
     throw new Error(
     throw new Error(
       'De-normalization error : Could not determine the type of the entity ' +
       'De-normalization error : Could not determine the type of the entity ' +
-      item['@id'] +
-      ' (found: ' +
-      entity +
-      ')',
+        item['@id'] +
+        ' (found: ' +
+        entity +
+        ')',
     )
     )
   }
   }
 
 
   protected static denormalizeEntity(model: typeof ApiResource, item: AnyJson) {
   protected static denormalizeEntity(model: typeof ApiResource, item: AnyJson) {
-
     item['id'] = this.getItemIdValue(model, item)
     item['id'] = this.getItemIdValue(model, item)
 
 
     // eslint-disable-next-line new-cap
     // eslint-disable-next-line new-cap
@@ -235,14 +234,14 @@ class HydraNormalizer {
     iri: string,
     iri: string,
     expectedEntity: string,
     expectedEntity: string,
   ): number | string {
   ): number | string {
-    const {entity, id} = HydraNormalizer.parseEntityIRI(iri)
+    const { entity, id } = HydraNormalizer.parseEntityIRI(iri)
     if (entity !== expectedEntity) {
     if (entity !== expectedEntity) {
       throw new Error(
       throw new Error(
         "IRI entity does not match the field's target entity (" +
         "IRI entity does not match the field's target entity (" +
-        entity +
-        ' != ' +
-        expectedEntity +
-        ')',
+          entity +
+          ' != ' +
+          expectedEntity +
+          ')',
       )
       )
     }
     }
     return id
     return id
@@ -259,7 +258,10 @@ class HydraNormalizer {
       return item.id
       return item.id
     }
     }
 
 
-    if (model.getIdField() !== undefined && item[model.getIdField()] !== undefined) {
+    if (
+      model.getIdField() !== undefined &&
+      item[model.getIdField()] !== undefined
+    ) {
       return item[model.getIdField()]
       return item[model.getIdField()]
     }
     }