Browse Source

upgrade vuetify, add the subscription page

Olivier Massot 2 years ago
parent
commit
4602286a20

+ 8 - 8
components/Layout/AlertBar/Cotisation.vue

@@ -7,36 +7,36 @@ Barre d'alerte qui s'affiche pour donner l'état de la cotisation
 <template>
   <main>
     <!-- TODO : fonctionnement à valider -->
-    <UiSystemBar v-if="showCotisationAccess" background-color="ot-info" text-color="ot-white">
+    <UiSystemBar v-if="showCotisationAccess" background-color="ot-info">
       <template #bar.text>
-        <a @click="goOn('AFFILIATION')">
+        <a @click="goOn('AFFILIATION')" class="text-ot-white">
           <v-icon small>fas fa-info-circle</v-icon>
           {{$t('cotisation_access')}}
         </a>
       </template>
     </UiSystemBar>
 
-    <UiSystemBar v-else-if="showUploadInvoice" background-color="ot-info" text-color="ot-white">
+    <UiSystemBar v-else-if="showUploadInvoice" background-color="ot-info">
       <template #bar.text>
-        <a @click="goOn('INVOICE')">
+        <a @click="goOn('INVOICE')" class="text-ot-white">
           <v-icon small>fas fa-info-circle</v-icon>
           {{$t('upload_cotisation_invoice')}}
         </a>
       </template>
     </UiSystemBar>
 
-    <UiSystemBar v-else-if="showRenewInsurance" background-color="ot-info" text-color="ot-white">
+    <UiSystemBar v-else-if="showRenewInsurance" background-color="ot-info">
       <template #bar.text>
-        <a @click="goOn('INSURANCE')">
+        <a @click="goOn('INSURANCE')" class="text-ot-white">
           <v-icon small>fas fa-info-circle</v-icon>
           {{$t('renew_insurance_cmf')}}
         </a>
       </template>
     </UiSystemBar>
 
-    <UiSystemBar v-else-if="showInsuranceSubscription" background-color="ot-info" text-color="ot-white">
+    <UiSystemBar v-else-if="showInsuranceSubscription" background-color="ot-info">
       <template #bar.text>
-        <a @click="goOn('ADVERTISINGINSURANCE')">
+        <a @click="goOn('ADVERTISINGINSURANCE')" class="text-ot-white">
           <v-icon small>fas fa-info-circle</v-icon>
           {{$t('insurance_cmf_subscription')}}
         </a>

+ 30 - 10
components/Ui/ExpansionPanel.vue

@@ -5,22 +5,25 @@ Panneaux déroulants de type "accordéon"
 -->
 
 <template>
-  <v-expansion-panel :id="id">
-    <v-expansion-panel-header color="ot-light_grey">
-      <v-icon class="ot-white--text ot-green icon">
-        {{ icon }}
-      </v-icon>
-      {{ $t(id) }}
-    </v-expansion-panel-header>
-    <v-expansion-panel-content>
+  <v-expansion-panel>
+    <v-expansion-panel-title color="ot-light-grey">
+      <template v-slot:default="{ expanded }">
+        <v-icon class="text-ot-white bg-ot-green icon">
+          {{ icon }}
+        </v-icon>
+        {{ $t(title) }}
+      </template>
+    </v-expansion-panel-title>
+
+    <v-expansion-panel-text>
       <slot />
-    </v-expansion-panel-content>
+    </v-expansion-panel-text>
   </v-expansion-panel>
 </template>
 
 <script setup lang="ts">
 const props = defineProps({
-  id: {
+  title: {
     type: String,
     required: true
   },
@@ -47,4 +50,21 @@ const props = defineProps({
   .v-expansion-panel--active > .v-expansion-panel-header{
     min-height: 47px;
   }
+
+  .v-expansion-panel-title {
+    padding-left: 0;
+    padding-top: 0;
+    padding-bottom: 0;
+    max-height: 47px;
+    min-height: 47px;
+  }
+
+  :deep(.v-expansion-panel-title__icon > .v-icon) {
+    font-size: 16px;
+    color: #707070;
+  }
+
+  .icon {
+    text-align: center;
+  }
 </style>

+ 32 - 0
layouts/error.vue

@@ -0,0 +1,32 @@
+<template>
+  <v-app dark>
+    <h1 v-if="error.statusCode !== 404">
+      {{ otherError }}
+    </h1>
+  </v-app>
+</template>
+
+<script setup lang="ts">
+  import {navigateTo} from "#app";
+  import Url from "~/services/utils/url";
+
+  const props = defineProps({
+    error: {
+      type: Object,
+      default: null
+    }
+  })
+
+  if(process.client && props.error.statusCode === 404 && process.env.NODE_ENV === 'production') {
+    const runtimeConfig = useRuntimeConfig()
+    navigateTo(Url.join(runtimeConfig.baseUrlAdminLegacy, 'dashboard'), {external: true})
+  }
+
+  const otherError = ref('Une erreur est parvenue')
+</script>
+
+<style scoped>
+h1 {
+  font-size: 20px;
+}
+</style>

+ 29 - 0
models/Organization/DolibarrAccount.ts

@@ -0,0 +1,29 @@
+import ApiResource from "~/models/ApiResource";
+import {Attr, Num, Str, Uid} from "pinia-orm/dist/decorators";
+
+/**
+ * The Dolibarr account of an organization
+ *
+ * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/ApiResources/Dolibarr/DolibarrAccount.php
+ */
+export class DolibarrAccount extends ApiResource {
+    static entity = 'dolibarr/account'
+
+    @Uid()
+    declare id: number | string | null
+
+    @Num(0, { notNullable: true })
+    declare organizationId: number
+
+    @Str(null)
+    declare clientNumber: string
+
+    @Str(null)
+    declare product: string
+
+    @Attr({})
+    declare contract: object
+
+    @Attr([])
+    declare bills: Array<object>
+}

+ 29 - 0
models/Organization/MobytUserStatus.ts

@@ -0,0 +1,29 @@
+import ApiResource from "~/models/ApiResource";
+import {Bool, Num, Uid} from "pinia-orm/dist/decorators";
+
+
+/**
+ * The Mobyt user status of an organization
+ *
+ * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/ApiResources/Mobyt/MobytUserStatus.php
+ */
+export class MobytUserStatus extends ApiResource {
+    static entity = 'mobyt/status'
+
+    @Uid()
+    declare id: number | string | null
+
+    @Num(0, { notNullable: true })
+    declare organizationId: number
+
+    @Bool(false, { notNullable: true })
+    declare active: boolean
+
+    @Num(0, { notNullable: true })
+    declare amount: number
+
+    @Num(0, { notNullable: true })
+    declare money: number
+}
+
+

+ 34 - 1
nuxt.config.ts

@@ -28,7 +28,19 @@ export default defineNuxtConfig({
             baseUrlTypo3: '',
             baseUrlMercure: '',
             supportUrl: '',
+            school_product: 'school',
+            school_premium_product: 'school-premium',
+            artist_product: 'artist',
+            artist_premium_product: 'artist-premium',
+            manager_product: 'manager',
+            cmf_network: 'CMF',
+            ffec_network: 'FFEC',
+            OPENTALENT_MANAGER_ID: 93931,
+            CMF_ID: 12097
         }
+    },
+    env: {
+
     },
     hooks: {
       'builder:watch': console.log
@@ -98,7 +110,27 @@ export default defineNuxtConfig({
         detectBrowserLanguage: false,
         vueI18n: {
             legacy: false,
-        }
+            datetimeFormats: {
+                'fr-FR': {
+                    short: {
+                        year: 'numeric', month: 'numeric', day: 'numeric'
+                    },
+                    long: {
+                        year: 'numeric', month: 'numeric', day: 'numeric',
+                        hour: 'numeric', minute: 'numeric'
+                    }
+                },
+                'en': {
+                    short: {
+                        year: 'numeric', month: 'numeric', day: 'numeric'
+                    },
+                    long: {
+                        year: 'numeric', month: 'numeric', day: 'numeric',
+                        hour: 'numeric', minute: 'numeric'
+                    }
+                }
+            }
+        },
     } as I18nOptions,
     build: {
         transpile: ['vuetify'],
@@ -119,5 +151,6 @@ export default defineNuxtConfig({
                 protocol: 'wss'
             }
         },
+        ssr: { noExternal: ["moment"], }
     }
 })

+ 2 - 1
package.json

@@ -64,6 +64,7 @@
     "event-source-polyfill": "^1.0.31",
     "js-yaml": "^4.1.0",
     "libphonenumber-js": "^1.10.14",
+    "moment": "^2.29.4",
     "nuxt": "^3.0.0",
     "nuxt-lodash": "^2.4.1",
     "pinia": "^2.0.28",
@@ -72,7 +73,7 @@
     "uuid": "^9.0.0",
     "vue-tel-input-vuetify": "^1.5.3",
     "vue-the-mask": "^0.11.1",
-    "vuetify": "^3.0.7",
+    "vuetify": "^3.1.1",
     "yaml-import": "^2.0.0"
   }
 }

+ 341 - 0
pages/subscription.vue

@@ -0,0 +1,341 @@
+<!--
+Page 'Mon abonnement'
+
+@see https://ressources.opentalent.fr/display/SPEC/Mon+abonnement
+-->
+<template>
+  <LayoutContainer>
+    <v-col cols="12" sm="12" md="12">
+      <v-expansion-panels :multiple="true" v-model="openedPanels">
+        <UiExpansionPanel title="informations" icon="fas fa-info">
+          <v-container fluid class="container">
+            <v-row>
+              <v-table>
+                <tbody>
+                <tr>
+                  <td>{{ $t('client_id') }}</td>
+                  <td>{{ dolibarrAccount ? dolibarrAccount.clientNumber : '-' }}</td>
+                </tr>
+                <tr>
+                  <td>{{ $t('version') }}</td>
+                  <td>{{ dolibarrAccount ? $t(dolibarrAccount.product) : '-' }}</td>
+                </tr>
+                <tr v-if="dolibarrAccount && dolibarrAccount.contract">
+                  <td>{{ $t('services') }}</td>
+                  <td class="py-2">
+                    <div
+                        v-for="line in dolibarrAccount.contract.lines"
+                        :key="line.id"
+                    >
+                      {{ line.serviceLabel }} - {{ $t('until') }} :
+                      {{ $d(line.dateEnd) }}
+                    </div>
+                  </td>
+                </tr>
+                <tr v-if="$can('manage', 'texto')">
+                  <td>{{ $t('remaining_sms_credit') }}</td>
+                  <td>
+                    <span v-if="!mobytPending && mobytStatus !== null && mobytStatus.active">
+                      {{ mobytStatus.money.toLocaleString($i18n.locale, { style: 'currency', currency: 'EUR' }) }}
+                      {{ $t('convert_price_to_sms', { nb_sms: mobytStatus.amount }) }}
+                    </span>
+                  </td>
+                </tr>
+                </tbody>
+              </v-table>
+            </v-row>
+          </v-container>
+        </UiExpansionPanel>
+
+        <UiExpansionPanel v-if="showDolibarrPanel" title="bills" icon="fas fa-file">
+          <v-container fluid class="container">
+            <v-row>
+              <v-table>
+                <thead>
+                <tr>
+                  <th>{{ $t('reference') }}</th>
+                  <th>{{ $t('date') }}</th>
+                  <th>{{ $t('taxExcludedAmount') }}</th>
+                  <th>{{ $t('status') }}</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>
+                </tr>
+                </tbody>
+              </v-table>
+            </v-row>
+          </v-container>
+        </UiExpansionPanel>
+
+        <UiExpansionPanel title="more_features" icon="fas fa-plus">
+          <v-container id="products-section" fluid class="container">
+            <v-row>
+              <v-table>
+                <template #default>
+                  <thead>
+                  <tr>
+                    <th v-if="organizationProfile.isArtistProduct">
+                      {{ $t('PRODUCT_ARTIST_PREMIUM') }}
+                    </th>
+                    <th v-if="organizationProfile.isArtist">
+                      {{ $t('PRODUCT_SCHOOL') }}
+                    </th>
+                    <th>
+                      {{ $t('sms') }}
+                    </th>
+                    <th>
+                      {{ $t('website') }}
+                    </th>
+                  </tr>
+                  </thead>
+                  <tbody>
+                  <tr class="center-td-content">
+                    <td v-if="organizationProfile.isArtistProduct">
+                      <v-img src="/images/Artist-Square.jpg" ratio="1/1" width="240" />
+                    </td>
+                    <td v-if="organizationProfile.isArtist">
+                      <v-img src="/images/School-Square.jpg" ratio="1/1" width="240" />
+                    </td>
+                    <td>
+                      <v-img src="/images/sms_big.png" ratio="1/1" width="240" />
+                    </td>
+                    <td>
+                      <v-img src="/images/nom-de-domaine.jpg" ratio="1/1" width="240" />
+                    </td>
+                  </tr>
+                  <tr>
+                    <!-- Opentalent Artist Premium -->
+                    <td v-if="organizationProfile.isArtistProduct">
+                      <p>
+                        {{ $t('get_more_functionalities_with_version') }} <b>{{ $t('PRODUCT_ARTIST_PREMIUM') }}</b>
+                      </p>
+
+                      <!-- Cmf member -->
+                      <div v-if="organizationProfile.isCmf()">
+                        <p>
+                          <b>{{ $t('for_only_x_eur_ttc_by_month', { price: formatCurrency(7.25, 'EUR') }) }} *</b>
+                        </p>
+                        <div><i>* {{ $t('yearly_paid_giving_x_eur_ttc_per_year', { price: formatCurrency(87.00, 'EUR') }) }}</i></div>
+                        <div><i>{{ $t('only_for_cmf_members') }} ({{ $t('public_price_x_ttc_a_year', { price: formatCurrency(168.00, 'EUR') }) }})</i></div>
+                      </div>
+
+                      <!-- Not a cmf member -->
+                      <div v-else>
+                        <p>
+                          <b>{{ $t('for_only_x_eur_ttc_by_month', { price: formatCurrency(14.00, 'EUR') }) }} *</b>
+                        </p>
+                        <p>
+                          <i>
+                            * {{ $t('yearly_paid_giving_x_eur_ttc_per_year', { price: formatCurrency(168.00, 'EUR') }) }}
+                          </i>
+                        </p>
+                      </div>
+
+                      <p class="mt-3">
+                        <a href="/resources/Fiche_produit_Opentalent_Artist.pdf" target="_blank">
+                          {{ $t('product_sheet') }} {{ $t('PRODUCT_ARTIST_PREMIUM') }}
+                        </a>
+                      </p>
+
+                      <p v-if="organizationProfile.isCmf()" class="mt-3">
+                        <a href="/resources/BDC_Artist_Premium_CMF.pdf">
+                          <b>{{ $t('download_cmf_order_form') }}</b>
+                        </a>
+                      </p>
+                      <p v-else class="mt-3">
+                        <a href="/resources/BDC_Artist_Premium_Public.pdf">
+                          <b>{{ $t('download_order_form') }}</b>
+                        </a>
+                      </p>
+                    </td>
+
+                    <!-- Opentalent School Premium -->
+                    <td v-if="organizationProfile.isArtist">
+                      <p>
+                        {{ $t('switch_to_version') }} <b>{{ $t('PRODUCT_SCHOOL_PREMIUM') }}</b> {{ $t('and_benefit') }} :
+                      </p>
+
+                      <ul class="mb-2">
+                        <li>{{ $t('of_accounts_for_teachers_and_students') }}</li>
+                        <li>{{ $t('of_a_complete_website') }}</li>
+                      </ul>
+
+                      <!-- Cmf member -->
+                      <div v-if="organizationProfile.isCmf">
+                        <p><b>{{ $t('starting_from_x_eur_ttc_per_month', { price: formatCurrency(26.50, 'EUR') }) }} *</b></p>
+                        <div><i>* {{ $t('yearly_paid_giving_x_eur_ttc_per_year', { price: formatCurrency(318.00, 'EUR') }) }}</i></div>
+                        <div><i>{{ $t('version_x_up_to_x_students', { product: $t('PRODUCT_SCHOOL_PREMIUM'), max_students: '69' }) }}</i></div>
+                        <div><i>{{ $t('excluding_license_and_training_fees') }}.</i></div>
+                        <div><i>{{ $t('only_for_cmf_members') }} ({{ $t('public_price_x_ttc_a_year', { price: formatCurrency(529.20, 'EUR') }) }})</i></div>
+                      </div>
+                      <!-- Not cmf member -->
+                      <div v-else>
+                        <p>{{ $t('starting_from_x_eur_ttc_per_month', { price: formatCurrency(44.10, 'EUR') }) }} *</p>
+                        <div><i>* {{ $t('yearly_paid_giving_x_eur_ttc_per_year', { price: formatCurrency(529.20, 'EUR') }) }}</i></div>
+                        <div><i>{{ $t('version_x_up_to_x_students', { product: $t('PRODUCT_SCHOOL_PREMIUM'), max_students: '69' }) }}</i></div>
+                        <div><i>{{ $t('excluding_license_and_training_fees') }}.</i></div>
+                      </div>
+
+                      <p class="mt-4">
+                        <a href="/resources/Fiche_produit_Opentalent_School.pdf" target="_blank">
+                          {{ $t('product_sheet') }} {{ $t('PRODUCT_SCHOOL') }}
+                        </a>
+                      </p>
+
+                      <p>
+                        {{ $t('contact_us_at') }} <a href="tel:+33972126017">0 972 126 017</a>, {{ $t('or_by_mail_at') }}
+                        <a href="mailto:contact@opentalent.fr">contact@opentalent.fr</a>
+                      </p>
+                    </td>
+
+                    <!-- SMS -->
+                    <td>
+                      <p><b>{{ $t('send_sms') }} {{ $t('to_your_members_from_app') }}</b></p>
+
+                      <!-- Cmf member -->
+                      <div v-if="organizationProfile.isCmf">
+                        <p><b>{{ $t('starting_from_x_eur_ttc_per_sms', { price: formatCurrency(0.10, 'EUR') }) }} *</b></p>
+                        <p><i>* {{ $t('for_x_sms', { amount: '5000' }) }}</i></p>
+
+                        <p>
+                          <b>
+                            <a href="/resources/BDC_SMS_CMF.pdf" target="_blank">
+                              {{ $t('download_cmf_order_form') }}
+                            </a>
+                          </b>
+                        </p>
+                      </div>
+                      <!-- Not cmf member -->
+                      <div v-else>
+                        <p><b>{{ $t('starting_from_x_eur_ttc_per_sms', { price: formatCurrency(0.12, 'EUR') }) }} *</b></p>
+                        <p><i>* {{ $t('for_x_sms', { amount: '5000' }) }}</i></p>
+
+                        <p>
+                          <a href="/resources/BDC_SMS_Public.pdf" target="_blank">
+                            <b>{{ $t('download_order_form') }}</b>
+                          </a>
+                        </p>
+                      </div>
+                    </td>
+
+                    <!-- Custom domain -->
+                    <td>
+                      <p>
+                        <b>{{ $t('get_your_own_domain_and_up_to_five_emails_for_only_x_eur_ttc_per_month', { price: formatCurrency(34.80, 'EUR') }) }}</b>
+                      </p>
+
+                      <p>{{ $t('example') }} :</p>
+                      <table>
+                        <tbody>
+                        <tr>
+                          <td style="width: 150px;">
+                            {{ $t('domain_name') }} :
+                          </td>
+                          <td>
+                            <i>{{ $t('dummy_domain_name') }}</i>
+                          </td>
+                        </tr>
+                        <tr>
+                          <td>{{ $t('associated_mail_address') }} : </td>
+                          <td>
+                            <i>{{ $t('dummy_email_address') }}</i>
+                          </td>
+                        </tr>
+                        </tbody>
+                      </table>
+
+                      <p>
+                        <a href="/resources/BDC_Nom_de_domaine.pdf" target="_blank">
+                          <b>{{ $t('download_order_form') }}</b>
+                        </a>
+                      </p>
+                    </td>
+                  </tr>
+                  </tbody>
+                </template>
+              </v-table>
+            </v-row>
+          </v-container>
+        </UiExpansionPanel>
+      </v-expansion-panels>
+    </v-col>
+  </LayoutContainer>
+</template>
+
+<script setup lang="ts">
+  import {useAbility} from "@casl/vue";
+  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+  import {useEntityFetch} from "~/composables/data/useEntityFetch";
+  import {DolibarrAccount} from "~/models/Organization/DolibarrAccount";
+  import {MobytUserStatus} from "~/models/Organization/MobytUserStatus";
+  import {Ref} from "@vue/reactivity";
+
+  const { can } = useAbility()
+
+  // onBeforeMount(() => {
+  //   if(!can('display', 'subscription_page'))
+  //     return navigateTo('/error')
+  // })
+
+  const showDolibarrPanel = computed(() => !dolibarrPending.value && dolibarrAccount.value && dolibarrAccount.value.bills.length > 0)
+  const openedPanels: Ref<Array<number>> = ref([0, 1, 2])
+
+  const i18n = useI18n()
+  const organizationProfile = useOrganizationProfileStore()
+  if (organizationProfile.id === null) {
+    throw new Error("Missing organization's id")
+  }
+
+  const { fetch } = useEntityFetch()
+
+  const { data: dolibarrAccount, pending: dolibarrPending } = fetch(DolibarrAccount, organizationProfile.id)
+
+  const { data: mobytStatus, pending: mobytPending } = fetch(MobytUserStatus, organizationProfile.id)
+
+  onMounted(() => {
+    if (!showDolibarrPanel) {
+      openedPanels.value = [0, 1]
+    }
+  })
+
+  const formatCurrency = (value: Number, currency: string): string => {
+    return value.toLocaleString(i18n.locale.value, { style: 'currency', currency: currency })
+  }
+</script>
+
+<style>
+#products-section table {
+  table-layout: fixed;
+  width: 100%;
+}
+
+#products-section table img {
+  max-height: 250px;
+  max-width: 100%;
+}
+
+#products-section tr.center-td-content td {
+  text-align: center;
+}
+
+#products-section td {
+  padding: 12px 18px;
+  vertical-align: top;
+}
+
+.theme--light.v-data-table > .v-data-table__wrapper > table > tbody > tr:hover:not(.v-data-table__expanded__content):not(.v-data-table__empty-wrapper) {
+  background:none;
+}
+
+.products-pictures img {
+  width: 70%;
+}
+</style>

+ 8 - 7
plugins/init.server.ts

@@ -12,19 +12,20 @@ export default defineNuxtPlugin(async ({ssrContext}) => {
 
     const accessProfile = useAccessProfileStore()
 
-    if (accessId.value !== null) {
-        accessProfile.$patch({
-            bearer: bearer.value,
-            id: parseInt(accessId.value)
-        })
-    } else {
+    if (accessId.value === null) {
         redirectToLogin()
+        return
     }
 
+    accessProfile.$patch({
+        bearer: bearer.value,
+        id: parseInt(accessId.value)
+    })
+
     const {em} = useEntityManager()
 
     try {
-        await em.refreshProfile()
+        await em.refreshProfile(parseInt(accessId.value))
     } catch (error) {
         if (error instanceof UnauthorizedError) {
             redirectToLogin()

+ 2 - 12
services/data/entityManager.ts

@@ -284,18 +284,8 @@ class EntityManager {
      *
      * Re-fetch the user profile and update the store
      */
-    public async refreshProfile() {
-        const response = await this.apiRequestService.get('api/my_profile')
-
-        // deserialize the response
-        const hydraResponse = await HydraDenormalizer.denormalize(response)
-
-        const profileData = hydraResponse.data
-
-        // On n'aura jamais 2 profils stockés, et on a besoin d'un id pour retrouver le profil dans le store :
-        profileData['id'] = 1
-
-        const profile = this.newInstance(MyProfile, hydraResponse.data)
+    public async refreshProfile(accessId: number) {
+        const profile = await this.fetch(MyProfile, accessId)
 
         // On met à jour le store accessProfile
         const accessProfileStore = useAccessProfileStore()

+ 12 - 10
stores/organizationProfile.ts

@@ -6,7 +6,7 @@ import {useEach} from "#imports";
 export const useOrganizationProfileStore = defineStore('organizationProfile', () => {
 
   // State
-  const id = ref(null)
+  const id: Ref<number | null> = ref(null)
   const parametersId = ref(null)
   const name = ref(null)
   const product = ref(null)
@@ -19,6 +19,8 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
   const website = ref(null)
   const parents: Ref<Array<BaseOrganizationProfile>> = ref([])
 
+  const runtimeConfig = useRuntimeConfig()
+
   // Getters
   /**
    * L'organization fait-elle partie du réseau CMF?
@@ -27,7 +29,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    */
   const isCmf = computed( (): boolean => {
     return networks.value.filter((network: string) => {
-      return network === process.env.cmf_network
+      return network === runtimeConfig.cmf_network
     }).length > 0
   })
 
@@ -38,7 +40,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    */
   const isFfec = computed( (): boolean => {
     return networks.value.filter((network: string) => {
-      return network === process.env.ffec_network
+      return network === runtimeConfig.ffec_network
     }).length > 0
   })
 
@@ -56,7 +58,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isArtistProduct = computed( (): boolean => {
-    return product.value === process.env.artist_product
+    return product.value === runtimeConfig.artist_product
   })
 
   /**
@@ -64,7 +66,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isArtistPremiumProduct = computed( (): boolean => {
-    return product.value === process.env.artist_premium_product
+    return product.value === runtimeConfig.artist_premium_product
   })
 
   /**
@@ -81,7 +83,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isSchoolProduct = computed( (): boolean => {
-    return product.value === process.env.school_product
+    return product.value === runtimeConfig.school_product
   })
 
   /**
@@ -90,7 +92,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isSchoolPremiumProduct = computed( (): boolean => {
-    return product.value === process.env.school_premium_product
+    return product.value === runtimeConfig.school_premium_product
   })
 
   /**
@@ -106,7 +108,7 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isManagerProduct = computed( (): boolean => {
-    return product.value === process.env.manager_product
+    return product.value === runtimeConfig.manager_product
   })
 
   /**
@@ -131,8 +133,8 @@ export const useOrganizationProfileStore = defineStore('organizationProfile', ()
    * @return {boolean}
    */
   const isCMFCentralService = computed((): boolean => {
-    if(process.env.CMF_ID)
-      return id.value === parseInt(process.env.CMF_ID)
+    if(runtimeConfig.CMF_ID)
+      return id.value === parseInt(runtimeConfig.CMF_ID)
     return false
   })