瀏覽代碼

apply eslint - prettier fixer and manual fixes

Olivier Massot 1 年之前
父節點
當前提交
d2444bd94b
共有 100 個文件被更改,包括 2218 次插入2041 次删除
  1. 17 1
      .eslintrc.cjs
  2. 5 0
      README.md
  3. 87 90
      composables/data/useAp2iRequestService.ts
  4. 46 30
      composables/data/useEntityFetch.ts
  5. 9 9
      composables/data/useEntityManager.ts
  6. 8 12
      composables/data/useEnumFetch.ts
  7. 12 11
      composables/data/useEnumManager.ts
  8. 22 17
      composables/data/useImageFetch.ts
  9. 9 9
      composables/data/useImageManager.ts
  10. 55 50
      composables/data/useRefreshProfile.ts
  11. 9 9
      composables/form/useFieldViolation.ts
  12. 20 11
      composables/form/useValidation.ts
  13. 2 3
      composables/form/validation/useSubdomainValidation.ts
  14. 13 8
      composables/layout/useExtensionPanel.ts
  15. 15 9
      composables/layout/useMenu.ts
  16. 14 9
      composables/utils/useAdminUrl.ts
  17. 14 11
      composables/utils/useDownloadFile.ts
  18. 4 5
      composables/utils/useHomeUrl.ts
  19. 10 10
      composables/utils/useI18nUtils.ts
  20. 17 13
      composables/utils/useRedirect.ts
  21. 8 8
      composables/utils/useValidationUtils.ts
  22. 1 1
      config/abilities/pages/myAccount.yaml
  23. 111 115
      config/theme.ts
  24. 33 24
      i18n.config.ts
  25. 2 1
      lang/fr.json
  26. 6 0
      layouts/.eslintrc.cjs
  27. 1 3
      layouts/default.vue
  28. 21 15
      layouts/error.vue
  29. 7 10
      layouts/parameters.vue
  30. 5 5
      models/Access/Access.ts
  31. 4 4
      models/Access/AdminAccess.ts
  32. 4 4
      models/Access/MyProfile.ts
  33. 5 5
      models/Access/PersonalizedList.ts
  34. 2 3
      models/ApiModel.ts
  35. 30 28
      models/ApiResource.ts
  36. 3 3
      models/Billing/ResidenceArea.ts
  37. 7 9
      models/Booking/AttendanceBookingReason.ts
  38. 9 9
      models/Core/AddressPostal.ts
  39. 9 9
      models/Core/BankAccount.ts
  40. 10 10
      models/Core/ContactPoint.ts
  41. 2 2
      models/Core/Country.ts
  42. 2 2
      models/Core/File.ts
  43. 5 5
      models/Core/Notification.ts
  44. 5 5
      models/Core/NotificationMessage.ts
  45. 4 4
      models/Core/NotificationUsers.ts
  46. 9 9
      models/Core/Tagg.ts
  47. 4 4
      models/Education/Cycle.ts
  48. 2 2
      models/Education/EducationTiming.ts
  49. 2 2
      models/Export/LicenceCmfOrganizationER.ts
  50. 2 2
      models/Network/Network.ts
  51. 3 3
      models/Network/NetworkOrganization.ts
  52. 9 9
      models/OnlineRegistration/RegistrationAvailability.ts
  53. 2 2
      models/OnlineRegistration/RegistrationStatus.ts
  54. 9 9
      models/Organization/Cotisation.ts
  55. 20 15
      models/Organization/DolibarrAccount.ts
  56. 13 16
      models/Organization/MobytUserStatus.ts
  57. 34 34
      models/Organization/Organization.ts
  58. 2 2
      models/Organization/OrganizationAddressPostal.ts
  59. 2 2
      models/Organization/OrganizationArticle.ts
  60. 5 5
      models/Organization/OrganizationLicence.ts
  61. 2 2
      models/Organization/OrganizationNetwork.ts
  62. 25 25
      models/Organization/OrganizationProfile.ts
  63. 4 4
      models/Organization/Parameters.ts
  64. 2 2
      models/Organization/Subdomain.ts
  65. 9 9
      models/Organization/SubdomainAvailability.ts
  66. 2 2
      models/Organization/TypeOfPractice.ts
  67. 5 5
      models/Person/Person.ts
  68. 9 10
      models/decorators.ts
  69. 6 4
      models/models.ts
  70. 190 143
      nuxt.config.ts
  71. 3 2
      package.json
  72. 6 0
      pages/.eslintrc.cjs
  73. 32 31
      pages/cmf_licence_structure.vue
  74. 0 11
      pages/index.vue
  75. 18 26
      pages/organization.vue.off
  76. 0 548
      pages/organization/index.vue
  77. 580 0
      pages/organization/index.vue.off
  78. 8 9
      pages/parameters.vue
  79. 3 3
      pages/parameters/attendance_booking_reasons/[id].vue
  80. 4 4
      pages/parameters/attendance_booking_reasons/new.vue
  81. 47 40
      pages/parameters/attendances.vue
  82. 39 40
      pages/parameters/bulletin.vue
  83. 6 12
      pages/parameters/cycles/[id].vue
  84. 31 32
      pages/parameters/education_notation.vue
  85. 3 4
      pages/parameters/education_timings/[id].vue
  86. 24 21
      pages/parameters/education_timings/index.vue
  87. 4 4
      pages/parameters/education_timings/new.vue
  88. 59 54
      pages/parameters/general_parameters.vue
  89. 2 3
      pages/parameters/index.vue
  90. 36 35
      pages/parameters/intranet.vue
  91. 4 4
      pages/parameters/residence_areas/[id].vue
  92. 28 28
      pages/parameters/residence_areas/index.vue
  93. 2 2
      pages/parameters/residence_areas/new.vue
  94. 15 16
      pages/parameters/sms.vue
  95. 38 43
      pages/parameters/subdomains/[id].vue
  96. 12 12
      pages/parameters/subdomains/new.vue
  97. 35 35
      pages/parameters/super_admin.vue
  98. 38 29
      pages/parameters/teaching.vue
  99. 80 64
      pages/parameters/website.vue
  100. 5 6
      pages/poc.vue

+ 17 - 1
.eslintrc.cjs

@@ -23,7 +23,8 @@ module.exports = {
     ".nuxt",
     "coverage/*",
     "vendor/*",
-    "dist/*"
+    "dist/*",
+    "tests/*",
   ],
   plugins: [
     "vue",
@@ -31,5 +32,20 @@ module.exports = {
   ],
   // add your custom rules here
   rules: {
+    "no-console": 0, // on autorise les appels à la console (puisque ceux ci seront de toute façon nettoyés à la compilation)
+    'vue/valid-v-slot': ['error', {
+      allowModifiers: true,
+    }],
+  },
+  "globals": {
+    "useRuntimeConfig": "readonly",
+    "navigateTo": "readonly",
+    "computed": "readonly",
+    "ref": "readonly",
+    "definePageMeta": "readonly",
+    "useRouter": "readonly",
+    "useRoute": "readonly",
+    "useI18n": "readonly",
+    "onMounted": "readonly",
   }
 }

+ 5 - 0
README.md

@@ -97,6 +97,11 @@ Sur les environnements où app est servie par supervisor, on peut consulter les
 > le `-6000` étant le nombre de bytes à afficher
 > Voir plus : http://supervisord.org/running.html#supervisorctl-command-line-options
 
+### Exécuter ESLint
+
+    yarn eslint
+
+
 ### Faire fonctionner le HMR
 
 Si le HMR (Hot Module Reload) ne fontionne pas et qu'un message d'erreur est logué en console disant que l'adresse

+ 87 - 90
composables/data/useAp2iRequestService.ts

@@ -1,109 +1,106 @@
-import {TYPE_ALERT} from "~/types/enum/enums";
-import ApiRequestService from "~/services/data/apiRequestService";
-import type {Ref} from "@vue/reactivity";
-import {usePageStore} from "~/stores/page";
-import UnauthorizedError from "~/services/error/UnauthorizedError";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import type {AssociativeArray} from "~/types/data";
-import type {FetchContext, FetchOptions} from "ofetch";
+import type { Ref } from 'vue'
+import type { FetchContext, FetchOptions } from 'ofetch'
+import { TYPE_ALERT } from '~/types/enum/enums'
+import ApiRequestService from '~/services/data/apiRequestService'
+import { usePageStore } from '~/stores/page'
+import UnauthorizedError from '~/services/error/UnauthorizedError'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import type { AssociativeArray } from '~/types/data'
 
 /**
  * Retourne une instance de ApiRequestService configurée pour interroger l'api Ap2i
  *
  * @see https://github.com/unjs/ohmyfetch/blob/main/README.md#%EF%B8%8F-create-fetch-with-default-options
  */
-let apiRequestServiceClass:null|ApiRequestService = null
+let apiRequestServiceClass: null | ApiRequestService = null
 export const useAp2iRequestService = () => {
-    const runtimeConfig = useRuntimeConfig()
-
-    const baseURL = runtimeConfig.baseUrl ?? runtimeConfig.public.baseUrl
-
-    const pending: Ref<boolean> = ref(false)
-
-    /**
-     * Peuple les headers avant l'envoi de la requête
-     *
-     * @param request
-     * @param options
-     */
-    const onRequest = async function ({ request, options }: FetchContext) {
-        // @ts-ignore
-        if(options && options.noXaccessId) {
-            return
-        }
-
-        const accessProfileStore = useAccessProfileStore()
-
-        const headers: AssociativeArray = {
-            'x-accessid': String(accessProfileStore.id),
-            'Authorization': 'BEARER ' + accessProfileStore.bearer,
-        }
-
-        if (accessProfileStore.switchId) {
-            headers['x-switch-user'] = String(accessProfileStore.switchId)
-        }
+  const runtimeConfig = useRuntimeConfig()
+
+  const baseURL = runtimeConfig.baseUrl ?? runtimeConfig.public.baseUrl
+
+  const pending: Ref<boolean> = ref(false)
+
+  /**
+   * Peuple les headers avant l'envoi de la requête
+   *
+   * @param request
+   * @param options
+   */
+  const onRequest = function ({ request, options }: FetchContext) {
+    // @ts-expect-error options is not aware of noXaccessId
+    if (options && options.noXaccessId) {
+      return
+    }
 
-        options.headers = { ...options.headers, ...headers }
+    const accessProfileStore = useAccessProfileStore()
 
-        pending.value = true
-        console.log('Request : ' + request + ' (SSR: ' + process.server + ')')
+    const headers: AssociativeArray = {
+      'x-accessid': String(accessProfileStore.id),
+      Authorization: 'BEARER ' + accessProfileStore.bearer,
     }
 
-    const onRequestError = async function({ request, options, response }: FetchContext) {
-        pending.value = false
+    if (accessProfileStore.switchId) {
+      headers['x-switch-user'] = String(accessProfileStore.switchId)
     }
 
-    /**
-     * Server responded
-     *
-     * @param request
-     * @param options
-     * @param response
-     */
-    const onResponse = async function({ request, options, response }: FetchContext) {
-        pending.value = false
+    options.headers = { ...options.headers, ...headers }
+
+    pending.value = true
+    console.log('Request : ' + request + ' (SSR: ' + process.server + ')')
+  }
+
+  const onRequestError = function (_: FetchContext) {
+    pending.value = false
+  }
+
+  /**
+   * Server responded
+   */
+  const onResponse = function (_: FetchContext) {
+    pending.value = false
+  }
+
+  /**
+   * Gère les erreurs retournées par l'api
+   *
+   * @param request
+   * @param response
+   * @param error
+   */
+  const onResponseError = function ({ response, error }: FetchContext) {
+    pending.value = false
+
+    if (response && response.status === 401) {
+      throw new UnauthorizedError('Ap2i - Unauthorized')
+    } else if (response && response.status === 403) {
+      console.error('! Request error: Forbidden')
+      usePageStore().addAlert(TYPE_ALERT.ALERT, ['forbidden'])
+    } else if (
+      response &&
+      (response.status === 400 || response.status >= 404)
+    ) {
+      // @see https://developer.mozilla.org/fr/docs/Web/HTTP/Status
+      const errorMsg = error ? error.message : response.statusText
+      console.error('! Request error: ' + errorMsg)
+      usePageStore().addAlert(TYPE_ALERT.ALERT, [errorMsg])
     }
+  }
 
-    /**
-     * Gère les erreurs retournées par l'api
-     *
-     * @param request
-     * @param response
-     * @param error
-     */
-    const onResponseError = async function ({ request, response, error }: FetchContext) {
-        pending.value = false
-
-        if (response && response.status === 401) {
-            throw new UnauthorizedError('Ap2i - Unauthorized')
-        }
-        else if (response && response.status === 403) {
-            console.error('! Request error: Forbidden')
-            usePageStore().addAlert(TYPE_ALERT.ALERT, ['forbidden'])
-        }
-        else if (response && (response.status === 400 || response.status >= 404)) {
-            // @see https://developer.mozilla.org/fr/docs/Web/HTTP/Status
-            const error_msg = error ? error.message : response.statusText
-            console.error('! Request error: ' + error_msg)
-            usePageStore().addAlert(TYPE_ALERT.ALERT, [error_msg])
-        }
-    }
+  const config: FetchOptions = {
+    baseURL,
+    onRequest,
+    onRequestError,
+    onResponse,
+    onResponseError,
+  }
 
-    const config : FetchOptions = {
-        baseURL,
-        onRequest,
-        onRequestError,
-        onResponse,
-        onResponseError
-    }
+  // Avoid memory leak
+  if (apiRequestServiceClass === null) {
+    // Utilise la fonction `create` d'ohmyfetch pour générer un fetcher dédié à l'interrogation de Ap2i
+    const fetcher = $fetch.create(config)
 
-    //Avoid memory leak
-    if (apiRequestServiceClass === null) {
-        // Utilise la fonction `create` d'ohmyfetch pour générer un fetcher dédié à l'interrogation de Ap2i
-        const fetcher = $fetch.create(config)
-        // @ts-ignore
-        apiRequestServiceClass = new ApiRequestService(fetcher)
-    }
+    apiRequestServiceClass = new ApiRequestService(fetcher)
+  }
 
-    return { apiRequestService: apiRequestServiceClass, pending: pending }
+  return { apiRequestService: apiRequestServiceClass, pending }
 }

+ 46 - 30
composables/data/useEntityFetch.ts

@@ -1,42 +1,58 @@
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import ApiResource from "~/models/ApiResource";
-import type {AssociativeArray, Collection} from "~/types/data";
-import type {AsyncData} from "#app";
-import type {ComputedRef, Ref} from "vue";
-import {v4 as uuid4} from "uuid";
+import type { AsyncData } from '#app'
+import type { ComputedRef, Ref } from 'vue'
+import { v4 as uuid4 } from 'uuid'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import ApiResource from '~/models/ApiResource'
+import type { AssociativeArray, Collection } from '~/types/data'
 
 interface useEntityFetchReturnType {
-    fetch: (model: typeof ApiResource, id: number) => AsyncData<ApiResource, ApiResource | true>,
-    fetchCollection: (model: typeof ApiResource, parent?: ApiResource | null, query?: Ref<AssociativeArray>) => AsyncData<Collection, any>
-    // @ts-ignore
-    getRef: <T extends ApiResource>(model: typeof T, id: Ref<number | null>) => ComputedRef<null | T>
+  fetch: (
+    model: typeof ApiResource,
+    id: number,
+  ) => AsyncData<ApiResource | null, Error | null>
+
+  fetchCollection: (
+    model: typeof ApiResource,
+    parent?: ApiResource | null,
+    query?: Ref<AssociativeArray>,
+  ) => AsyncData<Collection | null, Error | null>
+
+  getRef: <T extends ApiResource>(
+    model: new () => T,
+    id: Ref<number | null>,
+  ) => ComputedRef<null | T>
 }
 
 // TODO: améliorer le typage des fonctions sur le modèle de getRef
-export const useEntityFetch = (lazy: boolean = false): useEntityFetchReturnType => {
-    const { em } = useEntityManager()
+export const useEntityFetch = (
+  lazy: boolean = false,
+): useEntityFetchReturnType => {
+  const { em } = useEntityManager()
 
-    const fetch = (model: typeof ApiResource, id: number) => useAsyncData(
-        model.entity + '_' + id + '_' + uuid4(),
-        () => em.fetch(model, id, true),
-        { lazy }
+  const fetch = (model: typeof ApiResource, id: number) =>
+    useAsyncData(
+      model.entity + '_' + id + '_' + uuid4(),
+      () => em.fetch(model, id, true),
+      { lazy },
     )
 
-    const fetchCollection = (
-        model: typeof ApiResource,
-        parent: ApiResource | null = null,
-        query: Ref<AssociativeArray | null> = ref(null)
-    ) => useAsyncData(
-        model.entity + '_many_' + uuid4(),
-        () => em.fetchCollection(model, parent, query.value ?? undefined),
-        { lazy }
+  const fetchCollection = (
+    model: typeof ApiResource,
+    parent: ApiResource | null = null,
+    query: Ref<AssociativeArray | null> = ref(null),
+  ) =>
+    useAsyncData(
+      model.entity + '_many_' + uuid4(),
+      () => em.fetchCollection(model, parent, query.value ?? undefined),
+      { lazy },
     )
 
-    // @ts-ignore
-    const getRef = <T extends ApiResource>(model: typeof T, id: Ref<number | null>): ComputedRef<T | null> => {
-        return computed(() => (id.value ? em.find(model, id.value) as T : null))
-    }
+  const getRef = <T extends ApiResource>(
+    model: new () => T,
+    id: Ref<number | null>,
+  ): ComputedRef<T | null> => {
+    return computed(() => (id.value ? (em.find(model, id.value) as T) : null))
+  }
 
-    //@ts-ignore
-    return { fetch, fetchCollection, getRef }
+  return { fetch, fetchCollection, getRef }
 }

+ 9 - 9
composables/data/useEntityManager.ts

@@ -1,15 +1,15 @@
-import EntityManager from "~/services/data/entityManager";
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import {useRepo} from "pinia-orm";
+import { useRepo } from 'pinia-orm'
+import EntityManager from '~/services/data/entityManager'
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
 
 let entityManager: EntityManager | null = null
 
 export const useEntityManager = () => {
-    if (entityManager === null) {
-        const { apiRequestService } = useAp2iRequestService()
-        const getRepo = useRepo
+  if (entityManager === null) {
+    const { apiRequestService } = useAp2iRequestService()
+    const getRepo = useRepo
 
-        entityManager = new EntityManager(apiRequestService, getRepo)
-    }
-    return { em: entityManager }
+    entityManager = new EntityManager(apiRequestService, getRepo)
+  }
+  return { em: entityManager }
 }

+ 8 - 12
composables/data/useEnumFetch.ts

@@ -1,20 +1,16 @@
-import {useEnumManager} from "~/composables/data/useEnumManager";
-import type {Enum} from "~/types/data";
-import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
+import { useEnumManager } from '~/composables/data/useEnumManager'
+import type { Enum } from '~/types/data'
 
 interface useEnumFetchReturnType {
-    fetch: (enumName: string) => AsyncData<Enum, null | true | Error>,
+  fetch: (enumName: string) => AsyncData<Enum | null, Error | null>
 }
 
 export const useEnumFetch = (lazy: boolean = false): useEnumFetchReturnType => {
-    const { enumManager } = useEnumManager()
+  const { enumManager } = useEnumManager()
 
-    const fetch = (enumName: string) => useAsyncData(
-        enumName,
-        () => enumManager.fetch(enumName),
-        { lazy }
-    )
+  const fetch = (enumName: string) =>
+    useAsyncData(enumName, () => enumManager.fetch(enumName), { lazy })
 
-    //@ts-ignore
-    return { fetch }
+  return { fetch }
 }

+ 12 - 11
composables/data/useEnumManager.ts

@@ -1,15 +1,16 @@
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import EnumManager from "~/services/data/enumManager";
-import {useI18n} from "vue-i18n";
+import { useI18n } from 'vue-i18n'
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
+import EnumManager from '~/services/data/enumManager'
 
-let enumManager:EnumManager | null = null
+let enumManager: EnumManager | null = null
 
 export const useEnumManager = () => {
-    //Avoid memory leak
-    if (enumManager === null) {
-        const { apiRequestService } = useAp2iRequestService()
-        const i18n = useI18n() as any
-        enumManager = new EnumManager(apiRequestService, i18n)
-    }
-    return { enumManager: enumManager }
+  // Avoid memory leak
+  if (enumManager === null) {
+    const { apiRequestService } = useAp2iRequestService()
+    const i18n = useI18n()
+    // @ts-expect-error TODO: explain the error of conversion from useI18n result to VueI18n
+    enumManager = new EnumManager(apiRequestService, i18n)
+  }
+  return { enumManager }
 }

+ 22 - 17
composables/data/useImageFetch.ts

@@ -1,29 +1,34 @@
-import {useImageManager} from "~/composables/data/useImageManager";
-import type {AsyncData, FetchResult} from "#app";
-import {v4 as uuid4} from "uuid";
-import type {Ref} from "@vue/reactivity";
+import type { AsyncData } from '#app'
+import { v4 as uuid4 } from 'uuid'
+import type { Ref } from 'vue'
+import { useImageManager } from '~/composables/data/useImageManager'
 
 interface useImageFetchReturnType {
-    fetch: (id: Ref<number | null>, defaultImage?: string | null, height?: number, width?: number) => AsyncData<string | ArrayBuffer | null, Error | null>
+  fetch: (
+    id: Ref<number | null>,
+    defaultImage?: string | null,
+    height?: number,
+    width?: number,
+  ) => AsyncData<string | ArrayBuffer | null, Error | null>
 }
 
 /**
  * Sert d'intermédiaire entre les composants et l'ImageManager en fournissant une méthode useAsyncData toute prête.
  */
 export const useImageFetch = (): useImageFetchReturnType => {
-    const { imageManager } = useImageManager()
-
-    const fetch = (
-        id: Ref<number | null>,  // If id is null, fetch shall return the default image url
-        defaultImage: string | null = null,
-        height: number = 0,
-        width: number = 0
-    ) => useAsyncData(
-        'img' + (id ?? defaultImage ?? 0) + '_' + uuid4(),
-        () => imageManager.get(id.value, defaultImage, height, width),
-        { lazy: true, server: false },  // Always fetch images client-side
+  const { imageManager } = useImageManager()
 
+  const fetch = (
+    id: Ref<number | null>, // If id is null, fetch shall return the default image url
+    defaultImage: string | null = null,
+    height: number = 0,
+    width: number = 0,
+  ) =>
+    useAsyncData(
+      'img' + (id ?? defaultImage ?? 0) + '_' + uuid4(),
+      () => imageManager.get(id.value, defaultImage, height, width),
+      { lazy: true, server: false }, // Always fetch images client-side
     )
 
-    return { fetch }
+  return { fetch }
 }

+ 9 - 9
composables/data/useImageManager.ts

@@ -1,14 +1,14 @@
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import ImageManager from "~/services/data/imageManager";
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
+import ImageManager from '~/services/data/imageManager'
 
-let imageManager:ImageManager | null = null
+let imageManager: ImageManager | null = null
 
 export const useImageManager = () => {
-    //Avoid memory leak
-    if (imageManager === null) {
-        const { apiRequestService } = useAp2iRequestService()
-        imageManager = new ImageManager(apiRequestService)
-    }
+  // Avoid memory leak
+  if (imageManager === null) {
+    const { apiRequestService } = useAp2iRequestService()
+    imageManager = new ImageManager(apiRequestService)
+  }
 
-    return { imageManager: imageManager }
+  return { imageManager }
 }

+ 55 - 50
composables/data/useRefreshProfile.ts

@@ -1,61 +1,66 @@
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import MyProfile from "~/models/Access/MyProfile";
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import MyProfile from '~/models/Access/MyProfile'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
 export const useRefreshProfile = () => {
+  const accessProfileStore = useAccessProfileStore()
+  const organizationProfileStore = useOrganizationProfileStore()
+  const { em } = useEntityManager()
 
-    const accessProfileStore = useAccessProfileStore()
-    const organizationProfileStore = useOrganizationProfileStore()
-    const { em } = useEntityManager()
+  const fetchProfile = async (
+    accessId: number | null = null,
+  ): Promise<MyProfile> => {
+    if (accessId === null) {
+      accessId = accessProfileStore.currentAccessId
+    }
 
-    const fetchProfile = async (accessId: number | null = null): Promise<MyProfile> => {
-        if (accessId === null) {
-            accessId = accessProfileStore.currentAccessId
-        }
+    return (await em.fetch(MyProfile, accessId, true)) as MyProfile
+  }
 
-        return await em.fetch(MyProfile, accessId, true) as MyProfile
-    }
+  /**
+   * Fetch the access profile and initiate the user profile and organization profile stores
+   *
+   * /!\ Server side only!
+   *
+   * @param accessId
+   * @param bearer
+   * @param switchId
+   */
+  const initiateProfile = async (
+    accessId: number,
+    bearer: string,
+    switchId: number | null,
+  ): Promise<void> => {
+    accessProfileStore.$patch({
+      bearer,
+      id: accessId,
+      switchId,
+    })
 
-    /**
-     * Fetch the access profile and initiate the user profile and organization profile stores
-     *
-     * /!\ Server side only!
-     *
-     * @param accessId
-     * @param bearer
-     * @param switchId
-     */
-    const initiateProfile = async (accessId: number, bearer: string, switchId: number | null): Promise<void> => {
-        accessProfileStore.$patch({
-            bearer: bearer,
-            id: accessId,
-            switchId: switchId
-        })
-
-        const profile = await fetchProfile(accessId)
-
-        // Sans le flush, on observe un bug non-expliqué au rechargement de la page en mode dev : la fonction save
-        //  du repo de MyProfile ne fonctionne pas quand le plugin init.server.ts re-fetch le profil
-        em.flush(MyProfile)
-
-        accessProfileStore.initiateProfile(profile)
-        organizationProfileStore.initiateProfile(profile.organization)
-    }
+    const profile = await fetchProfile(accessId)
 
-    /**
-     * Re-fetch the user profile and update the store
-     */
-    const refreshProfile = async (accessId: number | null = null) => {
-        const profile = await fetchProfile(accessId)
+    // Sans le flush, on observe un bug non-expliqué au rechargement de la page en mode dev : la fonction save
+    //  du repo de MyProfile ne fonctionne pas quand le plugin init.server.ts re-fetch le profil
+    em.flush(MyProfile)
 
-        // Sans le flush, on observe un bug non-expliqué au rechargement de la page en mode dev : la fonction save
-        //  du repo de MyProfile ne fonctionne pas quand le plugin init.server.ts re-fetch le profil
-        em.flush(MyProfile)
+    accessProfileStore.initiateProfile(profile)
+    organizationProfileStore.initiateProfile(profile.organization)
+  }
 
-        accessProfileStore.setProfile(profile)
-        organizationProfileStore.setProfile(profile.organization)
-    }
+  /**
+   * Re-fetch the user profile and update the store
+   */
+  const refreshProfile = async (accessId: number | null = null) => {
+    const profile = await fetchProfile(accessId)
+
+    // Sans le flush, on observe un bug non-expliqué au rechargement de la page en mode dev : la fonction save
+    //  du repo de MyProfile ne fonctionne pas quand le plugin init.server.ts re-fetch le profil
+    em.flush(MyProfile)
+
+    accessProfileStore.setProfile(profile)
+    organizationProfileStore.setProfile(profile.organization)
+  }
 
-    return { initiateProfile, refreshProfile }
+  return { initiateProfile, refreshProfile }
 }

+ 9 - 9
composables/form/useFieldViolation.ts

@@ -1,7 +1,7 @@
-import {computed} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
-import {useFormStore} from "~/stores/form";
+import { computed } from 'vue'
+import type { ComputedRef } from 'vue'
 import * as _ from 'lodash-es'
+import { useFormStore } from '~/stores/form'
 
 /**
  * Composable pour gérer l'apparition de message d'erreurs de validation d'un champ de formulaire
@@ -9,22 +9,22 @@ import * as _ from 'lodash-es'
  * @param field
  */
 export function useFieldViolation(field: string) {
-  const fieldViolations: ComputedRef<string> = computed(()=> {
+  const fieldViolations: ComputedRef<string> = computed(() => {
     return _.get(useFormStore().violations, field, '')
   })
 
   /**
    * Lorsque la valeur d'un champ change, on supprime le fait qu'il puisse être "faux" dans le store
    * @param field
-   * @param value
    */
-  function updateViolationState(field: string, value: any) {
-    //@ts-ignore
-    useFormStore().setViolations(_.omit(useFormStore().violations, field))
+  function updateViolationState(field: string) {
+    useFormStore().setViolations(
+      _.omit(useFormStore().violations, field) as string[],
+    )
   }
 
   return {
     fieldViolations,
-    updateViolationState: (fieldValue: any) => updateViolationState(fieldValue, field)
+    updateViolationState,
   }
 }

+ 20 - 11
composables/form/useValidation.ts

@@ -1,14 +1,13 @@
-import  {useI18n} from 'vue-i18n'
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import UrlUtils from "~/services/utils/urlUtils";
-import type {Ref} from "@vue/reactivity";
+import { useI18n } from 'vue-i18n'
+import type { Ref } from 'vue'
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
+import UrlUtils from '~/services/utils/urlUtils'
 
 /**
  * @category composables/form
  * Composable pour des utils de verifications
  */
 export function useValidation() {
-
   /**
    * Use méthode fournissant une fonction pour tester la validité d'un Siret ainsi que la gestion du message d'erreur
    */
@@ -17,28 +16,38 @@ export function useValidation() {
     const siretErrorMessage: Ref<string> = ref('')
 
     const validateSiret = async (siret: string) => {
-
       const { apiRequestService } = useAp2iRequestService()
-      const response: any = await apiRequestService.get(UrlUtils.join('/api/siret-checking', siret))
+      const response: Response = await apiRequestService.get(
+        UrlUtils.join('/api/siret-checking', siret),
+      )
 
       if (typeof response === 'undefined') {
         siretError.value = false
         siretErrorMessage.value = ''
       }
 
+      if (!Object.prototype.hasOwnProperty.call(response, 'isCorrect')) {
+        throw new Error('Invalid response format')
+      }
+
+      // @ts-expect-error At this point, response has an 'isCorrect' property
+      const isCorrect = response.isCorrect
+
       const i18n = useI18n()
-      siretError.value = !response.isCorrect
-      siretErrorMessage.value = response.isCorrect ? '' : i18n.t('siret_error') as string
+      siretError.value = !isCorrect
+      siretErrorMessage.value = isCorrect
+        ? ''
+        : (i18n.t('siret_error') as string)
     }
 
     return {
       siretError,
       siretErrorMessage,
-      validateSiret
+      validateSiret,
     }
   }
 
   return {
-    useValidateSiret
+    useValidateSiret,
   }
 }

+ 2 - 3
composables/form/validation/useSubdomainValidation.ts

@@ -1,9 +1,8 @@
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import SubdomainValidation from "~/services/validation/subdomainValidation";
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
+import SubdomainValidation from '~/services/validation/subdomainValidation'
 
 let subdomainValidation: SubdomainValidation | null = null
 
-
 export function useSubdomainValidation() {
   if (subdomainValidation === null) {
     const { apiRequestService } = useAp2iRequestService()

+ 13 - 8
composables/layout/useExtensionPanel.ts

@@ -1,4 +1,4 @@
-import type {Ref} from "@vue/reactivity";
+import type { Ref } from 'vue'
 import * as _ from 'lodash-es'
 
 /**
@@ -11,16 +11,21 @@ export function useExtensionPanel(route: Ref) {
 
   onMounted(() => {
     setTimeout(function () {
-      _.each(document.getElementsByClassName('v-expansion-panel'), (element, index) => {
-        if (element.id == activeAccordionId) {
-          panel.value = index
-        }
-      })
-      if (!panel.value) { panel.value = 0 }
+      _.each(
+        document.getElementsByClassName('v-expansion-panel'),
+        (element, index) => {
+          if (element.id === activeAccordionId) {
+            panel.value = index
+          }
+        },
+      )
+      if (!panel.value) {
+        panel.value = 0
+      }
     }, 0)
   })
 
   return {
-    panel
+    panel,
   }
 }

+ 15 - 9
composables/layout/useMenu.ts

@@ -1,11 +1,11 @@
-import {useAccessProfileStore} from "~/stores/accessProfile";
-import {useAbility} from "@casl/vue";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {MenuGroup, MenuItem} from "~/types/layout";
-import {MENU_LINK_TYPE} from "~/types/enum/layout";
-import type {AccessProfile} from "~/types/interfaces";
-import {useLayoutStore} from "~/stores/layout";
-import MenuComposer from "~/services/layout/menuComposer";
+import { useAbility } from '@casl/vue'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import type { MenuGroup, MenuItem } from '~/types/layout'
+import { MENU_LINK_TYPE } from '~/types/enum/layout'
+import type { AccessProfile } from '~/types/interfaces'
+import { useLayoutStore } from '~/stores/layout'
+import MenuComposer from '~/services/layout/menuComposer'
 
 /**
  * Renvoie des méthodes pour interagir avec les menus
@@ -31,7 +31,13 @@ export const useMenu = () => {
    * false, jusqu'à ce que je tilte que le menu s'appelait MyFamily, et pas Family
    */
   const buildAllMenu = () => {
-    MenuComposer.build(runtimeConfig, ability, organizationProfile, accessProfile as AccessProfile, layoutState)
+    MenuComposer.build(
+      runtimeConfig,
+      ability,
+      organizationProfile,
+      accessProfile as AccessProfile,
+      layoutState,
+    )
   }
 
   /**

+ 14 - 9
composables/utils/useAdminUrl.ts

@@ -1,14 +1,19 @@
-import UrlUtils from "~/services/utils/urlUtils";
+import UrlUtils from '~/services/utils/urlUtils'
 
 export const useAdminUrl = () => {
-    const runtimeConfig = useRuntimeConfig()
+  const runtimeConfig = useRuntimeConfig()
 
-    const makeAdminUrl = (tail: string, query: Record<string, string> = {}): string => {
-        const baseUrl = runtimeConfig.baseUrlAdminLegacy ?? runtimeConfig.public.baseUrlAdminLegacy
-        let url = UrlUtils.join(baseUrl, '#', tail)
-        url = UrlUtils.addQuery(url, query)
-        return url
-    }
+  const makeAdminUrl = (
+    tail: string,
+    query: Record<string, string> = {},
+  ): string => {
+    const baseUrl =
+      runtimeConfig.baseUrlAdminLegacy ??
+      runtimeConfig.public.baseUrlAdminLegacy
+    let url = UrlUtils.join(baseUrl, '#', tail)
+    url = UrlUtils.addQuery(url, query)
+    return url
+  }
 
-    return { makeAdminUrl }
+  return { makeAdminUrl }
 }

+ 14 - 11
composables/utils/useDownloadFile.ts

@@ -1,19 +1,22 @@
-import {useAp2iRequestService} from "~/composables/data/useAp2iRequestService";
-import File from "~/models/Core/File"
-import FileSaver from "file-saver";
+import FileSaver from 'file-saver'
+import { useAp2iRequestService } from '~/composables/data/useAp2iRequestService'
+import File from '~/models/Core/File'
 
 export const useDownloadFile = async (file: File) => {
-    const { apiRequestService } = useAp2iRequestService()
+  const { apiRequestService } = useAp2iRequestService()
 
-    const downloadUrl = `api/download/${file.id}`
+  const downloadUrl = `api/download/${file.id}`
 
-    const response: any = await apiRequestService.get(downloadUrl)
+  const response = await apiRequestService.get(downloadUrl)
 
-    if (!response || response.size === 0) {
-        console.error('Error: file ' + file.id + ' not found')
-    }
+  const blobPart = await response.blob()
 
-    const blob = new Blob([response], { type: response.type })
+  if (!response || blobPart.size === 0) {
+    console.error('Error: file ' + file.id + ' not found')
+  }
 
-    FileSaver.saveAs(blob, file.name ?? 'unknown');
+  const blob = new Blob([blobPart], { type: response.type })
+
+  // eslint-disable-next-line import/no-named-as-default-member
+  FileSaver.saveAs(blob, file.name ?? 'unknown')
 }

+ 4 - 5
composables/utils/useHomeUrl.ts

@@ -1,10 +1,9 @@
-import {useAdminUrl} from "~/composables/utils/useAdminUrl";
+import { useAdminUrl } from '~/composables/utils/useAdminUrl'
 
 export const useHomeUrl = () => {
+  const { makeAdminUrl } = useAdminUrl()
 
-    const { makeAdminUrl } = useAdminUrl()
+  const homeUrl = makeAdminUrl('dashboard')
 
-    const homeUrl = makeAdminUrl('dashboard')
-
-    return { homeUrl }
+  return { homeUrl }
 }

+ 10 - 10
composables/utils/useI18nUtils.ts

@@ -1,13 +1,13 @@
-import {useI18n} from "vue-i18n";
-import I18nUtils from "~/services/utils/i18nUtils";
+import { useI18n } from 'vue-i18n'
+import I18nUtils from '~/services/utils/i18nUtils'
 
-let i18nUtilsClass:null|I18nUtils = null
+let i18nUtilsClass: null | I18nUtils = null
 export const useI18nUtils = () => {
-    //Avoid memory leak
-    if(i18nUtilsClass === null){
-        const i18n = useI18n()
-        //@ts-ignore
-        i18nUtilsClass = new I18nUtils(i18n)
-    }
-    return i18nUtilsClass
+  // Avoid memory leak
+  if (i18nUtilsClass === null) {
+    const i18n = useI18n()
+    // @ts-expect-error TODO: explain the error of conversion from useI18n result to VueI18n
+    i18nUtilsClass = new I18nUtils(i18n)
+  }
+  return i18nUtilsClass
 }

+ 17 - 13
composables/utils/useRedirect.ts

@@ -1,21 +1,25 @@
-import UrlUtils from "~/services/utils/urlUtils";
+import UrlUtils from '~/services/utils/urlUtils'
 
 export const useRedirect = () => {
-    const runtimeConfig = useRuntimeConfig()
+  const runtimeConfig = useRuntimeConfig()
 
-    const redirectToLogout = () => {
-        if (!runtimeConfig.baseUrlAdminLegacy) {
-            throw new Error('Configuration error : no redirection target')
-        }
-        navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#/logout'), {external: true})
+  const redirectToLogout = () => {
+    if (!runtimeConfig.baseUrlAdminLegacy) {
+      throw new Error('Configuration error : no redirection target')
     }
+    navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#/logout'), {
+      external: true,
+    })
+  }
 
-    const redirectToHome = () => {
-        if (!runtimeConfig.baseUrlAdminLegacy) {
-            throw new Error('Configuration error : no redirection target')
-        }
-        navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#/dashboard'), {external: true})
+  const redirectToHome = () => {
+    if (!runtimeConfig.baseUrlAdminLegacy) {
+      throw new Error('Configuration error : no redirection target')
     }
+    navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#/dashboard'), {
+      external: true,
+    })
+  }
 
-    return { redirectToLogout, redirectToHome }
+  return { redirectToLogout, redirectToHome }
 }

+ 8 - 8
composables/utils/useValidationUtils.ts

@@ -1,10 +1,10 @@
-import ValidationUtils from "~/services/utils/validationUtils";
+import ValidationUtils from '~/services/utils/validationUtils'
 
-let validationUtilsClass:null|ValidationUtils = null
+let validationUtilsClass: null | ValidationUtils = null
 export const useValidationUtils = () => {
-    //Avoid memory leak
-    if(validationUtilsClass === null){
-        validationUtilsClass = new ValidationUtils()
-    }
-    return validationUtilsClass
-}
+  // Avoid memory leak
+  if (validationUtilsClass === null) {
+    validationUtilsClass = new ValidationUtils()
+  }
+  return validationUtilsClass
+}

+ 1 - 1
config/abilities/pages/myAccount.yaml

@@ -6,7 +6,7 @@
   attendance_bookings_page:
     action: 'display'
     conditions:
-      - {function: organizationHasAnyModule, parameters: ['Attendances']}
+      - { function: organizationHasAnyModule, parameters: ['Attendances'] }
       # TODO: l'action write existe-t-elle?
       - { function: accessHasAnyRoleAbility, parameters: [{action: 'write', subject: 'attendances'}] }
       - { function: accessIsAdminAccount, expectedResult: false }

+ 111 - 115
config/theme.ts

@@ -1,151 +1,147 @@
-
 interface Theme {
-    dark: boolean,
-    colors: {
-        /**
-         * @see Cf. doc/colors.md pour le rôle de chaque couleur
-         */
-        // Global
-        'background': string,
-        'on-background': string,
-        'surface': string,
-        'on-surface': string,
-        'primary': string,
-        'on-primary': string,
-        'primary-alt': string,
-        'on-primary-alt': string,
-        'secondary': string,
-        'on-secondary': string,
-        'secondary-alt': string,
-        'on-secondary-alt': string,
-        'neutral-strong': string,
-        'on-neutral-strong': string,
-        'neutral': string,
-        'on-neutral': string,
-        'on-neutral--clickable': string,
-        'neutral-soft': string,
-        'on-neutral-soft': string,
-        'neutral-very-soft': string,
-        'on-neutral-very-soft': string,
-        'danger': string,
-        'on-danger': string,
-        'success': string,
-        'on-success': string,
-        'warning': string,
-        'on-warning': string,
-        'info': string,
-        'on-info': string,
-
-        // Special cases
-        // TODO: voir ceux dont on peut se passer
-        'x-create-btn': string,
-        'on-x-create-btn': string
-    }
+  dark: boolean
+  colors: {
+    /**
+     * @see Cf. doc/colors.md pour le rôle de chaque couleur
+     */
+    // Global
+    background: string
+    'on-background': string
+    surface: string
+    'on-surface': string
+    primary: string
+    'on-primary': string
+    'primary-alt': string
+    'on-primary-alt': string
+    secondary: string
+    'on-secondary': string
+    'secondary-alt': string
+    'on-secondary-alt': string
+    'neutral-strong': string
+    'on-neutral-strong': string
+    neutral: string
+    'on-neutral': string
+    'on-neutral--clickable': string
+    'neutral-soft': string
+    'on-neutral-soft': string
+    'neutral-very-soft': string
+    'on-neutral-very-soft': string
+    danger: string
+    'on-danger': string
+    success: string
+    'on-success': string
+    warning: string
+    'on-warning': string
+    info: string
+    'on-info': string
+
+    // Special cases
+    // TODO: voir ceux dont on peut se passer
+    'x-create-btn': string
+    'on-x-create-btn': string
+  }
 }
 
-
 export const lightTheme: Theme = {
-    dark: false,
-    colors: {
-        'background': '#ecf0f5',
-        'on-background': '#000000',
+  dark: false,
+  colors: {
+    background: '#ecf0f5',
+    'on-background': '#000000',
 
-        'surface': '#ffffff',
-        'on-surface': '#000000',
+    surface: '#ffffff',
+    'on-surface': '#000000',
 
-        'primary': '#00ad8e',
-        'on-primary': '#ffffff',
+    primary: '#00ad8e',
+    'on-primary': '#ffffff',
 
-        'primary-alt': '#a9e0d6',
-        'on-primary-alt': '#777777',
+    'primary-alt': '#a9e0d6',
+    'on-primary-alt': '#777777',
 
-        'secondary': '#324150',
-        'on-secondary': '#f5f5f5',
+    secondary: '#324150',
+    'on-secondary': '#f5f5f5',
 
-        'secondary-alt': '#2a3745',
-        'on-secondary-alt': '#ffffff',
+    'secondary-alt': '#2a3745',
+    'on-secondary-alt': '#ffffff',
 
-        'neutral-strong': '#777777',
-        'on-neutral-strong': '#ecf0f5',
+    'neutral-strong': '#777777',
+    'on-neutral-strong': '#ecf0f5',
 
-        'neutral': '#e6e6e6',
-        'on-neutral': '#666666',
-        'on-neutral--clickable': '#00997d',
+    neutral: '#e6e6e6',
+    'on-neutral': '#666666',
+    'on-neutral--clickable': '#00997d',
 
-        'neutral-soft': '#f2f2f2',
-        'on-neutral-soft': '#333333',
+    'neutral-soft': '#f2f2f2',
+    'on-neutral-soft': '#333333',
 
-        'neutral-very-soft': '#ffffff',
-        'on-neutral-very-soft': '#333333',
+    'neutral-very-soft': '#ffffff',
+    'on-neutral-very-soft': '#333333',
 
-        // Content
-        'danger': '#f56954',
-        'on-danger': '#ffffff',
+    // Content
+    danger: '#f56954',
+    'on-danger': '#ffffff',
 
-        'success': '#00a65a',
-        'on-success': '#ffffff',
+    success: '#00a65a',
+    'on-success': '#ffffff',
 
-        'warning': '#f39c12',
-        'on-warning': '#ffffff',
+    warning: '#f39c12',
+    'on-warning': '#ffffff',
 
-        'info': '#3c8dbc',
-        'on-info': '#ffffff',
+    info: '#3c8dbc',
+    'on-info': '#ffffff',
 
-        'x-create-btn': '#f39c12',
-        'on-x-create-btn': '#ffffff',
-    }
+    'x-create-btn': '#f39c12',
+    'on-x-create-btn': '#ffffff',
+  },
 }
 
 export const darkTheme: Theme = {
-    dark: true,
-    colors: {
-        'background': '#1a1a1a',
-        'on-background': '#ffffff',
+  dark: true,
+  colors: {
+    background: '#1a1a1a',
+    'on-background': '#ffffff',
 
-        'surface': '#262626',
-        'on-surface': '#ffffff',
+    surface: '#262626',
+    'on-surface': '#ffffff',
 
-        'primary': '#00ad8e',
-        'on-primary': '#ffffff',
+    primary: '#00ad8e',
+    'on-primary': '#ffffff',
 
-        'primary-alt': '#a9e0d6',
-        'on-primary-alt': '#777777',
+    'primary-alt': '#a9e0d6',
+    'on-primary-alt': '#777777',
 
-        'secondary': '#324150',
-        'on-secondary': '#f5f5f5',
+    secondary: '#324150',
+    'on-secondary': '#f5f5f5',
 
-        'secondary-alt': '#2c3a48',
-        'on-secondary-alt': '#ffffff',
+    'secondary-alt': '#2c3a48',
+    'on-secondary-alt': '#ffffff',
 
-        'neutral-strong': '#595959',
-        'on-neutral-strong': '#ecf0f5',
+    'neutral-strong': '#595959',
+    'on-neutral-strong': '#ecf0f5',
 
-        'neutral': '#324150',
-        'on-neutral': '#cccccc',
-        'on-neutral--clickable': '#a9e0d6',
+    neutral: '#324150',
+    'on-neutral': '#cccccc',
+    'on-neutral--clickable': '#a9e0d6',
 
-        'neutral-soft': '#090c11',
-        'on-neutral-soft': '#cccccc',
+    'neutral-soft': '#090c11',
+    'on-neutral-soft': '#cccccc',
 
-        'neutral-very-soft': '#333333',
-        'on-neutral-very-soft': '#ecf0f5',
+    'neutral-very-soft': '#333333',
+    'on-neutral-very-soft': '#ecf0f5',
 
-        // Content
-        'danger': '#f56954',
-        'on-danger': '#ffffff',
+    // Content
+    danger: '#f56954',
+    'on-danger': '#ffffff',
 
-        'success': '#00a65a',
-        'on-success': '#ffffff',
+    success: '#00a65a',
+    'on-success': '#ffffff',
 
-        'warning': '#f39c12',
-        'on-warning': '#ffffff',
+    warning: '#f39c12',
+    'on-warning': '#ffffff',
 
-        'info': '#3c8dbc',
-        'on-info': '#ffffff',
+    info: '#3c8dbc',
+    'on-info': '#ffffff',
 
-        'x-create-btn': '#f39c12',
-        'on-x-create-btn': '#ffffff',
-    }
+    'x-create-btn': '#f39c12',
+    'on-x-create-btn': '#ffffff',
+  },
 }
-
-

+ 33 - 24
i18n.config.ts

@@ -1,26 +1,35 @@
-import {defineI18nConfig} from "#i18n";
+import { defineI18nConfig } from '#i18n'
 
 export default defineI18nConfig(() => ({
-        legacy: false,
-        datetimeFormats: {
-            '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'
-                }
-            }
-        }
-    })
-)
+  legacy: false,
+  datetimeFormats: {
+    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',
+      },
+    },
+  },
+}))

+ 2 - 1
lang/fr.json

@@ -680,5 +680,6 @@
   "cycles_breadcrumbs": "Enseignements",
   "cmf_licence_structure_breadcrumbs": "Licence CMF - Structure",
   "no_recorded_subdomain": "Aucun sous-domaine enregistré",
-  "no_admin_access_recorded": "Aucun compte super-admin enregistré"
+  "no_admin_access_recorded": "Aucun compte super-admin enregistré",
+  "redirecting": "Redirection en cours"
 }

+ 6 - 0
layouts/.eslintrc.cjs

@@ -0,0 +1,6 @@
+/** On désactive cette règle pour les pages et les layouts seulement : https://eslint.vuejs.org/rules/multi-word-component-names.html */
+module.exports = {
+  rules: {
+    'vue/multi-word-component-names': 0,
+  }
+}

+ 1 - 3
layouts/default.vue

@@ -11,7 +11,6 @@
       <LayoutMainMenu />
 
       <v-main class="main">
-
         <LayoutSubheader />
 
         <LayoutAlertBar class="mt-1" />
@@ -21,13 +20,12 @@
       </v-main>
 
       <LazyLayoutAlertContainer />
-
     </v-app>
   </div>
 </template>
 
 <script setup lang="ts">
-import {useLayoutStore} from "~/stores/layout";
+import { useLayoutStore } from '~/stores/layout'
 
 const layoutStore = useLayoutStore()
 layoutStore.name = 'default'

+ 21 - 15
layouts/error.vue

@@ -7,25 +7,31 @@
 </template>
 
 <script setup lang="ts">
-  import UrlUtils from "~/services/utils/urlUtils";
-  import {useLayoutStore} from "~/stores/layout";
+import UrlUtils from '~/services/utils/urlUtils'
+import { useLayoutStore } from '~/stores/layout'
 
-  const layoutStore = useLayoutStore()
-  layoutStore.name = 'error'
+const layoutStore = useLayoutStore()
+layoutStore.name = 'error'
 
-  const props = defineProps({
-    error: {
-      type: Object,
-      default: null
-    }
-  })
+const props = defineProps({
+  error: {
+    type: Object,
+    default: null,
+  },
+})
 
-  if(process.client && props.error.statusCode === 404 && process.env.NODE_ENV === 'production') {
-    const runtimeConfig = useRuntimeConfig()
-    navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, 'dashboard'), {external: true})
-  }
+if (
+  process.client &&
+  props.error.statusCode === 404 &&
+  process.env.NODE_ENV === 'production'
+) {
+  const runtimeConfig = useRuntimeConfig()
+  navigateTo(UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, 'dashboard'), {
+    external: true,
+  })
+}
 
-  const otherError = ref('Une erreur est parvenue')
+const otherError = ref('Une erreur est parvenue')
 </script>
 
 <style scoped>

+ 7 - 10
layouts/parameters.vue

@@ -11,7 +11,6 @@
       <LayoutParametersMenu />
 
       <v-main class="main">
-
         <LayoutSubheader />
 
         <LayoutAlertBar class="mt-1" />
@@ -23,24 +22,22 @@
       </v-main>
 
       <LazyLayoutAlertContainer />
-
     </v-app>
   </div>
 </template>
 
 <script setup lang="ts">
-
-import {useLayoutStore} from "~/stores/layout";
+import { useLayoutStore } from '~/stores/layout'
 
 const layoutStore = useLayoutStore()
 layoutStore.name = 'parameters'
 </script>
 
 <style scoped lang="scss">
-  .parameters-page-card {
-    background-color: rgb(var(--v-theme-neutral-very-soft));
-    color: rgb(var(--v-theme-on-neutral-very-soft));
-    margin: 3%;
-    padding: 24px;
-  }
+.parameters-page-card {
+  background-color: rgb(var(--v-theme-neutral-very-soft));
+  color: rgb(var(--v-theme-on-neutral-very-soft));
+  margin: 3%;
+  padding: 24px;
+}
 </style>

+ 5 - 5
models/Access/Access.ts

@@ -1,9 +1,9 @@
+import { HasOne, Num, Uid, Attr } from 'pinia-orm/dist/decorators'
 import type { Historical } from '~/types/interfaces'
-import Person from "~/models/Person/Person";
-import ApiModel from "~/models/ApiModel";
-import {HasOne, Num, Uid, Attr} from "pinia-orm/dist/decorators";
-import {IriEncoded} from "~/models/decorators";
-import Organization from "~/models/Organization/Organization";
+import Person from '~/models/Person/Person'
+import ApiModel from '~/models/ApiModel'
+import { IriEncoded } from '~/models/decorators'
+import Organization from '~/models/Organization/Organization'
 
 /**
  * AP2i Model : Access

+ 4 - 4
models/Access/AdminAccess.ts

@@ -1,5 +1,5 @@
-import {Str, Uid} from "pinia-orm/dist/decorators";
-import ApiResource from "~/models/ApiResource";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiResource from '~/models/ApiResource'
 
 /**
  * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/ApiResources/Access/AdminAccess.php
@@ -11,8 +11,8 @@ export default class AdminAccess extends ApiResource {
   declare id: number
 
   @Str(null)
-  declare username: string|null
+  declare username: string | null
 
   @Str(null)
-  declare email: string|null
+  declare email: string | null
 }

+ 4 - 4
models/Access/MyProfile.ts

@@ -1,8 +1,8 @@
-import {Num, Uid, Attr, Bool, Str} from 'pinia-orm/dist/decorators'
+import { Num, Uid, Attr, Bool, Str } from 'pinia-orm/dist/decorators'
 import type { Historical } from '~/types/interfaces'
-import Access from "~/models/Access/Access";
-import OrganizationProfile from "~/models/Organization/OrganizationProfile";
-import ApiResource from "~/models/ApiResource";
+import Access from '~/models/Access/Access'
+import OrganizationProfile from '~/models/Organization/OrganizationProfile'
+import ApiResource from '~/models/ApiResource'
 
 /**
  * Ap2i ApiResource : AccessProfile

+ 5 - 5
models/Access/PersonalizedList.ts

@@ -1,5 +1,5 @@
-import {Str, Uid} from "pinia-orm/dist/decorators";
-import ApiModel from "~/models/ApiModel";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * Ap2i Model : PersonalizedList
@@ -13,11 +13,11 @@ export default class PersonalizedList extends ApiModel {
   declare id: number | string | null
 
   @Str(null)
-  declare label:string|null
+  declare label: string | null
 
   @Str(null)
-  declare entity:string|null
+  declare entity: string | null
 
   @Str('')
-  declare menuKey:string
+  declare menuKey: string
 }

+ 2 - 3
models/ApiModel.ts

@@ -1,11 +1,10 @@
-import ApiResource from "~/models/ApiResource";
+import ApiResource from '~/models/ApiResource'
 
 /**
  * Entities from the API
  *
  * These models support CRUD operations
  */
-export class ApiModel extends ApiResource {
-}
+class ApiModel extends ApiResource {}
 
 export default ApiModel

+ 30 - 28
models/ApiResource.ts

@@ -1,40 +1,42 @@
-import {Model} from "pinia-orm";
+import { Model } from 'pinia-orm'
 
 /**
  * Base class for resources that can be fetched from the API
  */
-export class ApiResource extends Model {
+class ApiResource extends Model {
+  // eslint-disable-next-line no-use-before-define
+  protected static _iriEncodedFields: Record<string, ApiResource>
 
-    protected static _iriEncodedFields: Record<string, ApiResource>
-
-    public static addIriEncodedField(name: string, target: ApiResource) {
-        if (!this._iriEncodedFields) {
-            this._iriEncodedFields = {}
-        }
-        this._iriEncodedFields[name] = target
+  public static addIriEncodedField(name: string, target: ApiResource) {
+    if (!this._iriEncodedFields) {
+      this._iriEncodedFields = {}
     }
+    this._iriEncodedFields[name] = target
+  }
 
-    public static getIriEncodedFields() {
-        return this._iriEncodedFields
-    }
+  public static getIriEncodedFields() {
+    return this._iriEncodedFields
+  }
 
-    /**
-     * Fix the 'Cannot stringify arbitrary non-POJOs' warning, meaning server can not parse the store
-     *
-     * @see https://github.com/vuex-orm/vuex-orm/issues/255#issuecomment-876378684
-     */
-    toJSON () {
-        return { ...this }
-    }
+  /**
+   * Fix the 'Cannot stringify arbitrary non-POJOs' warning, meaning server can not parse the store
+   *
+   * @see https://github.com/vuex-orm/vuex-orm/issues/255#issuecomment-876378684
+   */
+  toJSON() {
+    return { ...this }
+  }
 
-    /**
-     * Is it a newly created entity?
-     *
-     * If it is, it means this entity does not exist in the data source and that it has a temporary id
-     */
-    public isNew(): boolean {
-        return !this.id || (typeof this.id === 'string' && this.id.slice(0, 3) === 'tmp')
-    }
+  /**
+   * Is it a newly created entity?
+   *
+   * If it is, it means this entity does not exist in the data source and that it has a temporary id
+   */
+  public isNew(): boolean {
+    return (
+      !this.id || (typeof this.id === 'string' && this.id.slice(0, 3) === 'tmp')
+    )
+  }
 }
 
 export default ApiResource

+ 3 - 3
models/Billing/ResidenceArea.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * Ap2i Model : ResidenceArea
@@ -13,5 +13,5 @@ export default class ResidenceArea extends ApiModel {
   declare id: number | string
 
   @Str(null)
-  declare label: string|null
+  declare label: string | null
 }

+ 7 - 9
models/Booking/AttendanceBookingReason.ts

@@ -1,7 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Attr, Str, Uid} from "pinia-orm/dist/decorators";
-import {IriEncoded} from "~/models/decorators";
-import Organization from "~/models/Organization/Organization";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * Motif d'absence ou de retard
@@ -9,11 +7,11 @@ import Organization from "~/models/Organization/Organization";
  * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/Entity/Booking/AttendanceBookingReason.php
  */
 export default class AttendanceBookingReason extends ApiModel {
-    static entity = 'attendance_booking_reasons'
+  static entity = 'attendance_booking_reasons'
 
-    @Uid()
-    declare id: number | string
+  @Uid()
+  declare id: number | string
 
-    @Str(null)
-    declare reason: string|null
+  @Str(null)
+  declare reason: string | null
 }

+ 9 - 9
models/Core/AddressPostal.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Num, Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import { Num, Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * Ap2i Model : AddressPostal
@@ -16,25 +16,25 @@ export default class AddressPostal extends ApiModel {
   declare organizationAddressPostalId: number | null
 
   @Str(null)
-  declare addressCountry: string|null
+  declare addressCountry: string | null
 
   @Str(null)
-  declare addressCity: string|null
+  declare addressCity: string | null
 
   @Str(null)
-  declare addressOwner: string|null
+  declare addressOwner: string | null
 
   @Str(null)
-  declare postalCode: string|null
+  declare postalCode: string | null
 
   @Str(null)
-  declare streetAddress: string|null
+  declare streetAddress: string | null
 
   @Str(null)
-  declare streetAddressSecond: string|null
+  declare streetAddressSecond: string | null
 
   @Str(null)
-  declare streetAddressThird: string|null
+  declare streetAddressThird: string | null
 
   @Num(0)
   declare latitude: number

+ 9 - 9
models/Core/BankAccount.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Bool, Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import { Bool, Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : BankAccount
@@ -13,25 +13,25 @@ export default class BankAccount extends ApiModel {
   declare id: number | string | null
 
   @Str(null)
-  declare bankName: string|null
+  declare bankName: string | null
 
   @Str(null)
-  declare bic: string|null
+  declare bic: string | null
 
   @Str(null)
-  declare bicInvalid: string|null
+  declare bicInvalid: string | null
 
   @Str(null)
-  declare iban: string|null
+  declare iban: string | null
 
   @Str(null)
-  declare ibanInvalid: string|null
+  declare ibanInvalid: string | null
 
   @Str(null)
-  declare debitAddress: string|null
+  declare debitAddress: string | null
 
   @Str(null)
-  declare holder: string|null
+  declare holder: string | null
 
   @Bool(false)
   declare principal: boolean

+ 10 - 10
models/Core/ContactPoint.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import { Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : ContactPoint
@@ -16,28 +16,28 @@ export default class ContactPoint extends ApiModel {
   declare contactType: string
 
   @Str(null)
-  declare email: string|null
+  declare email: string | null
 
   @Str(null)
-  declare emailInvalid: string|null
+  declare emailInvalid: string | null
 
   @Str(null)
-  declare telphone: string|null
+  declare telphone: string | null
 
   @Str(null)
-  declare telphoneInvalid: string|null
+  declare telphoneInvalid: string | null
 
   @Str(null)
-  declare mobilPhone: string|null
+  declare mobilPhone: string | null
 
   @Str(null)
-  declare mobilPhoneInvalid: string|null
+  declare mobilPhoneInvalid: string | null
 
   @Str(null)
-  declare faxNumber: string|null
+  declare faxNumber: string | null
 
   @Str(null)
-  declare faxNumberInvalid: string|null
+  declare faxNumberInvalid: string | null
 
   @Attr([])
   declare organization: []

+ 2 - 2
models/Core/Country.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Country

+ 2 - 2
models/Core/File.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Num, Str, Uid} from "pinia-orm/dist/decorators";
+import { Num, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : File

+ 5 - 5
models/Core/Notification.ts

@@ -1,6 +1,6 @@
-import NotificationMessage from "~/models/Core/NotificationMessage";
-import {HasOne, Str, Uid, Attr} from "pinia-orm/dist/decorators";
-import ApiModel from "~/models/ApiModel";
+import { Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import NotificationMessage from '~/models/Core/NotificationMessage'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Notification
@@ -23,10 +23,10 @@ export default class Notification extends ApiModel {
   declare createDate: string
 
   @Str(null)
-  declare type: string|null
+  declare type: string | null
 
   @Str(null)
-  declare link: string|null
+  declare link: string | null
 
   @Attr({})
   declare notificationUsers: Array<string>

+ 5 - 5
models/Core/NotificationMessage.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * // TODO: qu'est-ce que c'est?
@@ -11,11 +11,11 @@ export default class NotificationMessage extends ApiModel {
   declare id: number | string | null
 
   @Str(null)
-  declare about: string|null
+  declare about: string | null
 
   @Str(null)
-  declare action: string|null
+  declare action: string | null
 
   @Str(null)
-  declare fileName: string|null
+  declare fileName: string | null
 }

+ 4 - 4
models/Core/NotificationUsers.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Bool, Str, Uid} from "pinia-orm/dist/decorators";
+import { Bool, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : NotificationUser
@@ -13,10 +13,10 @@ export default class NotificationUsers extends ApiModel {
   declare id: number | string | null
 
   @Str(null)
-  declare notification: string|null
+  declare notification: string | null
 
   @Str(null)
-  declare access: string|null
+  declare access: string | null
 
   @Bool(false)
   declare isRead: boolean

+ 9 - 9
models/Core/Tagg.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Attr, Str, Uid} from "pinia-orm/dist/decorators";
+import { Attr, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Tagg
@@ -7,14 +7,14 @@ import {Attr, Str, Uid} from "pinia-orm/dist/decorators";
  * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/Entity/Core/Tagg.php
  */
 export default class Tagg extends ApiModel {
-    static entity = 'taggs'
+  static entity = 'taggs'
 
-    @Uid()
-    declare id: number | string | null
+  @Uid()
+  declare id: number | string | null
 
-    @Str('')
-    declare label: string
+  @Str('')
+  declare label: string
 
-    @Attr([])
-    declare organization: []
+  @Attr([])
+  declare organization: []
 }

+ 4 - 4
models/Education/Cycle.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Num, Str, Uid} from "pinia-orm/dist/decorators";
+import { Num, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model: Cycle
@@ -13,10 +13,10 @@ export default class Cycle extends ApiModel {
   declare id: number | string | null
 
   @Str(null)
-  declare label: string|null
+  declare label: string | null
 
   @Str(null)
-  declare cycleEnum: string|null
+  declare cycleEnum: string | null
 
   @Num(0)
   declare order: number

+ 2 - 2
models/Education/EducationTiming.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Num, Uid} from "pinia-orm/dist/decorators";
+import { Num, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : EducationTiming

+ 2 - 2
models/Export/LicenceCmfOrganizationER.ts

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

+ 2 - 2
models/Network/Network.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import { Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Network

+ 3 - 3
models/Network/NetworkOrganization.ts

@@ -1,6 +1,6 @@
-import Network from "~/models/Network/Network";
-import ApiModel from "~/models/ApiModel";
-import {HasOne, Str, Uid} from "pinia-orm/dist/decorators";
+import { HasOne, Str, Uid } from 'pinia-orm/dist/decorators'
+import Network from '~/models/Network/Network'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : NetworkOrganization

+ 9 - 9
models/OnlineRegistration/RegistrationAvailability.ts

@@ -1,18 +1,18 @@
-import ApiResource from "~/models/ApiResource";
-import {Bool, Str, Uid} from "pinia-orm/dist/decorators";
+import { Bool, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiResource from '~/models/ApiResource'
 
 /**
  * Disponibilité (ouverture) de l'IEL
  */
 export default class RegistrationAvailability extends ApiResource {
-    static entity = 'online_registration/availability'
+  static entity = 'online_registration/availability'
 
-    @Uid()
-    declare id: number | string | null
+  @Uid()
+  declare id: number | string | null
 
-    @Bool(false)
-    declare available: boolean
+  @Bool(false)
+  declare available: boolean
 
-    @Str(null)
-    declare message: string
+  @Str(null)
+  declare message: string
 }

+ 2 - 2
models/OnlineRegistration/RegistrationStatus.ts

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

+ 9 - 9
models/Organization/Cotisation.ts

@@ -1,5 +1,5 @@
-import ApiResource from "~/models/ApiResource";
-import {Num, Str, Uid} from "pinia-orm/dist/decorators";
+import { Num, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiResource from '~/models/ApiResource'
 
 /**
  * Ap2i ApiResource : Cotisation
@@ -7,14 +7,14 @@ import {Num, Str, Uid} from "pinia-orm/dist/decorators";
  * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/ApiResources/Cotisation/Cotisation.php
  */
 export default class Cotisation extends ApiResource {
-    static entity = 'cotisations'
+  static entity = 'cotisations'
 
-    @Uid()
-    declare id: number | string | null
+  @Uid()
+  declare id: number | string | null
 
-    @Str(null)
-    declare alertState: string | null
+  @Str(null)
+  declare alertState: string | null
 
-    @Num(null)
-    declare cotisationYear: number | null
+  @Num(null)
+  declare cotisationYear: number | null
 }

+ 20 - 15
models/Organization/DolibarrAccount.ts

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

+ 13 - 16
models/Organization/MobytUserStatus.ts

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

+ 34 - 34
models/Organization/Organization.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Bool, Str, Attr, Num, Uid} from "pinia-orm/dist/decorators";
+import { Bool, Str, Attr, Num, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Organization
@@ -13,103 +13,103 @@ export default class Organization extends ApiModel {
   declare id: number | string
 
   @Str(null)
-  declare name: string|null
+  declare name: string | null
 
   @Str(null)
-  declare acronym: string|null
+  declare acronym: string | null
 
   @Str(null)
-  declare siretNumber: string|null
+  declare siretNumber: string | null
 
   @Str(null)
-  declare apeNumber: string|null
+  declare apeNumber: string | null
 
   @Str(null)
-  declare waldecNumber: string|null
+  declare waldecNumber: string | null
 
   @Str(null)
-  declare identifier: string|null
+  declare identifier: string | null
 
   @Str(null)
-  declare ffecApproval: string|null
+  declare ffecApproval: string | null
 
   @Str(null)
-  declare description: string|null
+  declare description: string | null
 
   @Attr([])
   declare typeOfPractices: []
 
   @Str(null)
-  declare otherPractice: string|null
+  declare otherPractice: string | null
 
   @Str(null)
-  declare legalStatus: string|null
+  declare legalStatus: string | null
 
   @Str(null)
-  declare principalType: string|null
+  declare principalType: string | null
 
   @Str(null)
-  declare youngApproval: string|null
+  declare youngApproval: string | null
 
   @Str(null)
-  declare trainingApproval: string|null
+  declare trainingApproval: string | null
 
   @Str(null)
-  declare otherApproval: string|null
+  declare otherApproval: string | null
 
   @Str(null)
-  declare collectiveAgreement: string|null
+  declare collectiveAgreement: string | null
 
   @Str(null)
-  declare opca: string|null
+  declare opca: string | null
 
   @Str(null)
-  declare icomNumber: string|null
+  declare icomNumber: string | null
 
   @Str(null)
-  declare urssafNumber: string|null
+  declare urssafNumber: string | null
 
   @Str(null)
-  declare twitter: string|null
+  declare twitter: string | null
 
   @Str(null)
-  declare youtube: string|null
+  declare youtube: string | null
 
   @Str(null)
-  declare facebook: string|null
+  declare facebook: string | null
 
   @Str(null)
-  declare instagram: string|null
+  declare instagram: string | null
 
   @Bool(true, { notNullable: true })
   declare portailVisibility: boolean
 
   @Str(null)
-  declare image: string|null
+  declare image: string | null
 
   @Str(null)
-  declare creationDate: string|null
+  declare creationDate: string | null
 
   @Str(null)
-  declare prefectureName: string|null
+  declare prefectureName: string | null
 
   @Str(null)
-  declare prefectureNumber: string|null
+  declare prefectureNumber: string | null
 
   @Str(null)
-  declare declarationDate: string|null
+  declare declarationDate: string | null
 
   @Str(null)
-  declare tvaNumber: string|null
+  declare tvaNumber: string | null
 
   @Str(null)
-  declare schoolCategory: string|null
+  declare schoolCategory: string | null
 
   @Str(null)
-  declare typeEstablishment: string|null
+  declare typeEstablishment: string | null
 
   @Str(null)
-  declare typeEstablishmentDetail: string|null
+  declare typeEstablishmentDetail: string | null
 
   @Bool(false, { notNullable: true })
   declare isPerformanceContractor: boolean
@@ -124,5 +124,5 @@ export default class Organization extends ApiModel {
   declare pedagogicBudget: number
 
   @Str(null)
-  declare logo: string|null
+  declare logo: string | null
 }

+ 2 - 2
models/Organization/OrganizationAddressPostal.ts

@@ -1,6 +1,6 @@
+import { HasOne, Str, Uid } from 'pinia-orm/dist/decorators'
 import AddressPostal from '~/models/Core/AddressPostal'
-import ApiModel from "~/models/ApiModel";
-import {HasOne, Str, Uid} from "pinia-orm/dist/decorators";
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : OrganizationAddressPostal

+ 2 - 2
models/Organization/OrganizationArticle.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : OrganizationArticle

+ 5 - 5
models/Organization/OrganizationLicence.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : OrganizationLicence
@@ -16,11 +16,11 @@ export default class OrganizationLicence extends ApiModel {
   declare licensee: string
 
   @Str(null)
-  declare licenceNumber: string|null
+  declare licenceNumber: string | null
 
   @Str(null)
-  declare categorie: string|null
+  declare categorie: string | null
 
   @Str(null)
-  declare validityDate: string|null
+  declare validityDate: string | null
 }

+ 2 - 2
models/Organization/OrganizationNetwork.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Uid} from "pinia-orm/dist/decorators";
+import { Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 export default class OrganizationNetwork extends ApiModel {
   static entity = 'organization_networks'

+ 25 - 25
models/Organization/OrganizationProfile.ts

@@ -1,5 +1,5 @@
-import ApiResource from "~/models/ApiResource";
-import {Str, Uid, Attr, Bool, Num} from "pinia-orm/dist/decorators";
+import { Str, Uid, Attr, Bool, Num } from 'pinia-orm/dist/decorators'
+import ApiResource from '~/models/ApiResource'
 
 /**
  * ApiResource : OrganizationProfile
@@ -7,38 +7,38 @@ import {Str, Uid, Attr, Bool, Num} from "pinia-orm/dist/decorators";
  * @see https://gitlab.2iopenservice.com/opentalent/ap2i/-/blob/develop/src/ApiResources/Profile/OrganizationProfile.php
  */
 export default class OrganizationProfile extends ApiResource {
-    static entity = 'organization_profile'
+  static entity = 'organization_profile'
 
-    @Uid()
-    declare id: number | string | null
+  @Uid()
+  declare id: number | string | null
 
-    @Str(null)
-    declare name: string
+  @Str(null)
+  declare name: string
 
-    @Str(null)
-    declare product: string
+  @Str(null)
+  declare product: string
 
-    @Str(null)
-    declare legalStatus: string
+  @Str(null)
+  declare legalStatus: string
 
-    @Attr([])
-    declare networks: Array<string>
+  @Attr([])
+  declare networks: Array<string>
 
-    @Attr([])
-    declare modules: Array<string>
+  @Attr([])
+  declare modules: Array<string>
 
-    @Bool(false)
-    declare hasChildren: boolean
+  @Bool(false)
+  declare hasChildren: boolean
 
-    @Attr([])
-    declare parents: Array<number>
+  @Attr([])
+  declare parents: Array<{ id: number; name: string; website: string }>
 
-    @Bool(false)
-    declare showAdherentList: boolean
+  @Bool(false)
+  declare showAdherentList: boolean
 
-    @Num(null)
-    declare currentYear: null
+  @Num(null)
+  declare currentYear: null
 
-    @Num(null)
-    declare parametersId: null
+  @Num(null)
+  declare parametersId: null
 }

+ 4 - 4
models/Organization/Parameters.ts

@@ -1,8 +1,8 @@
-import ApiModel from '~/models/ApiModel'
 import { Bool, Num, Str, Uid, Attr } from 'pinia-orm/dist/decorators'
-import Access from "~/models/Access/Access";
-import {IriEncoded} from "~/models/decorators";
-import File from "~/models/Core/File";
+import ApiModel from '~/models/ApiModel'
+import Access from '~/models/Access/Access'
+import { IriEncoded } from '~/models/decorators'
+import File from '~/models/Core/File'
 
 /**
  * AP2i Model : Parameters

+ 2 - 2
models/Organization/Subdomain.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Bool, Str, Uid} from "pinia-orm/dist/decorators";
+import { Bool, Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Subdomain

+ 9 - 9
models/Organization/SubdomainAvailability.ts

@@ -1,15 +1,15 @@
-import ApiResource from "~/models/ApiResource";
-import {Str, Uid, Attr, Bool, Num} from "pinia-orm/dist/decorators";
+import { Str, Uid, Bool } from 'pinia-orm/dist/decorators'
+import ApiResource from '~/models/ApiResource'
 
 export default class SubdomainAvailability extends ApiResource {
-    static entity = 'subdomains/is_available'
+  static entity = 'subdomains/is_available'
 
-    @Uid()
-    declare id: number | string | null
+  @Uid()
+  declare id: number | string | null
 
-    @Str(null)
-    declare subdomain: string
+  @Str(null)
+  declare subdomain: string
 
-    @Bool(false)
-    declare available: boolean
+  @Bool(false)
+  declare available: boolean
 }

+ 2 - 2
models/Organization/TypeOfPractice.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid} from "pinia-orm/dist/decorators";
+import { Str, Uid } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : TypeOfPractice

+ 5 - 5
models/Person/Person.ts

@@ -1,5 +1,5 @@
-import ApiModel from "~/models/ApiModel";
-import {Str, Uid, Attr} from "pinia-orm/dist/decorators";
+import { Str, Uid, Attr } from 'pinia-orm/dist/decorators'
+import ApiModel from '~/models/ApiModel'
 
 /**
  * AP2i Model : Person
@@ -16,11 +16,11 @@ export default class Person extends ApiModel {
   declare accessId: number | null
 
   @Str(null)
-  declare username: string|null
+  declare username: string | null
 
   @Str(null)
-  declare name: string|null
+  declare name: string | null
 
   @Str(null)
-  declare givenName: string|null
+  declare givenName: string | null
 }

+ 9 - 10
models/decorators.ts

@@ -1,4 +1,4 @@
-import ApiResource from "~/models/ApiResource";
+import ApiResource from '~/models/ApiResource'
 
 /**
  * Decorates an ApiResource's property to signal it as a field that is provided
@@ -7,14 +7,13 @@ import ApiResource from "~/models/ApiResource";
  * If the property is decorated, the HydraNormalizer will parse the IRI when de-normalizing
  * to get the id(s), then re-encode it as IRI(s) when re-normalizing.
  */
-export function IriEncoded (
-    apiResource: typeof ApiResource
-): PropertyDecorator {
-    return (target: Object, propertyKey: string | symbol) => {
-        //@ts-ignore
-        const self = target.$self()
+export function IriEncoded(apiResource: typeof ApiResource): PropertyDecorator {
+  // We have to comply with the PropertyDecorator return type
+  // eslint-disable-next-line @typescript-eslint/ban-types
+  return (target: Object, propertyKey: string | symbol) => {
+    // @ts-expect-error The object is an ApiResource
+    const self = target.$self()
 
-        //@ts-ignore
-        self.addIriEncodedField(propertyKey, apiResource)
-    }
+    self.addIriEncodedField(propertyKey as string, apiResource)
+  }
 }

+ 6 - 4
models/models.ts

@@ -1,12 +1,14 @@
+import ApiResource from '~/models/ApiResource'
+
 const modules = import.meta.glob('~/models/*/*.ts')
-import ApiResource from "~/models/ApiResource";
 
 const models: Record<string, typeof ApiResource> = {}
 
 for (const path in modules) {
-    modules[path]().then((mod: any) => {
-        models[mod.default.entity] = mod.default
-    })
+  modules[path]().then((mod) => {
+    // @ts-expect-error On est dépendant du retour de import.meta.glob pour le type
+    models[mod.default.entity] = mod.default
+  })
 }
 
 export default models

+ 190 - 143
nuxt.config.ts

@@ -1,21 +1,27 @@
-import fs from 'fs';
+import fs from 'fs'
 import vuetify from 'vite-plugin-vuetify'
 
 let https = {}
 
-let transpile = ['vuetify', '@vuepic/vue-datepicker', 'pinia', 'pinia-orm', 'date-fns']
+const transpile = [
+  'vuetify',
+  '@vuepic/vue-datepicker',
+  'pinia',
+  'pinia-orm',
+  'date-fns',
+]
 
 if (!process.env.NUXT_ENV) {
-    throw Error('Missing environment file - Run yarn install')
+  throw new Error('Missing environment file - Run yarn install')
 }
 
 if (process.env.NUXT_ENV === 'dev') {
-    https = {
-        key: fs.readFileSync('env/local.app.opentalent.fr.key'),
-        cert: fs.readFileSync('env/local.app.opentalent.fr.crt'),
-    }
+  https = {
+    key: fs.readFileSync('env/local.app.opentalent.fr.key'),
+    cert: fs.readFileSync('env/local.app.opentalent.fr.crt'),
+  }
 } else {
-    transpile.push('lodash')
+  transpile.push('lodash')
 }
 
 /**
@@ -24,148 +30,189 @@ if (process.env.NUXT_ENV === 'dev') {
  * @see https://nuxt.com/docs/api/configuration/nuxt-config
  */
 export default defineNuxtConfig({
-    ssr: true,
-    experimental: {
-        // Fix the 'Cannot stringify non POJO' bug
-        // @see https://github.com/nuxt/nuxt/issues/20787
-        renderJsonPayloads: false
+  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: '',
+    supportUrl: '',
+    // Config within public will be also exposed to the client
+    public: {
+      env: '',
+      baseUrl: '',
+      baseUrlLegacy: '',
+      baseUrlAdminLegacy: '',
+      baseUrlTypo3: '',
+      baseUrlMercure: '',
+      supportUrl: '',
     },
-    runtimeConfig: {
-        // Private config that is only available on the server
-        env: '',
-        baseUrl: '',
-        baseUrlLegacy: '',
-        baseUrlAdminLegacy: '',
-        baseUrlTypo3: '',
-        baseUrlMercure: '',
-        supportUrl: '',
-        // Config within public will be also exposed to the client
-        public: {
-            env: '',
-            baseUrl: '',
-            baseUrlLegacy: '',
-            baseUrlAdminLegacy: '',
-            baseUrlTypo3: '',
-            baseUrlMercure: '',
-            supportUrl: '',
-        }
-    },
-    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/mstile-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: [
-        async (options, nuxt) => {
-            nuxt.hooks.hook('vite:extendConfig', config => (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' }
-                // })
-            ) as any );
+  },
+  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/mstile-144x144.png',
         },
-        [
-            '@pinia/nuxt',
-            {
-                autoImports: [
-                    // automatically imports `usePinia()`
-                    'defineStore',
-                    // automatically imports `usePinia()` as `usePiniaStore()`
-                    ['defineStore', 'definePiniaStore'],
-                ],
-            }
-        ],
-        '@pinia-orm/nuxt',
-        '@nuxtjs/i18n',
-        '@nuxt/devtools',
-        '@nuxt/image'
-    ],
-    vite: {
-        esbuild: {
-            drop: process.env.DEBUG ? [] : ['console', 'debugger'],
-            tsconfigRaw: {
-                compilerOptions: {
-                    experimentalDecorators: true,
-                }
-            }
+      ],
+      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',
         },
-        ssr: {
-            // with ssr enabled, this config is required to load vuetify properly
-            noExternal: ['vuetify']
+        {
+          rel: 'apple-touch-icon-precomposed',
+          sizes: '144x144',
+          href: '/favicon/apple-touch-icon-144x144.png',
         },
-        server : {
-            https,
-            //@ts-ignore
-            port: 443,
-            hmr: {
-                protocol: 'wss',
-                port: 24678
-            }
-        }
+        {
+          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',
+        },
+      ],
     },
-    // Hide the sourcemaps warnings with vuetify
-    // @see https://github.com/vuetifyjs/vuetify-loader/issues/290#issuecomment-1435702713
-    sourcemap: {
-        server: false,
-        client: false,
+  },
+  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' }
+          // })
+        ),
+      )
     },
-    i18n: {
-        langDir: 'lang',
-        lazy: true,
-        locales: [
-            {
-                code: 'en',
-                iso: 'en-US',
-                file: 'en.json',
-                name: 'English'
-            },
-            {
-                code: 'fr',
-                iso: 'fr-FR',
-                file: 'fr.json',
-                name: 'Français'
-            }
+    [
+      '@pinia/nuxt',
+      {
+        autoImports: [
+          // automatically imports `usePinia()`
+          'defineStore',
+          // automatically imports `usePinia()` as `usePiniaStore()`
+          ['defineStore', 'definePiniaStore'],
         ],
-        defaultLocale: 'fr',
-        detectBrowserLanguage: false,
-        vueI18n: './i18n.config.ts'
+      },
+    ],
+    '@pinia-orm/nuxt',
+    '@nuxtjs/i18n',
+    '@nuxt/devtools',
+    '@nuxt/image',
+  ],
+  vite: {
+    esbuild: {
+      drop: process.env.DEBUG ? [] : ['console', 'debugger'],
+      tsconfigRaw: {
+        compilerOptions: {
+          experimentalDecorators: true,
+        },
+      },
     },
-    image: {
-        provider: 'none'
+    ssr: {
+      // with ssr enabled, this config is required to load vuetify properly
+      noExternal: ['vuetify'],
     },
-    build: {
-        transpile: transpile,
-    }
+    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,
+    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,
+  },
 })

+ 3 - 2
package.json

@@ -16,7 +16,8 @@
     "lint-fix": "eslint --fix --ext \".ts,.js,.vue\" --ignore-path .gitignore .",
     "test": "vitest run",
     "enable-devtools": "nuxi devtools enable",
-    "nuxt-upgrade": "nuxi upgrade --force"
+    "nuxt-upgrade": "nuxi upgrade --force",
+    "eslint":  "eslint ."
   },
   "dependencies": {
     "@casl/ability": "^6.5.0",
@@ -24,7 +25,7 @@
     "@fortawesome/fontawesome-free": "^6.5.1",
     "@mdi/font": "^7.3.67",
     "@nuxt/image": "1.1.0",
-    "@nuxtjs/i18n": "^8.0.0-rc.8",
+    "@nuxtjs/i18n": "^8.2.0",
     "@pinia-orm/nuxt": "^1.7.0",
     "@pinia/nuxt": "0.5.1",
     "@vuepic/vue-datepicker": "^7.4.0",

+ 6 - 0
pages/.eslintrc.cjs

@@ -0,0 +1,6 @@
+/** On désactive cette règle pour les pages et les layouts seulement : https://eslint.vuejs.org/rules/multi-word-component-names.html */
+module.exports = {
+  rules: {
+    'vue/multi-word-component-names': 0,
+  }
+}

+ 32 - 31
pages/cmf_licence_structure.vue

@@ -1,32 +1,26 @@
 <template>
   <div class="d-flex flex-column align-center">
-    <h2 class="ma-4">{{ $t('cmf_structure_licence')}}</h2>
+    <h2 class="ma-4">{{ $t('cmf_structure_licence') }}</h2>
     <a
-        href="https://www.cmf-musique.org/services/tarifs-preferentiels/"
-        target="_blank"
+      href="https://www.cmf-musique.org/services/tarifs-preferentiels/"
+      target="_blank"
     >
-      {{ $t('cmf_licence_details_url')}}
+      {{ $t('cmf_licence_details_url') }}
     </a>
 
-    <v-form
-        ref="form"
-        lazy-validation
-    >
+    <v-form ref="form" lazy-validation>
       <div class="ma-12">
-        <v-btn
-            v-if="!pending && file === null"
-            @click="submit"
-        >
+        <v-btn v-if="!pending && file === null" @click="submit">
           {{ $t('generate') }}
         </v-btn>
 
         <v-btn
-            v-else
-            color="primary"
-            :loading="pending"
-            :disabled="pending"
-            target="_blank"
-            @click="download"
+          v-else
+          color="primary"
+          :loading="pending"
+          :disabled="pending"
+          target="_blank"
+          @click="download"
         >
           {{ $t('download') }}
         </v-btn>
@@ -36,19 +30,21 @@
 </template>
 
 <script setup lang="ts">
-import File from "~/models/Core/File"
-import type {ComputedRef, Ref} from "vue";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useSseStore} from "~/stores/sse";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import LicenceCmfOrganizationER from "~/models/Export/LicenceCmfOrganizationER";
-import {useDownloadFile} from "~/composables/utils/useDownloadFile";
+import type { ComputedRef, Ref } from 'vue'
+import File from '~/models/Core/File'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useSseStore } from '~/stores/sse'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import LicenceCmfOrganizationER from '~/models/Export/LicenceCmfOrganizationER'
+import { useDownloadFile } from '~/composables/utils/useDownloadFile'
 
 const { em } = useEntityManager()
 const { getRef } = useEntityFetch()
 
 const sseStore = useSseStore()
-const async = () => { return sseStore.connected }
+const async = () => {
+  return sseStore.connected
+}
 
 const fileId = ref(null)
 
@@ -65,7 +61,11 @@ const submitting: Ref<boolean> = ref(false)
 const downloading: Ref<boolean> = ref(false)
 
 const pending: ComputedRef<boolean> = computed(() => {
-  return submitting.value || (file.value !== null && file.value.status === 'PENDING') || downloading.value
+  return (
+    submitting.value ||
+    (file.value !== null && file.value.status === 'PENDING') ||
+    downloading.value
+  )
 })
 
 const submit = async () => {
@@ -97,12 +97,13 @@ const submit = async () => {
     for (let i = 0; i < 3; i++) {
       setTimeout(async () => {
         if (file.value && file.value.status === 'PENDING') {
-          console.warn("File's status has not been updated : force a status checkup")
+          console.warn(
+            "File's status has not been updated : force a status checkup",
+          )
           await em.fetch(File, receipt.fileId, true)
         }
       }, i * 4000)
     }
-
   } finally {
     submitting.value = false
   }
@@ -112,12 +113,12 @@ const download = async () => {
   downloading.value = true
 
   if (file.value === null) {
-    console.error("File is not defined yet, impossible to download")
+    console.error('File is not defined yet, impossible to download')
     return
   }
 
   if (file.value.status === 'PENDING') {
-    console.error("File is still pending, impossible to download")
+    console.error('File is still pending, impossible to download')
     return
   }
 

+ 0 - 11
pages/index.vue

@@ -1,11 +0,0 @@
-<template>
-  <main>
-    <nuxt-link to="/poc/1">Goto organizations</nuxt-link>
-  </main>
-</template>
-
-<script setup lang="ts">
-
-</script>
-
-

+ 18 - 26
pages/organization.vue → pages/organization.vue.off

@@ -8,9 +8,7 @@
         <template #block1>
           {{ organization.name }}
         </template>
-        <template #block2>
-          N°Siret : {{ organization.siretNumber }}
-        </template>
+        <template #block2> N°Siret : {{ organization.siretNumber }} </template>
         <template #block3>
           {{ organization.description }}
         </template>
@@ -19,29 +17,26 @@
       <!-- Rend le contenu de la page -->
       <NuxtPage />
     </LayoutContainer>
-    <LayoutContainer v-else>
-      Pending : {{ emPending }}
-    </LayoutContainer>
+    <LayoutContainer v-else> Pending : {{ emPending }} </LayoutContainer>
   </div>
 </template>
 
 <script setup lang="ts">
-
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {computed, ComputedRef, Ref} from "@vue/reactivity";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import Organization from "~/models/Organization/Organization";
-import ContactPoint from "~/models/Core/ContactPoint";
-import BankAccount from "~/models/Core/BankAccount";
-import OrganizationAddressPostal from "~/models/Organization/OrganizationAddressPostal";
-import AddressPostal from "~/models/Core/AddressPostal";
-import Country from "~/models/Core/Country";
-import TypeOfPractice from "~/models/Organization/TypeOfPractice";
-import Network from "~/models/Network/Network";
-import NetworkOrganization from "~/models/Network/NetworkOrganization";
-import OrganizationArticle from "~/models/Organization/OrganizationArticle";
-import File from "~/models/Core/File";
+import { computed, ComputedRef } from 'vue'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import Organization from '~/models/Organization/Organization'
+import ContactPoint from '~/models/Core/ContactPoint'
+import BankAccount from '~/models/Core/BankAccount'
+import OrganizationAddressPostal from '~/models/Organization/OrganizationAddressPostal'
+import AddressPostal from '~/models/Core/AddressPostal'
+import Country from '~/models/Core/Country'
+import TypeOfPractice from '~/models/Organization/TypeOfPractice'
+import Network from '~/models/Network/Network'
+import NetworkOrganization from '~/models/Network/NetworkOrganization'
+import OrganizationArticle from '~/models/Organization/OrganizationArticle'
+import File from '~/models/Core/File'
 
 const { em, pending: emPending } = useEntityManager()
 
@@ -55,19 +50,16 @@ const { fetch } = useEntityFetch()
 const { pending } = fetch(Organization, id)
 
 // Get file from store
-const organization: ComputedRef<Organization> = computed( () => {
+const organization: ComputedRef<Organization> = computed(() => {
   return em.find(Organization, id)
 })
 
-
-
 // TODO: restaurer le middleware  (peut-être utiliser beforeMount?)
 // middleware({ $ability, redirect }) {
 //   if(!$ability.can('display', 'organization_page'))
 //     return redirect('/error')
 // }
 
-
 onBeforeUnmount(() => {
   console.log('flush store') // TODO: vérifier le bon fonctionnement
   em.flush(Organization)

+ 0 - 548
pages/organization/index.vue

@@ -1,548 +0,0 @@
-<!--
-Contenu de la page pages/organization.vue
-Contient toutes les informations sur l'organization courante
--->
-<template>
-  <LayoutContainer>
-    <UiForm v-if="!pending" :model="models().Organization" :entity="organization">
-      <template #form.input="{model, entity: organization}">
-        <v-expansion-panels :value="panel" focusable accordion>
-          <!-- Description -->
-          <UiExpansionPanel id="description" icon="fa-info">
-            <v-container fluid class="container">
-              <v-row>
-                <v-col cols="12" sm="6">
-                  <UiInputText field="name" v-model="organization.name" :rules="rules.name" />
-                </v-col>
-
-                <v-col cols="12" sm="6">
-                  <UiInputText field="acronym" v-model="organization.acronym" />
-                </v-col>
-
-                <v-col v-if="organizationProfile.isInsideNetwork()" cols="12" sm="6">
-                  <UiInputText
-                      :label="organizationProfile.isCmf() ? 'identifierCmf' : 'identifierFfec'"
-                      field="identifier"
-                      v-model="organization.identifier"
-                  />
-                </v-col>
-
-                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">
-                  <UiInputText field="ffecApproval" v-model="organization.ffecApproval"/>
-                </v-col>
-
-                <v-col cols="12" sm="6">
-                  <UiInputText field="description" v-model="organization.description"/>
-                </v-col>
-
-                <v-col cols="12" sm="6">
-                  <div>
-                    <span>{{ $t('logo') }}</span>
-                    <UiHelp right>
-                      <p v-html="$t('logo_upload')"/>
-                    </UiHelp>
-                  </div>
-                  <UiImage
-                    :imageId="getIdFromUri(organization.logo)"
-                    :width="200"
-                    field="logo"
-                    :ownerId="id"
-                  ></UiImage>
-                </v-col>
-
-                <v-col v-if="!organizationProfile.isManagerProduct()" cols="12" sm="6">
-<!--                  <UiInputEnum field="principalType" v-model="organization.principalType" enum-type="organization_principal_type"/>-->
-                </v-col>
-
-<!--                <v-col v-if="!organizationProfile.isFfec() && !organizationProfile.isManagerProduct() && !organizationProfile.isArtist()" cols="12" sm="6">-->
-<!--                  <UiInputEnum field="schoolCategory" v-model="organization.schoolCategory" enum-type="organization_school_cat"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
-<!--                  <UiInputEnum field="typeEstablishment" v-model="organization.typeEstablishment" enum-type="organization_type_establishment"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col v-if="organization.typeEstablishment === 'MULTIPLE'" cols="12" sm="6">-->
-<!--                  <UiInputEnum field="typeEstablishmentDetail" v-model="organization.typeEstablishmentDetail" enum-type="organization_type_establishment_detail" />-->
-<!--                </v-col>-->
-
-                <v-col cols="12" sm="6" v-if="organizationProfile.isCmf()">
-                  <div class="d-flex flex-row">
-<!--                    <UiInputAutocomplete-->
-<!--                      field="typeOfPractices"-->
-<!--                      :items="typeOfPractices"-->
-<!--                      :isLoading="typeOfPracticesFetchingState.pending"-->
-<!--                      :item-text="['name']"-->
-<!--                      :data="getIdsFromUris(organization.typeOfPractices)"-->
-<!--                      :translate="true"-->
-<!--                      :multiple="true"-->
-<!--                      group="category"-->
-<!--                      :rules="rules.typeOfPractice"-->
-<!--                      @update="updateRepository($event.map((id) => `/api/type_of_practices/${id}`), 'typeOfPractices')"-->
-<!--                      class="flex"-->
-<!--                    />-->
-                    <UiHelp>
-                      {{ $t('type_of_practices_autocomplete') }}
-                    </UiHelp>
-                  </div>
-                </v-col>
-
-                <!-- TODO: essayer de faire une condition plus explicite pour le v-if -->
-<!--                <v-col cols="12" sm="6" v-if="getIdsFromUris(organization.typeOfPractices).indexOf(37) >= 0">-->
-<!--                  <UiInputTextArea field="otherPractice" v-model="organization.otherPractice" />-->
-<!--                </v-col>-->
-              </v-row>
-            </v-container>
-          </UiExpansionPanel>
-
-          <!-- Adresses -->
-<!--          <UiExpansionPanel id="address_postal" icon="fa-globe-europe">-->
-<!--            <v-container fluid class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="12">-->
-<!--                  <UiCollection-->
-<!--                    :model="models().OrganizationAddressPostal"-->
-<!--                    :parent="entity"-->
-<!--                    loaderType="image"-->
-<!--                    newLink="/organization/address/new"-->
-<!--                  >-->
-<!--                    <template #list.item="{items}">-->
-<!--                      <v-container fluid>-->
-<!--                        <v-row dense>-->
-<!--                          <v-col-->
-<!--                            v-for="item in items"-->
-<!--                            :key="item.id"-->
-<!--                            cols="4"-->
-<!--                          >-->
-<!--                            <UiCard-->
-<!--                              :link="`/organization/address/${item.id}`"-->
-<!--                              :model="models().OrganizationAddressPostal"-->
-<!--                              :entity="item"-->
-<!--                            >-->
-<!--                              <template #card.title>-->
-<!--                                {{ $t(item.type) }}-->
-<!--                              </template>-->
-<!--                              <template #card.text>-->
-<!--                                {{ item.addressPostal.streetAddress }} <br>-->
-<!--                                <span v-if="item.addressPostal.streetAddressSecond">{{ item.addressPostal.streetAddressSecond }} <br></span>-->
-<!--                                <span v-if="item.addressPostal.streetAddressThird">{{ item.addressPostal.streetAddressThird }} <br></span>-->
-<!--                                {{ item.addressPostal.postalCode }} {{ item.addressPostal.addressCity }}<br>-->
-<!--                                <span v-if="item.addressPostal.addressCountry">-->
-<!--                                  <UiItemFromUri-->
-<!--                                    :model="models().Country"-->
-<!--                                    :query="repositories().countryRepository.query()"-->
-<!--                                    :uri="item.addressPostal.addressCountry"-->
-<!--                                  >-->
-<!--                                    <template #item.text="{item}">-->
-<!--                                      {{item.name}}-->
-<!--                                    </template>-->
-<!--                                  </UiItemFromUri>-->
-<!--                                </span>-->
-<!--                              </template>-->
-<!--                            </UiCard>-->
-<!--                          </v-col>-->
-<!--                        </v-row>-->
-<!--                      </v-container>-->
-<!--                    </template>-->
-<!--                  </UiCollection>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash;  Point de Contact&ndash;&gt;-->
-<!--          <UiExpansionPanel id="contact_point" icon="fa-phone">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="12">-->
-<!--                  <UiCollection-->
-<!--                    :model="models().ContactPoint"-->
-<!--                    :parent="entity"-->
-<!--                    loaderType="image"-->
-<!--                    newLink="/organization/contact_points/new"-->
-<!--                  >-->
-<!--                    <template #list.item="{items}">-->
-<!--                      <v-container fluid>-->
-<!--                        <v-row :dense="true">-->
-<!--                          <v-col-->
-<!--                            v-for="item in items"-->
-<!--                            :key="item.id"-->
-<!--                            cols="4"-->
-<!--                          >-->
-<!--                            <UiCard-->
-<!--                              :link="`/organization/contact_points/${item.id}`"-->
-<!--                              :model="models().ContactPoint"-->
-<!--                              :entity="item"-->
-<!--                            >-->
-<!--                              <template #card.title>-->
-<!--                                {{ $t(item.contactType) }}-->
-<!--                              </template>-->
-<!--                              <template #card.text>-->
-<!--                                <span v-if="item.email"><strong>{{ $t('email') }}</strong> : {{ item.email }} <br></span>-->
-<!--                                <span v-if="item.emailInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('emailInvalid') }}</strong> : {{ item.emailInvalid }} <br></span>-->
-
-<!--                                <span v-if="item.telphone"><strong>{{ $t('telphone') }}</strong> : {{ formatPhoneNumber(item.telphone) }} <br></span>-->
-<!--                                <span v-if="item.telphoneInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('telphoneInvalid') }}</strong> : {{ formatPhoneNumber(item.telphoneInvalid) }} <br></span>-->
-
-<!--                                <span v-if="item.mobilPhone"><strong>{{ $t('mobilPhone') }}</strong> : {{ formatPhoneNumber(item.mobilPhone) }} <br></span>-->
-<!--                                <span v-if="item.mobilPhoneInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('mobilPhoneInvalid') }}</strong> : {{ formatPhoneNumber(item.mobilPhoneInvalid) }} </span>-->
-<!--                              </template>-->
-<!--                            </UiCard>-->
-<!--                          </v-col>-->
-<!--                        </v-row>-->
-<!--                      </v-container>-->
-<!--                    </template>-->
-<!--                  </UiCollection>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash; Informations légales &ndash;&gt;-->
-<!--          <UiExpansionPanel id="legalInformation" icon="fa-gavel">-->
-<!--            <v-container fluid class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText-->
-<!--                    field="siretNumber"-->
-<!--                    :data="entity['siretNumber']"-->
-<!--                    :error="siretError"-->
-<!--                    :error-message="siretErrorMessage"-->
-<!--                    :rules="rules.siretRule"-->
-<!--                    @update="checkSiretHook($event, 'siretNumber')"-->
-<!--                  />-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="apeNumber" :data="entity['apeNumber']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col v-if="entity['legalStatus'] === 'ASSOCIATION_LAW_1901'" cols="12" sm="6">-->
-<!--                  <UiInputText field="waldecNumber" :data="entity['waldecNumber']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputDatePicker field="creationDate" :data="entity['creationDate']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="prefectureName" :data="entity['prefectureName']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="prefectureNumber" :data="entity['prefectureNumber']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputDatePicker field="declarationDate" :data="entity['declarationDate']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="tvaNumber" :data="entity['tvaNumber']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputEnum field="legalStatus" :data="entity['legalStatus']" enum-type="organization_legal"/>-->
-<!--                </v-col>-->
-
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash;  Agréments &ndash;&gt;-->
-<!--          <UiExpansionPanel id="agrements" icon="fa-certificate">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="youngApproval" :data="entity['youngApproval']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="trainingApproval" :data="entity['trainingApproval']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="otherApproval" :data="entity['otherApproval']"/>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash; Salariés &ndash;&gt;-->
-<!--          <UiExpansionPanel id="salary" icon="fa-users">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="collectiveAgreement" :data="entity['collectiveAgreement']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputEnum field="opca" :data="entity['opca']" enum-type="organization_opca"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="icomNumber" :data="entity['icomNumber']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="urssafNumber" :data="entity['urssafNumber']"/>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash; Réseaux &ndash;&gt;-->
-<!--          <UiExpansionPanel v-if="organizationProfile.isInsideNetwork()" id="network" icon="fa-share-alt">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="12">-->
-<!--                  <UiCollection-->
-<!--                    :model="models().NetworkOrganization"-->
-<!--                    :parent="entity"-->
-<!--                    loaderType="text"-->
-<!--                  >-->
-<!--                    <template #list.item="{items}">-->
-<!--                      <div v-for="item in items" :key="item.id">-->
-<!--                        <span>{{ item.network.name }}</span> - <span>{{$t('first_subscription')}} : <UiTemplateDate :data="item.startDate" /></span>-->
-<!--                      </div>-->
-<!--                    </template>-->
-<!--                  </UiCollection>-->
-<!--                </v-col>-->
-<!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
-<!--                  <UiInputText field="budget" :data="entity['budget']" type="number" />-->
-<!--                </v-col>-->
-
-<!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
-<!--                  <UiInputCheckbox field="isPedagogicIsPrincipalActivity" :data="entity['isPedagogicIsPrincipalActivity']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
-<!--                  <UiInputText field="pedagogicBudget" :data="entity['pedagogicBudget']" type="number"/>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash; Communication &ndash;&gt;-->
-<!--          <UiExpansionPanel id="communication" icon="fa-rss">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="twitter" :data="entity['twitter']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="youtube" :data="entity['youtube']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="facebook" :data="entity['facebook']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputText field="instagram" :data="entity['instagram']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <UiInputCheckbox field="portailVisibility" :data="entity['portailVisibility']"/>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="6">-->
-<!--                  <div class="d-flex flex-column">-->
-<!--                    <UiHelp class="d-flex flex-row">-->
-<!--                      <span>{{ $t('image') }}</span>-->
-<!--                      <p v-html="$t('communication_image_upload')"/>-->
-<!--                    </UiHelp>-->
-<!--                    <UiImage-->
-<!--                      :id="getIdFromUri(entity['image'])"-->
-<!--                      :upload="true"-->
-<!--                      :width="200"-->
-<!--                      field="image"-->
-<!--                      :ownerId="id"-->
-
-<!--                    ></UiImage>-->
-<!--                  </div>-->
-<!--                </v-col>-->
-
-<!--                <v-col cols="12" sm="12">-->
-<!--                  <UiCollection-->
-<!--                    :model="models().OrganizationArticle"-->
-<!--                    :parent="entity"-->
-<!--                    loaderType="text"-->
-<!--                  >-->
-<!--                    <template #list.item="{items}">-->
-<!--                      <h4 class="neutral-strong&#45;&#45;text font-weight-regular">{{$t('organizationArticle')}}</h4>-->
-<!--                      <UiTemplateDataTable-->
-<!--                        :headers="[-->
-<!--                          { text: $t('title'), value: 'title' },-->
-<!--                          { text: $t('link'), value: 'link' },-->
-<!--                          { text: $t('date'), value: 'date' },-->
-<!--                        ]"-->
-<!--                        :items="items"-->
-<!--                      >-->
-<!--                        <template #item.date="{item}">-->
-<!--                          <UiTemplateDate :data="item.date" />-->
-<!--                        </template>-->
-<!--                      </UiTemplateDataTable>-->
-<!--                    </template>-->
-<!--                  </UiCollection>-->
-<!--                </v-col>-->
-
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-
-<!--          &lt;!&ndash; IBAN &ndash;&gt;-->
-<!--          <UiExpansionPanel id="bank_account" icon="fa-euro-sign">-->
-<!--            <v-container class="container">-->
-<!--              <v-row>-->
-<!--                <v-col cols="12" sm="12">-->
-<!--                  <UiCollection-->
-<!--                    :model="models().BankAccount"-->
-<!--                    :parent="entity"-->
-<!--                    loaderType="image"-->
-<!--                    newLink="/organization/bank_account/new"-->
-<!--                  >-->
-<!--                    <template #list.item="{items}">-->
-<!--                      <v-container fluid>-->
-<!--                        <v-row :dense="true">-->
-<!--                          <v-col-->
-<!--                            v-for="item in items"-->
-<!--                            :key="item.id"-->
-<!--                            cols="4"-->
-<!--                          >-->
-<!--                            <UiCard-->
-<!--                              :id="item.id"-->
-<!--                              :link="`/organization/bank_account/${item.id}`"-->
-<!--                              :model="models().BankAccount"-->
-<!--                            >-->
-<!--                              <template #card.text>-->
-<!--                                <span v-if="item.bankName"><strong>{{ $t('bankName') }}</strong> : {{ item.bankName }} <br></span>-->
-
-<!--                                <span v-if="item.bic"><strong>{{ $t('bic') }}</strong> : {{ item.bic }} <br></span>-->
-<!--                                <span v-if="item.bicInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('bicInvalid') }}</strong> : {{ item.bicInvalid }} <br></span>-->
-
-<!--                                <span v-if="item.iban"><strong>{{ $t('iban') }}</strong> : {{ item.iban }} <br></span>-->
-<!--                                <span v-if="item.ibanInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('ibanInvalid') }}</strong> : {{ item.ibanInvalid }} <br></span>-->
-
-<!--                              </template>-->
-<!--                            </UiCard>-->
-<!--                          </v-col>-->
-<!--                        </v-row>-->
-<!--                      </v-container>-->
-<!--                    </template>-->
-<!--                  </UiCollection>-->
-<!--                </v-col>-->
-<!--              </v-row>-->
-<!--            </v-container>-->
-<!--          </UiExpansionPanel>-->
-        </v-expansion-panels>
-      </template>
-    </UiForm>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import TypeOfPractice from "~/models/Organization/TypeOfPractice";
-import {computed, reactive, ref} from "@vue/reactivity";
-import type {ComputedRef} from "@vue/reactivity";
-import {useExtensionPanel} from "~/composables/layout/useExtensionPanel";
-import {useRoute} from "#app";
-import { useValidation } from "~/composables/form/useValidation";
-import {useEntityManager} from "~/composables/data/useEntityManager";
-import UrlUtils from "~/services/utils/urlUtils";
-import Organization from "~/models/Organization/Organization";
-import {useI18nUtils} from "~/composables/utils/useI18nUtils";
-import ContactPoint from "~/models/Core/ContactPoint";
-import BankAccount from "~/models/Core/BankAccount";
-import OrganizationAddressPostal from "~/models/Organization/OrganizationAddressPostal";
-import Country from "~/models/Core/Country";
-import NetworkOrganization from "~/models/Network/NetworkOrganization";
-import OrganizationArticle from "~/models/Organization/OrganizationArticle";
-import {useI18n} from "vue-i18n";
-
-const id: number | null = useOrganizationProfileStore().id
-if (id === null) {
-  throw new Error('Missing organization id')
-}
-
-const organizationProfile = reactive($organizationProfile())
-
-const { em } = useEntityManager()
-const { fetch, fetchCollection } = useEntityFetch()
-const { pending } = fetch(Organization, id)
-
-const organization: ComputedRef<Organization> = computed(() => {
-  return em.find(Organization, id)
-})
-
-const { data: typeOfPractices, pending: typeOfPracticesPending } = fetchCollection(TypeOfPractice)
-
-const route = ref(useRoute())
-const { panel } = useExtensionPanel(route)
-
-const { siretError, siretErrorMessage, validateSiret } = useValidation().useValidateSiret()
-
-const validateSiretHook = async (siret: string, field: string, updateRepository: any) => {
-  await validateSiret(siret)
-  if (!siretError.value) {
-    em.save(Organization, organization.value)
-  }
-}
-
-const formatPhoneNumber = (number: string): string => {
-  return useI18nUtils().formatPhoneNumber(number)
-}
-
-// TODO: voir si l'extraction de cette id ne pourrait pas être faite en amont, au niveau des post-processors
-const getIdsFromUris = (uris: Array<string>) => {
-  const ids:Array<any> = []
-  for(const uri of uris){
-    ids.push(UrlUtils.extractIdFromUri(uri))
-  }
-  return ids
-}
-
-// TODO: voir si l'extraction de cette id ne pourrait pas être faite en amont, au niveau des post-processors
-const getIdFromUri = (uri: string) => UrlUtils.extractIdFromUri(uri)
-
-const models = () => {
-  return {
-    Organization,
-    ContactPoint,
-    BankAccount,
-    OrganizationAddressPostal,
-    Country,
-    NetworkOrganization,
-    OrganizationArticle
-  }
-}
-
-const i18n = useI18n()
-
-const rules = {
-  name: [
-    (nameValue: string) => !!nameValue || i18n.t('required'),
-    (nameValue: string) => (nameValue || '').length <= 128 || i18n.t('name_length_rule')
-  ],
-  siret: [
-    (siretValue: string) => /^([0-9]{9}|[0-9]{14})$/.test(siretValue) || i18n.t('siret_error')
-  ],
-  typeOfPractice: [
-    (typeOfPracticeValue: Array<number>) => {
-      if(!$organizationProfile().isManagerProduct())
-      return typeOfPracticeValue.length > 0 || i18n.t('required')
-      return true
-    }
-  ]
-}
-
-</script>
-
-<style scoped>
-  .v-icon.v-icon {
-    font-size: 14px;
-  }
-</style>

+ 580 - 0
pages/organization/index.vue.off

@@ -0,0 +1,580 @@
+<!--
+Contenu de la page pages/organization.vue
+Contient toutes les informations sur l'organization courante
+-->
+<template>
+  <LayoutContainer>
+    <UiForm
+      v-if="!pending"
+      :model="models().Organization"
+      :entity="organization"
+    >
+      <template #form.input="{ model, entity: organization }">
+        <v-expansion-panels :value="panel" focusable accordion>
+          <!-- Description -->
+          <UiExpansionPanel id="description" icon="fa-info">
+            <v-container fluid class="container">
+              <v-row>
+                <v-col cols="12" sm="6">
+                  <UiInputText
+                    v-model="organization.name"
+                    field="name"
+                    :rules="rules.name"
+                  />
+                </v-col>
+
+                <v-col cols="12" sm="6">
+                  <UiInputText v-model="organization.acronym" field="acronym" />
+                </v-col>
+
+                <v-col
+                  v-if="organizationProfile.isInsideNetwork()"
+                  cols="12"
+                  sm="6"
+                >
+                  <UiInputText
+                    v-model="organization.identifier"
+                    :label="
+                      organizationProfile.isCmf()
+                        ? 'identifierCmf'
+                        : 'identifierFfec'
+                    "
+                    field="identifier"
+                  />
+                </v-col>
+
+                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">
+                  <UiInputText
+                    v-model="organization.ffecApproval"
+                    field="ffecApproval"
+                  />
+                </v-col>
+
+                <v-col cols="12" sm="6">
+                  <UiInputText
+                    v-model="organization.description"
+                    field="description"
+                  />
+                </v-col>
+
+                <v-col cols="12" sm="6">
+                  <div>
+                    <span>{{ $t('logo') }}</span>
+                    <UiHelp right>
+                      <p v-html="$t('logo_upload')" />
+                    </UiHelp>
+                  </div>
+                  <UiImage
+                    :image-id="getIdFromUri(organization.logo)"
+                    :width="200"
+                    field="logo"
+                    :owner-id="id"
+                  ></UiImage>
+                </v-col>
+
+                <v-col
+                  v-if="!organizationProfile.isManagerProduct()"
+                  cols="12"
+                  sm="6"
+                >
+                  <!--                  <UiInputEnum field="principalType" v-model="organization.principalType" enum-type="organization_principal_type"/>-->
+                </v-col>
+
+                <!--                <v-col v-if="!organizationProfile.isFfec() && !organizationProfile.isManagerProduct() && !organizationProfile.isArtist()" cols="12" sm="6">-->
+                <!--                  <UiInputEnum field="schoolCategory" v-model="organization.schoolCategory" enum-type="organization_school_cat"/>-->
+                <!--                </v-col>-->
+
+                <!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
+                <!--                  <UiInputEnum field="typeEstablishment" v-model="organization.typeEstablishment" enum-type="organization_type_establishment"/>-->
+                <!--                </v-col>-->
+
+                <!--                <v-col v-if="organization.typeEstablishment === 'MULTIPLE'" cols="12" sm="6">-->
+                <!--                  <UiInputEnum field="typeEstablishmentDetail" v-model="organization.typeEstablishmentDetail" enum-type="organization_type_establishment_detail" />-->
+                <!--                </v-col>-->
+
+                <v-col v-if="organizationProfile.isCmf()" cols="12" sm="6">
+                  <div class="d-flex flex-row">
+                    <!--                    <UiInputAutocomplete-->
+                    <!--                      field="typeOfPractices"-->
+                    <!--                      :items="typeOfPractices"-->
+                    <!--                      :isLoading="typeOfPracticesFetchingState.pending"-->
+                    <!--                      :item-text="['name']"-->
+                    <!--                      :data="getIdsFromUris(organization.typeOfPractices)"-->
+                    <!--                      :translate="true"-->
+                    <!--                      :multiple="true"-->
+                    <!--                      group="category"-->
+                    <!--                      :rules="rules.typeOfPractice"-->
+                    <!--                      @update="updateRepository($event.map((id) => `/api/type_of_practices/${id}`), 'typeOfPractices')"-->
+                    <!--                      class="flex"-->
+                    <!--                    />-->
+                    <UiHelp>
+                      {{ $t('type_of_practices_autocomplete') }}
+                    </UiHelp>
+                  </div>
+                </v-col>
+
+                <!-- TODO: essayer de faire une condition plus explicite pour le v-if -->
+                <!--                <v-col cols="12" sm="6" v-if="getIdsFromUris(organization.typeOfPractices).indexOf(37) >= 0">-->
+                <!--                  <UiInputTextArea field="otherPractice" v-model="organization.otherPractice" />-->
+                <!--                </v-col>-->
+              </v-row>
+            </v-container>
+          </UiExpansionPanel>
+
+          <!-- Adresses -->
+          <!--          <UiExpansionPanel id="address_postal" icon="fa-globe-europe">-->
+          <!--            <v-container fluid class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="12">-->
+          <!--                  <UiCollection-->
+          <!--                    :model="models().OrganizationAddressPostal"-->
+          <!--                    :parent="entity"-->
+          <!--                    loaderType="image"-->
+          <!--                    newLink="/organization/address/new"-->
+          <!--                  >-->
+          <!--                    <template #list.item="{items}">-->
+          <!--                      <v-container fluid>-->
+          <!--                        <v-row dense>-->
+          <!--                          <v-col-->
+          <!--                            v-for="item in items"-->
+          <!--                            :key="item.id"-->
+          <!--                            cols="4"-->
+          <!--                          >-->
+          <!--                            <UiCard-->
+          <!--                              :link="`/organization/address/${item.id}`"-->
+          <!--                              :model="models().OrganizationAddressPostal"-->
+          <!--                              :entity="item"-->
+          <!--                            >-->
+          <!--                              <template #card.title>-->
+          <!--                                {{ $t(item.type) }}-->
+          <!--                              </template>-->
+          <!--                              <template #card.text>-->
+          <!--                                {{ item.addressPostal.streetAddress }} <br>-->
+          <!--                                <span v-if="item.addressPostal.streetAddressSecond">{{ item.addressPostal.streetAddressSecond }} <br></span>-->
+          <!--                                <span v-if="item.addressPostal.streetAddressThird">{{ item.addressPostal.streetAddressThird }} <br></span>-->
+          <!--                                {{ item.addressPostal.postalCode }} {{ item.addressPostal.addressCity }}<br>-->
+          <!--                                <span v-if="item.addressPostal.addressCountry">-->
+          <!--                                  <UiItemFromUri-->
+          <!--                                    :model="models().Country"-->
+          <!--                                    :query="repositories().countryRepository.query()"-->
+          <!--                                    :uri="item.addressPostal.addressCountry"-->
+          <!--                                  >-->
+          <!--                                    <template #item.text="{item}">-->
+          <!--                                      {{item.name}}-->
+          <!--                                    </template>-->
+          <!--                                  </UiItemFromUri>-->
+          <!--                                </span>-->
+          <!--                              </template>-->
+          <!--                            </UiCard>-->
+          <!--                          </v-col>-->
+          <!--                        </v-row>-->
+          <!--                      </v-container>-->
+          <!--                    </template>-->
+          <!--                  </UiCollection>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash;  Point de Contact&ndash;&gt;-->
+          <!--          <UiExpansionPanel id="contact_point" icon="fa-phone">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="12">-->
+          <!--                  <UiCollection-->
+          <!--                    :model="models().ContactPoint"-->
+          <!--                    :parent="entity"-->
+          <!--                    loaderType="image"-->
+          <!--                    newLink="/organization/contact_points/new"-->
+          <!--                  >-->
+          <!--                    <template #list.item="{items}">-->
+          <!--                      <v-container fluid>-->
+          <!--                        <v-row :dense="true">-->
+          <!--                          <v-col-->
+          <!--                            v-for="item in items"-->
+          <!--                            :key="item.id"-->
+          <!--                            cols="4"-->
+          <!--                          >-->
+          <!--                            <UiCard-->
+          <!--                              :link="`/organization/contact_points/${item.id}`"-->
+          <!--                              :model="models().ContactPoint"-->
+          <!--                              :entity="item"-->
+          <!--                            >-->
+          <!--                              <template #card.title>-->
+          <!--                                {{ $t(item.contactType) }}-->
+          <!--                              </template>-->
+          <!--                              <template #card.text>-->
+          <!--                                <span v-if="item.email"><strong>{{ $t('email') }}</strong> : {{ item.email }} <br></span>-->
+          <!--                                <span v-if="item.emailInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('emailInvalid') }}</strong> : {{ item.emailInvalid }} <br></span>-->
+
+          <!--                                <span v-if="item.telphone"><strong>{{ $t('telphone') }}</strong> : {{ formatPhoneNumber(item.telphone) }} <br></span>-->
+          <!--                                <span v-if="item.telphoneInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('telphoneInvalid') }}</strong> : {{ formatPhoneNumber(item.telphoneInvalid) }} <br></span>-->
+
+          <!--                                <span v-if="item.mobilPhone"><strong>{{ $t('mobilPhone') }}</strong> : {{ formatPhoneNumber(item.mobilPhone) }} <br></span>-->
+          <!--                                <span v-if="item.mobilPhoneInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('mobilPhoneInvalid') }}</strong> : {{ formatPhoneNumber(item.mobilPhoneInvalid) }} </span>-->
+          <!--                              </template>-->
+          <!--                            </UiCard>-->
+          <!--                          </v-col>-->
+          <!--                        </v-row>-->
+          <!--                      </v-container>-->
+          <!--                    </template>-->
+          <!--                  </UiCollection>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash; Informations légales &ndash;&gt;-->
+          <!--          <UiExpansionPanel id="legalInformation" icon="fa-gavel">-->
+          <!--            <v-container fluid class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText-->
+          <!--                    field="siretNumber"-->
+          <!--                    :data="entity['siretNumber']"-->
+          <!--                    :error="siretError"-->
+          <!--                    :error-message="siretErrorMessage"-->
+          <!--                    :rules="rules.siretRule"-->
+          <!--                    @update="checkSiretHook($event, 'siretNumber')"-->
+          <!--                  />-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="apeNumber" :data="entity['apeNumber']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col v-if="entity['legalStatus'] === 'ASSOCIATION_LAW_1901'" cols="12" sm="6">-->
+          <!--                  <UiInputText field="waldecNumber" :data="entity['waldecNumber']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputDatePicker field="creationDate" :data="entity['creationDate']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="prefectureName" :data="entity['prefectureName']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="prefectureNumber" :data="entity['prefectureNumber']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputDatePicker field="declarationDate" :data="entity['declarationDate']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="tvaNumber" :data="entity['tvaNumber']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputEnum field="legalStatus" :data="entity['legalStatus']" enum-type="organization_legal"/>-->
+          <!--                </v-col>-->
+
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash;  Agréments &ndash;&gt;-->
+          <!--          <UiExpansionPanel id="agrements" icon="fa-certificate">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="youngApproval" :data="entity['youngApproval']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="trainingApproval" :data="entity['trainingApproval']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="otherApproval" :data="entity['otherApproval']"/>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash; Salariés &ndash;&gt;-->
+          <!--          <UiExpansionPanel id="salary" icon="fa-users">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="collectiveAgreement" :data="entity['collectiveAgreement']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputEnum field="opca" :data="entity['opca']" enum-type="organization_opca"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="icomNumber" :data="entity['icomNumber']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="urssafNumber" :data="entity['urssafNumber']"/>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash; Réseaux &ndash;&gt;-->
+          <!--          <UiExpansionPanel v-if="organizationProfile.isInsideNetwork()" id="network" icon="fa-share-alt">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="12">-->
+          <!--                  <UiCollection-->
+          <!--                    :model="models().NetworkOrganization"-->
+          <!--                    :parent="entity"-->
+          <!--                    loaderType="text"-->
+          <!--                  >-->
+          <!--                    <template #list.item="{items}">-->
+          <!--                      <div v-for="item in items" :key="item.id">-->
+          <!--                        <span>{{ item.network.name }}</span> - <span>{{$t('first_subscription')}} : <UiTemplateDate :data="item.startDate" /></span>-->
+          <!--                      </div>-->
+          <!--                    </template>-->
+          <!--                  </UiCollection>-->
+          <!--                </v-col>-->
+          <!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
+          <!--                  <UiInputText field="budget" :data="entity['budget']" type="number" />-->
+          <!--                </v-col>-->
+
+          <!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
+          <!--                  <UiInputCheckbox field="isPedagogicIsPrincipalActivity" :data="entity['isPedagogicIsPrincipalActivity']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col v-if="organizationProfile.isFfec()" cols="12" sm="6">-->
+          <!--                  <UiInputText field="pedagogicBudget" :data="entity['pedagogicBudget']" type="number"/>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash; Communication &ndash;&gt;-->
+          <!--          <UiExpansionPanel id="communication" icon="fa-rss">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="twitter" :data="entity['twitter']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="youtube" :data="entity['youtube']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="facebook" :data="entity['facebook']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputText field="instagram" :data="entity['instagram']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <UiInputCheckbox field="portailVisibility" :data="entity['portailVisibility']"/>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="6">-->
+          <!--                  <div class="d-flex flex-column">-->
+          <!--                    <UiHelp class="d-flex flex-row">-->
+          <!--                      <span>{{ $t('image') }}</span>-->
+          <!--                      <p v-html="$t('communication_image_upload')"/>-->
+          <!--                    </UiHelp>-->
+          <!--                    <UiImage-->
+          <!--                      :id="getIdFromUri(entity['image'])"-->
+          <!--                      :upload="true"-->
+          <!--                      :width="200"-->
+          <!--                      field="image"-->
+          <!--                      :ownerId="id"-->
+
+          <!--                    ></UiImage>-->
+          <!--                  </div>-->
+          <!--                </v-col>-->
+
+          <!--                <v-col cols="12" sm="12">-->
+          <!--                  <UiCollection-->
+          <!--                    :model="models().OrganizationArticle"-->
+          <!--                    :parent="entity"-->
+          <!--                    loaderType="text"-->
+          <!--                  >-->
+          <!--                    <template #list.item="{items}">-->
+          <!--                      <h4 class="neutral-strong&#45;&#45;text font-weight-regular">{{$t('organizationArticle')}}</h4>-->
+          <!--                      <UiTemplateDataTable-->
+          <!--                        :headers="[-->
+          <!--                          { text: $t('title'), value: 'title' },-->
+          <!--                          { text: $t('link'), value: 'link' },-->
+          <!--                          { text: $t('date'), value: 'date' },-->
+          <!--                        ]"-->
+          <!--                        :items="items"-->
+          <!--                      >-->
+          <!--                        <template #item.date="{item}">-->
+          <!--                          <UiTemplateDate :data="item.date" />-->
+          <!--                        </template>-->
+          <!--                      </UiTemplateDataTable>-->
+          <!--                    </template>-->
+          <!--                  </UiCollection>-->
+          <!--                </v-col>-->
+
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+
+          <!--          &lt;!&ndash; IBAN &ndash;&gt;-->
+          <!--          <UiExpansionPanel id="bank_account" icon="fa-euro-sign">-->
+          <!--            <v-container class="container">-->
+          <!--              <v-row>-->
+          <!--                <v-col cols="12" sm="12">-->
+          <!--                  <UiCollection-->
+          <!--                    :model="models().BankAccount"-->
+          <!--                    :parent="entity"-->
+          <!--                    loaderType="image"-->
+          <!--                    newLink="/organization/bank_account/new"-->
+          <!--                  >-->
+          <!--                    <template #list.item="{items}">-->
+          <!--                      <v-container fluid>-->
+          <!--                        <v-row :dense="true">-->
+          <!--                          <v-col-->
+          <!--                            v-for="item in items"-->
+          <!--                            :key="item.id"-->
+          <!--                            cols="4"-->
+          <!--                          >-->
+          <!--                            <UiCard-->
+          <!--                              :id="item.id"-->
+          <!--                              :link="`/organization/bank_account/${item.id}`"-->
+          <!--                              :model="models().BankAccount"-->
+          <!--                            >-->
+          <!--                              <template #card.text>-->
+          <!--                                <span v-if="item.bankName"><strong>{{ $t('bankName') }}</strong> : {{ item.bankName }} <br></span>-->
+
+          <!--                                <span v-if="item.bic"><strong>{{ $t('bic') }}</strong> : {{ item.bic }} <br></span>-->
+          <!--                                <span v-if="item.bicInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('bicInvalid') }}</strong> : {{ item.bicInvalid }} <br></span>-->
+
+          <!--                                <span v-if="item.iban"><strong>{{ $t('iban') }}</strong> : {{ item.iban }} <br></span>-->
+          <!--                                <span v-if="item.ibanInvalid" class="danger&#45;&#45;text"><v-icon class="danger&#45;&#45;text">mdi-alert</v-icon> <strong>{{ $t('ibanInvalid') }}</strong> : {{ item.ibanInvalid }} <br></span>-->
+
+          <!--                              </template>-->
+          <!--                            </UiCard>-->
+          <!--                          </v-col>-->
+          <!--                        </v-row>-->
+          <!--                      </v-container>-->
+          <!--                    </template>-->
+          <!--                  </UiCollection>-->
+          <!--                </v-col>-->
+          <!--              </v-row>-->
+          <!--            </v-container>-->
+          <!--          </UiExpansionPanel>-->
+        </v-expansion-panels>
+      </template>
+    </UiForm>
+  </LayoutContainer>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, ref } from 'vue'
+import type { ComputedRef } from 'vue'
+import { useRoute } from '#app'
+import { useI18n } from 'vue-i18n'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import TypeOfPractice from '~/models/Organization/TypeOfPractice'
+import { useExtensionPanel } from '~/composables/layout/useExtensionPanel'
+import { useValidation } from '~/composables/form/useValidation'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import UrlUtils from '~/services/utils/urlUtils'
+import Organization from '~/models/Organization/Organization'
+import { useI18nUtils } from '~/composables/utils/useI18nUtils'
+import ContactPoint from '~/models/Core/ContactPoint'
+import BankAccount from '~/models/Core/BankAccount'
+import OrganizationAddressPostal from '~/models/Organization/OrganizationAddressPostal'
+import Country from '~/models/Core/Country'
+import NetworkOrganization from '~/models/Network/NetworkOrganization'
+import OrganizationArticle from '~/models/Organization/OrganizationArticle'
+
+const id: number | null = useOrganizationProfileStore().id
+if (id === null) {
+  throw new Error('Missing organization id')
+}
+
+const organizationProfile = reactive($organizationProfile())
+
+const { em } = useEntityManager()
+const { fetch, fetchCollection } = useEntityFetch()
+const { pending } = fetch(Organization, id)
+
+const organization: ComputedRef<Organization> = computed(() => {
+  return em.find(Organization, id)
+})
+
+const { data: typeOfPractices, pending: typeOfPracticesPending } =
+  fetchCollection(TypeOfPractice)
+
+const route = ref(useRoute())
+const { panel } = useExtensionPanel(route)
+
+const { siretError, siretErrorMessage, validateSiret } =
+  useValidation().useValidateSiret()
+
+const validateSiretHook = async (
+  siret: string,
+  field: string,
+  updateRepository: any,
+) => {
+  await validateSiret(siret)
+  if (!siretError.value) {
+    em.save(Organization, organization.value)
+  }
+}
+
+const formatPhoneNumber = (number: string): string => {
+  return useI18nUtils().formatPhoneNumber(number)
+}
+
+// TODO: voir si l'extraction de cette id ne pourrait pas être faite en amont, au niveau des post-processors
+const getIdsFromUris = (uris: Array<string>) => {
+  const ids: Array<any> = []
+  for (const uri of uris) {
+    ids.push(UrlUtils.extractIdFromUri(uri))
+  }
+  return ids
+}
+
+// TODO: voir si l'extraction de cette id ne pourrait pas être faite en amont, au niveau des post-processors
+const getIdFromUri = (uri: string) => UrlUtils.extractIdFromUri(uri)
+
+const models = () => {
+  return {
+    Organization,
+    ContactPoint,
+    BankAccount,
+    OrganizationAddressPostal,
+    Country,
+    NetworkOrganization,
+    OrganizationArticle,
+  }
+}
+
+const i18n = useI18n()
+
+const rules = {
+  name: [
+    (nameValue: string) => !!nameValue || i18n.t('required'),
+    (nameValue: string) =>
+      (nameValue || '').length <= 128 || i18n.t('name_length_rule'),
+  ],
+  siret: [
+    (siretValue: string) =>
+      /^([0-9]{9}|[0-9]{14})$/.test(siretValue) || i18n.t('siret_error'),
+  ],
+  typeOfPractice: [
+    (typeOfPracticeValue: Array<number>) => {
+      if (!$organizationProfile().isManagerProduct())
+        return typeOfPracticeValue.length > 0 || i18n.t('required')
+      return true
+    },
+  ],
+}
+</script>
+
+<style scoped>
+.v-icon.v-icon {
+  font-size: 14px;
+}
+</style>

+ 8 - 9
pages/parameters.vue

@@ -8,14 +8,13 @@
 </template>
 
 <script setup lang="ts">
-  /**
-   * Disable the default layout, the page will use the layout defined with <NuxtLayout />
-   * @see https://nuxt.com/docs/guide/directory-structure/layouts#overriding-a-layout-on-a-per-page-basis
-   */
-  definePageMeta({
-    layout: false,
-  });
+/**
+ * Disable the default layout, the page will use the layout defined with <NuxtLayout />
+ * @see https://nuxt.com/docs/guide/directory-structure/layouts#overriding-a-layout-on-a-per-page-basis
+ */
+definePageMeta({
+  layout: false,
+})
 </script>
 
-<style scoped lang="scss">
-</style>
+<style scoped lang="scss"></style>

+ 3 - 3
pages/parameters/attendance_booking_reasons/[id].vue

@@ -6,10 +6,10 @@
         :model="AttendanceBookingReason"
         go-back-route="/parameters/attendances"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <UiInputText
-            field="reason"
             v-model="entity.reason"
+            field="reason"
             :rules="rules()"
           />
         </template>
@@ -19,7 +19,7 @@
 </template>
 <script setup lang="ts">
 import { useI18n } from 'vue-i18n'
-import AttendanceBookingReason from "~/models/Booking/AttendanceBookingReason";
+import AttendanceBookingReason from '~/models/Booking/AttendanceBookingReason'
 
 const i18n = useI18n()
 

+ 4 - 4
pages/parameters/attendance_booking_reasons/new.vue

@@ -1,12 +1,12 @@
 <template>
   <LayoutContainer>
     <div>
-      <h2>{{ $t("new_attendance_booking_reason")}}</h2>
+      <h2>{{ $t('new_attendance_booking_reason') }}</h2>
       <UiFormCreation
         :model="AttendanceBookingReason"
         go-back-route="/parameters/attendances"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <v-container :fluid="true" class="container">
             <v-row>
               <v-col cols="12" sm="6"> </v-col>
@@ -29,12 +29,12 @@
 
 <script setup lang="ts">
 import { useI18n } from 'vue-i18n'
-import AttendanceBookingReason from "~/models/Booking/AttendanceBookingReason";
+import AttendanceBookingReason from '~/models/Booking/AttendanceBookingReason'
 
 const i18n = useI18n()
 
 const rules = () => [
   (reason: string | null) =>
-      (reason !== null && reason.length > 0) || i18n.t('please_enter_a_value'),
+    (reason !== null && reason.length > 0) || i18n.t('please_enter_a_value'),
 ]
 </script>

+ 47 - 40
pages/parameters/attendances.vue

@@ -2,10 +2,10 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else-if="parameters !== null"
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
@@ -28,48 +28,52 @@
       </v-row>
     </UiForm>
 
-    <v-divider class="my-10"/>
+    <v-divider class="my-10" />
 
     <UiLoadingPanel v-if="attendanceBookingReasonsPending" />
     <div v-else>
       <v-table>
         <thead>
-        <tr>
-          <td>{{ $t('attendanceBookingReasons') }}</td>
-          <td></td>
-        </tr>
+          <tr>
+            <td>{{ $t('attendanceBookingReasons') }}</td>
+            <td></td>
+          </tr>
         </thead>
-        <tbody>
-        <tr v-if="attendanceBookingReasons.length > 0" v-for="reason in attendanceBookingReasons" :key="reason.id">
-          <td class="cycle-editable-cell">
-            {{ reason.reason }}
-          </td>
-          <td class="d-flex flex-row">
-            <v-btn
+        <tbody v-if="attendanceBookingReasons.length > 0">
+          <tr v-for="reason in attendanceBookingReasons" :key="reason.id">
+            <td class="cycle-editable-cell">
+              {{ reason.reason }}
+            </td>
+            <td class="d-flex flex-row">
+              <v-btn
                 :flat="true"
                 icon="fa fa-pen"
                 class="cycle-edit-icon mr-3"
                 @click="goToEditPage(reason.id as number)"
-            />
-            <UiButtonDelete
+              />
+              <UiButtonDelete
                 :model="AttendanceBookingReason"
                 :entity="reason"
                 :flat="true"
                 class="cycle-edit-icon"
-            />
-          </td>
-        </tr>
-        <tr v-else class="theme-neutral">
-          <td><i>{{ $t('nothing_to_show')}}</i></td>
-          <td></td>
-        </tr>
+              />
+            </td>
+          </tr>
+        </tbody>
+        <tbody v-else>
+          <tr class="theme-neutral">
+            <td>
+              <i>{{ $t('nothing_to_show') }}</i>
+            </td>
+            <td></td>
+          </tr>
         </tbody>
       </v-table>
       <v-btn
-          :flat="true"
-          prepend-icon="fa fa-plus"
-          class="theme-primary mt-4"
-          @click="goToCreatePage"
+        :flat="true"
+        prepend-icon="fa fa-plus"
+        class="theme-primary mt-4"
+        @click="goToCreatePage"
       >
         {{ $t('add') }}
       </v-btn>
@@ -77,16 +81,15 @@
   </LayoutContainer>
 </template>
 <script setup lang="ts">
+import type { AsyncData } from '#app'
+import { type Collection, useRepo } from 'pinia-orm'
+import type { ComputedRef } from 'vue'
 import Parameters from '~/models/Organization/Parameters'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import { useOrganizationProfileStore } from '~/stores/organizationProfile'
-import type { AsyncData } from '#app'
-import {type Collection, useRepo} from "pinia-orm";
-import type {ComputedRef} from "vue";
-import UrlUtils from "~/services/utils/urlUtils";
-import AttendanceBookingReason from "~/models/Booking/AttendanceBookingReason";
-import AttendanceBookingReasonRepository from "~/stores/repositories/AttendanceBookingReasonRepository";
-
+import UrlUtils from '~/services/utils/urlUtils'
+import AttendanceBookingReason from '~/models/Booking/AttendanceBookingReason'
+import AttendanceBookingReasonRepository from '~/stores/repositories/AttendanceBookingReasonRepository'
 
 const { fetch } = useEntityFetch()
 
@@ -98,12 +101,14 @@ if (organizationProfile.parametersId === null) {
 
 const { data: parameters, pending } = fetch(
   Parameters,
-  organizationProfile.parametersId
-) as AsyncData<Parameters, Parameters | true>
+  organizationProfile.parametersId,
+) as AsyncData<Parameters | null, Error | null>
 
 const { fetchCollection } = useEntityFetch()
 
-const { pending: attendanceBookingReasonsPending } = fetchCollection(AttendanceBookingReason)
+const { pending: attendanceBookingReasonsPending } = fetchCollection(
+  AttendanceBookingReason,
+)
 
 const attendanceBookingReasonsRepo = useRepo(AttendanceBookingReasonRepository)
 
@@ -111,7 +116,9 @@ const attendanceBookingReasonsRepo = useRepo(AttendanceBookingReasonRepository)
  * On récupère les timings via le store
  * (sans ça, les mises à jour SSE ne seront pas prises en compte)
  */
-const attendanceBookingReasons: ComputedRef<Collection<AttendanceBookingReason>> = computed(() => {
+const attendanceBookingReasons: ComputedRef<
+  Collection<AttendanceBookingReason>
+> = computed(() => {
   return attendanceBookingReasonsRepo.getReasons()
 })
 

+ 39 - 40
pages/parameters/bulletin.vue

@@ -1,72 +1,71 @@
-
 <template>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <UiInputCheckbox
-              v-model="parameters.bulletinWithTeacher"
-              field="bulletinWithTeacher"
+            v-model="parameters.bulletinWithTeacher"
+            field="bulletinWithTeacher"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinSignatureDirector"
-              field="bulletinSignatureDirector"
+            v-model="parameters.bulletinSignatureDirector"
+            field="bulletinSignatureDirector"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinShowEducationWithoutEvaluation"
-              field="bulletinShowEducationWithoutEvaluation"
+            v-model="parameters.bulletinShowEducationWithoutEvaluation"
+            field="bulletinShowEducationWithoutEvaluation"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinShowAbsences"
-              field="bulletinShowAbsences"
+            v-model="parameters.bulletinShowAbsences"
+            field="bulletinShowAbsences"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinEditWithoutEvaluation"
-              field="bulletinEditWithoutEvaluation"
+            v-model="parameters.bulletinEditWithoutEvaluation"
+            field="bulletinEditWithoutEvaluation"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinPrintAddress"
-              field="bulletinPrintAddress"
+            v-model="parameters.bulletinPrintAddress"
+            field="bulletinPrintAddress"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinDisplayLevelAcquired"
-              field="bulletinDisplayLevelAcquired"
+            v-model="parameters.bulletinDisplayLevelAcquired"
+            field="bulletinDisplayLevelAcquired"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinViewTestResults"
-              field="bulletinViewTestResults"
+            v-model="parameters.bulletinViewTestResults"
+            field="bulletinViewTestResults"
           />
 
           <UiInputCheckbox
-              v-model="parameters.bulletinShowAverages"
-              field="bulletinShowAverages"
+            v-model="parameters.bulletinShowAverages"
+            field="bulletinShowAverages"
           />
 
           <UiInputAutocompleteWithEnum
-              v-model="parameters.bulletinReceiver"
-              field="bulletinReceiver"
-              enum-name="organization_bulletin_send_to"
-              variant="underlined"
+            v-model="parameters.bulletinReceiver"
+            field="bulletinReceiver"
+            enum-name="organization_bulletin_send_to"
+            variant="underlined"
           />
 
           <UiInputAutocompleteWithEnum
-              v-model="parameters.bulletinCriteriaSort"
-              field="bulletinCriteriaSort"
-              enum-name="organization_bulletin_criteria_sort"
-              variant="underlined"
+            v-model="parameters.bulletinCriteriaSort"
+            field="bulletinCriteriaSort"
+            enum-name="organization_bulletin_criteria_sort"
+            variant="underlined"
           />
         </v-col>
       </v-row>
@@ -75,10 +74,10 @@
 </template>
 
 <script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
 const { fetch } = useEntityFetch()
 
@@ -88,10 +87,10 @@ if (organizationProfile.parametersId === null) {
   throw new Error('Missing organization parameters id')
 }
 
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
-
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<Parameters, Parameters | true>
 </script>
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 6 - 12
pages/parameters/cycles/[id].vue

@@ -2,16 +2,9 @@
   <LayoutContainer>
     <div>
       <h2>{{ $t('cycle') }}</h2>
-      <UiFormEdition
-          :model="Cycle"
-          go-back-route="/parameters/teaching"
-      >
-        <template v-slot="{ entity }">
-          <UiInputText
-              field="label"
-              v-model="entity.label"
-              :rules="rules()"
-          />
+      <UiFormEdition :model="Cycle" go-back-route="/parameters/teaching">
+        <template #default="{ entity }">
+          <UiInputText v-model="entity.label" field="label" :rules="rules()" />
         </template>
       </UiFormEdition>
     </div>
@@ -19,11 +12,12 @@
 </template>
 <script setup lang="ts">
 import { useI18n } from 'vue-i18n'
-import Cycle from "~/models/Education/Cycle";
+import Cycle from '~/models/Education/Cycle'
 
 const i18n = useI18n()
 
 const rules = () => [
-  (label: string | null) => (label !== null && label.length > 0) || i18n.t('please_enter_a_value'),
+  (label: string | null) =>
+    (label !== null && label.length > 0) || i18n.t('please_enter_a_value'),
 ]
 </script>

+ 31 - 32
pages/parameters/education_notation.vue

@@ -2,44 +2,44 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <UiInputCheckbox
-              v-model="parameters.periodValidation"
-              field="periodValidation"
-              label="define_validation_periods_for_teachers"
+            v-model="parameters.periodValidation"
+            field="periodValidation"
+            label="define_validation_periods_for_teachers"
           />
 
           <UiInputCheckbox
-              v-model="parameters.editCriteriaNotationByAdminOnly"
-              field="editCriteriaNotationByAdminOnly"
-              label="evaluation_criterium_edition_is_admin_only"
+            v-model="parameters.editCriteriaNotationByAdminOnly"
+            field="editCriteriaNotationByAdminOnly"
+            label="evaluation_criterium_edition_is_admin_only"
           />
 
           <UiInputAutocompleteWithEnum
-              v-if="organizationProfile.hasModule('AdvancedEducationNotation')"
-              v-model="parameters.advancedEducationNotationType"
-              enum-name="advanced_education_notation"
-              field="advancedEducationNotationType"
-              variant="underlined"
+            v-if="organizationProfile.hasModule('AdvancedEducationNotation')"
+            v-model="parameters.advancedEducationNotationType"
+            enum-name="advanced_education_notation"
+            field="advancedEducationNotationType"
+            variant="underlined"
           />
 
           <UiInputCheckbox
-              v-model="parameters.requiredValidation"
-              field="requiredValidation"
-              label="mandatory_validation_for_evaluations"
+            v-model="parameters.requiredValidation"
+            field="requiredValidation"
+            label="mandatory_validation_for_evaluations"
           />
 
           <UiInputAutocompleteWithEnum
-              v-model="parameters.educationPeriodicity"
-              enum-name="education_periodicity"
-              field="educationPeriodicity"
-              variant="underlined"
+            v-model="parameters.educationPeriodicity"
+            enum-name="education_periodicity"
+            field="educationPeriodicity"
+            variant="underlined"
           />
 
           <UiInputNumber
@@ -59,12 +59,11 @@
 </template>
 
 <script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
-const i18n = useI18n()
 const { fetch } = useEntityFetch()
 
 const organizationProfile = useOrganizationProfileStore()
@@ -73,10 +72,10 @@ if (organizationProfile.parametersId === null) {
   throw new Error('Missing organization parameters id')
 }
 
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
-
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<Parameters | null, Error | null>
 </script>
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 3 - 4
pages/parameters/education_timings/[id].vue

@@ -6,10 +6,10 @@
         :model="EducationTiming"
         go-back-route="/parameters/education_timings"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <UiInputNumber
-            field="educationTiming"
             v-model="entity.timing"
+            field="educationTiming"
             :rules="rules()"
           />
         </template>
@@ -18,9 +18,8 @@
   </LayoutContainer>
 </template>
 <script setup lang="ts">
-import EducationTiming from '~/models/Education/EducationTiming'
-import type {RouteLocationPathRaw} from 'vue-router'
 import { useI18n } from 'vue-i18n'
+import EducationTiming from '~/models/Education/EducationTiming'
 
 const i18n = useI18n()
 

+ 24 - 21
pages/parameters/education_timings/index.vue

@@ -9,53 +9,56 @@
             <td></td>
           </tr>
         </thead>
-        <tbody>
-          <tr v-if="educationTimings.length > 0" v-for="timing in educationTimings" :key="timing.id">
+        <tbody v-if="educationTimings.length > 0">
+          <tr v-for="timing in educationTimings" :key="timing.id">
             <td class="cycle-editable-cell">
               {{ timing.timing }}
             </td>
             <td class="d-flex flex-row">
               <v-btn
-                  :flat="true"
-                  icon="fa fa-pen"
-                  class="cycle-edit-icon mr-3"
-                  @click="goToEditPage(timing.id as number)"
+                :flat="true"
+                icon="fa fa-pen"
+                class="cycle-edit-icon mr-3"
+                @click="goToEditPage(timing.id as number)"
               />
               <UiButtonDelete
-                  :model="EducationTiming"
-                  :entity="timing"
-                  :flat="true"
-                  class="cycle-edit-icon"
+                :model="EducationTiming"
+                :entity="timing"
+                :flat="true"
+                class="cycle-edit-icon"
               />
             </td>
           </tr>
-          <tr v-else class="theme-neutral">
-            <td><i>{{ $t('nothing_to_show')}}</i></td>
+        </tbody>
+        <tbody v-else>
+          <tr class="theme-neutral">
+            <td>
+              <i>{{ $t('nothing_to_show') }}</i>
+            </td>
             <td></td>
           </tr>
         </tbody>
       </v-table>
       <v-btn
-          :flat="true"
-          prepend-icon="fa fa-plus"
-          class="theme-primary mt-4"
-          @click="goToCreatePage"
+        :flat="true"
+        prepend-icon="fa fa-plus"
+        class="theme-primary mt-4"
+        @click="goToCreatePage"
       >
         {{ $t('add') }}
       </v-btn>
     </div>
   </LayoutContainer>
-
 </template>
 
 <script setup lang="ts">
+import { useRepo } from 'pinia-orm'
+import type { ComputedRef } from 'vue'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import EducationTiming from '~/models/Education/EducationTiming'
-import { useRepo } from 'pinia-orm'
 import EducationTimingsRepository from '~/stores/repositories/EducationTimingsRepository'
-import type {ComputedRef} from "vue";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import UrlUtils from "~/services/utils/urlUtils";
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import UrlUtils from '~/services/utils/urlUtils'
 
 const organizationProfile = useOrganizationProfileStore()
 

+ 4 - 4
pages/parameters/education_timings/new.vue

@@ -1,12 +1,12 @@
 <template>
   <LayoutContainer>
     <div>
-      <h2>{{ $t("new_education_timing")}}</h2>
+      <h2>{{ $t('new_education_timing') }}</h2>
       <UiFormCreation
         :model="EducationTiming"
         go-back-route="/parameters/education_timings"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <v-container :fluid="true" class="container">
             <v-row>
               <v-col cols="12" sm="6"> </v-col>
@@ -28,13 +28,13 @@
 </template>
 
 <script setup lang="ts">
-import EducationTiming from '~/models/Education/EducationTiming'
 import { useI18n } from 'vue-i18n'
+import EducationTiming from '~/models/Education/EducationTiming'
 
 const i18n = useI18n()
 
 const rules = () => [
-  (timing: number | null ) =>
+  (timing: number | null) =>
     (timing !== null && timing > 0) || i18n.t('please_enter_a_value'),
 ]
 </script>

+ 59 - 54
pages/parameters/general_parameters.vue

@@ -2,82 +2,85 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.financialDate"
-              field="financialDate"
-              label="start_date_of_financial_season"
-              position="left"
-              class="my-2"
+            v-if="organizationProfile.isSchool"
+            v-model="parameters.financialDate"
+            field="financialDate"
+            label="start_date_of_financial_season"
+            position="left"
+            class="my-2"
           />
 
           <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.startCourseDate"
-              field="startCourseDate"
-              label="start_date_of_courses"
-              position="left"
-              class="my-2"
+            v-if="organizationProfile.isSchool"
+            v-model="parameters.startCourseDate"
+            field="startCourseDate"
+            label="start_date_of_courses"
+            position="left"
+            class="my-2"
           />
 
           <UiInputCheckbox
-              v-model="parameters.showAdherentList"
-              field="showAdherentList"
-              label="show_adherents_list_and_their_coordinates"
+            v-model="parameters.showAdherentList"
+            field="showAdherentList"
+            label="show_adherents_list_and_their_coordinates"
           />
 
           <UiInputAutocompleteWithEnum
-              v-model="parameters.timezone"
-              enum-name="timezone"
-              field="timezone"
-              variant="underlined"
+            v-model="parameters.timezone"
+            enum-name="timezone"
+            field="timezone"
+            variant="underlined"
           />
 
           <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.musicalDate"
-              field="musicalDate"
-              label="start_date_of_activity_season"
-              position="left"
-              class="my-2"
+            v-if="organizationProfile.isSchool"
+            v-model="parameters.musicalDate"
+            field="musicalDate"
+            label="start_date_of_activity_season"
+            position="left"
+            class="my-2"
           />
 
           <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.endCourseDate"
-              field="endCourseDate"
-              label="end_date_of_courses"
-              position="left"
-              class="my-2"
+            v-if="organizationProfile.isSchool"
+            v-model="parameters.endCourseDate"
+            field="endCourseDate"
+            label="end_date_of_courses"
+            position="left"
+            class="my-2"
           />
 
           <UiInputCheckbox
-              v-if="organizationProfile.isSchool && organizationProfile.isAssociation"
-              v-model="parameters.studentsAreAdherents"
-              field="studentsAreAdherents"
-              label="students_are_also_association_members"
+            v-if="
+              organizationProfile.isSchool && organizationProfile.isAssociation
+            "
+            v-model="parameters.studentsAreAdherents"
+            field="studentsAreAdherents"
+            label="students_are_also_association_members"
           />
 
           <div
-              v-if="organizationProfile.isCMFCentralService"
-              class="d-flex flex-column"
+            v-if="organizationProfile.isCMFCentralService"
+            class="d-flex flex-column"
           >
-            <span class="mb-1 v-label" style="font-size: 12px;">{{ $t('qrCode')}} </span>
+            <span class="mb-1 v-label" style="font-size: 12px"
+              >{{ $t('qrCode') }}
+            </span>
             <UiInputImage
-                v-model="parameters.qrCode"
-                field="qrCode"
-                label="licenceQrCode"
-                :width="120"
+              v-model="parameters.qrCode"
+              field="qrCode"
+              label="licenceQrCode"
+              :width="120"
             />
           </div>
-
         </v-col>
       </v-row>
     </UiForm>
@@ -85,10 +88,10 @@
 </template>
 
 <script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
 const { fetch } = useEntityFetch()
 
@@ -98,8 +101,10 @@ if (organizationProfile.parametersId === null) {
   throw new Error('Missing organization parameters id')
 }
 
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<Parameters, Parameters | true>
 </script>
 
-<style scoped lang="scss">
-</style>
+<style scoped lang="scss"></style>

+ 2 - 3
pages/parameters/index.vue

@@ -1,4 +1,5 @@
 <template>
+  <span>{{ $t('redirecting') }}...</span>
 </template>
 
 <script setup lang="ts">
@@ -8,7 +9,5 @@
  * Voir aussi : pages/parameters.vue
  * */
 const router = useRouter()
-router.push(
-    { path: `/parameters/general_parameters` }
-)
+router.push({ path: `/parameters/general_parameters` })
 </script>

+ 36 - 35
pages/parameters/intranet.vue

@@ -2,47 +2,47 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <UiInputCheckbox
-              v-model="parameters.generateAttendanceReport"
-              field="generateAttendanceReport"
-              label="allow_teachers_to_generate_attendance_reports"
+            v-model="parameters.generateAttendanceReport"
+            field="generateAttendanceReport"
+            label="allow_teachers_to_generate_attendance_reports"
           />
 
           <UiInputCheckbox
-              v-model="parameters.administrationCc"
-              field="administrationCc"
-              label="send_teachers_mail_reports_copy_to_administration"
+            v-model="parameters.administrationCc"
+            field="administrationCc"
+            label="send_teachers_mail_reports_copy_to_administration"
           />
 
           <UiInputCheckbox
-              v-model="parameters.allowMembersToChangeGivenNameAndName"
-              field="allowMembersToChangeGivenNameAndName"
-              label="allow_members_to_change_their_names_and_firstnames"
+            v-model="parameters.allowMembersToChangeGivenNameAndName"
+            field="allowMembersToChangeGivenNameAndName"
+            label="allow_members_to_change_their_names_and_firstnames"
           />
 
           <UiInputCheckbox
-              v-model="parameters.createCourse"
-              field="createCourse"
-              label="allow_teachers_to_create_courses"
+            v-model="parameters.createCourse"
+            field="createCourse"
+            label="allow_teachers_to_create_courses"
           />
 
           <UiInputCheckbox
-              v-model="parameters.consultTeacherListing"
-              field="consultTeacherListing"
-              label="allow_teachers_to_consult_colleagues_informations"
+            v-model="parameters.consultTeacherListing"
+            field="consultTeacherListing"
+            label="allow_teachers_to_consult_colleagues_informations"
           />
 
           <UiInputCheckbox
-              v-model="parameters.showAdherentList"
-              field="showAdherentList"
-              label="allow_students_to_consult_their_pedagogical_followup"
+            v-model="parameters.showAdherentList"
+            field="showAdherentList"
+            label="allow_students_to_consult_their_pedagogical_followup"
           />
         </v-col>
       </v-row>
@@ -51,22 +51,23 @@
 </template>
 
 <script setup lang="ts">
-  import Parameters from "~/models/Organization/Parameters";
-  import {useEntityFetch} from "~/composables/data/useEntityFetch";
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import type {AsyncData} from "#app";
+import type { AsyncData } from '#app'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
 
-  const { fetch } = useEntityFetch()
+const { fetch } = useEntityFetch()
 
-  const organizationProfile = useOrganizationProfileStore()
+const organizationProfile = useOrganizationProfileStore()
 
-  if (organizationProfile.parametersId === null) {
-    throw new Error('Missing organization parameters id')
-  }
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
 
-  const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<Parameters, Parameters | true>
 </script>
 
-<style scoped lang="scss">
-
-</style>
+<style scoped lang="scss"></style>

+ 4 - 4
pages/parameters/residence_areas/[id].vue

@@ -6,10 +6,11 @@
         :model="ResidenceArea"
         go-back-route="/parameters/residence_areas"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <UiInputText
-            field="label"
+            v-if="entity !== null"
             v-model="entity.label"
+            field="label"
             :rules="rules()"
           />
         </template>
@@ -19,9 +20,8 @@
 </template>
 
 <script setup lang="ts">
-import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import ResidenceArea from '~/models/Billing/ResidenceArea'
 import { useI18n } from 'vue-i18n'
+import ResidenceArea from '~/models/Billing/ResidenceArea'
 
 const i18n = useI18n()
 

+ 28 - 28
pages/parameters/residence_areas/index.vue

@@ -9,42 +9,42 @@
             <td></td>
           </tr>
         </thead>
-        <tbody>
-        <tr
-            v-if="residenceAreas.length > 0"
-            v-for="residenceArea in residenceAreas"
-            :key="residenceArea.id"
-        >
-          <td class="cycle-editable-cell">
-            {{ residenceArea.label }}
-          </td>
-          <td class="d-flex flex-row">
-            <v-btn
+        <tbody v-if="residenceAreas.length > 0">
+          <tr v-for="residenceArea in residenceAreas" :key="residenceArea.id">
+            <td class="cycle-editable-cell">
+              {{ residenceArea.label }}
+            </td>
+            <td class="d-flex flex-row">
+              <v-btn
                 :flat="true"
                 icon="fa fa-pen"
                 class="cycle-edit-icon mr-3"
                 @click="goToEditPage(residenceArea.id as number)"
-            />
-            <UiButtonDelete
+              />
+              <UiButtonDelete
                 :model="ResidenceArea"
                 :entity="residenceArea"
                 :flat="true"
                 class="cycle-edit-icon"
-            />
-          </td>
-        </tr>
-        <tr v-else class="theme-neutral">
-          <td><i>{{ $t('nothing_to_show')}}</i></td>
-          <td></td>
-        </tr>
+              />
+            </td>
+          </tr>
+        </tbody>
+        <tbody v-else>
+          <tr class="theme-neutral">
+            <td>
+              <i>{{ $t('nothing_to_show') }}</i>
+            </td>
+            <td></td>
+          </tr>
         </tbody>
       </v-table>
 
       <v-btn
-          :flat="true"
-          prepend-icon="fa fa-plus"
-          class="theme-primary mt-4"
-          @click="goToCreatePage"
+        :flat="true"
+        prepend-icon="fa fa-plus"
+        class="theme-primary mt-4"
+        @click="goToCreatePage"
       >
         {{ $t('add') }}
       </v-btn>
@@ -53,12 +53,12 @@
 </template>
 
 <script setup lang="ts">
+import { useRepo } from 'pinia-orm'
+import type { ComputedRef } from 'vue'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import ResidenceArea from '~/models/Billing/ResidenceArea'
-import { useRepo } from 'pinia-orm'
 import ResidenceAreasRepository from '~/stores/repositories/ResidenceAreasRepository'
-import { useRouter } from 'vue-router'
-import UrlUtils from "~/services/utils/urlUtils";
+import UrlUtils from '~/services/utils/urlUtils'
 
 const residenceAreasRepo = useRepo(ResidenceAreasRepository)
 
@@ -70,7 +70,7 @@ const { pending } = fetchCollection(ResidenceArea)
  * On récupère les Residence Area via le store
  * (sans ça, les mises à jour SSE ne seront pas prises en compte)
  */
- const residenceAreas: ComputedRef<Array<ResidenceArea>> = computed(() => {
+const residenceAreas: ComputedRef<Array<ResidenceArea>> = computed(() => {
   return residenceAreasRepo.getResidenceAreas()
 })
 

+ 2 - 2
pages/parameters/residence_areas/new.vue

@@ -6,7 +6,7 @@
         :model="ResidenceArea"
         go-back-route="/parameters/residence_areas"
       >
-        <template v-slot="{ entity }">
+        <template #default="{ entity }">
           <v-container :fluid="true" class="container">
             <v-row>
               <v-col cols="12" sm="6">
@@ -26,8 +26,8 @@
 </template>
 
 <script setup lang="ts">
-import ResidenceArea from '~/models/Billing/ResidenceArea'
 import { useI18n } from 'vue-i18n'
+import ResidenceArea from '~/models/Billing/ResidenceArea'
 
 const i18n = useI18n()
 

+ 15 - 16
pages/parameters/sms.vue

@@ -1,16 +1,17 @@
 <template>
   <div>
     <UiForm
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-if="parameters"
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <UiInputText
-              v-model="parameters.smsSenderName"
-              field="smsSenderName"
-              variant="underlined"
+            v-model="parameters.smsSenderName"
+            field="smsSenderName"
+            variant="underlined"
           />
         </v-col>
         <v-col cols="12">
@@ -23,10 +24,10 @@
         </v-col>
         <v-col cols="12">
           <UiInputText
-              v-model="parameters.passwordSMS"
-              field="passwordSMS"
-              class="password"
-              variant="underlined"
+            v-model="parameters.passwordSMS"
+            field="passwordSMS"
+            class="password"
+            variant="underlined"
           />
         </v-col>
       </v-row>
@@ -34,10 +35,10 @@
   </div>
 </template>
 <script setup lang="ts">
+import type { AsyncData } from '#app'
 import Parameters from '~/models/Organization/Parameters'
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import { useOrganizationProfileStore } from '~/stores/organizationProfile'
-import type { AsyncData } from '#app'
 
 const { fetch } = useEntityFetch()
 
@@ -47,14 +48,13 @@ if (organizationProfile.parametersId === null) {
   throw new Error('Missing organization parameters id')
 }
 
-const { data: parameters, pending } = fetch(
+const { data: parameters } = fetch(
   Parameters,
-  organizationProfile.parametersId
-) as AsyncData<Parameters, Parameters | true>
+  organizationProfile.parametersId,
+) as AsyncData<Parameters | null, Error | null>
 </script>
 
 <style scoped lang="scss">
-
 /**
 Simule une apparence de saisie de type mot de passe
 Sans ça, les navigateurs proposent la saisie semi auto et la mémorisation du mot de passe
@@ -69,5 +69,4 @@ Sans ça, les navigateurs proposent la saisie semi auto et la mémorisation du m
 :deep(.password input) {
   font-family: 'password';
 }
-
 </style>

+ 38 - 43
pages/parameters/subdomains/[id].vue

@@ -3,10 +3,8 @@
   <main>
     <LayoutContainer>
       <UiLoadingPanel v-if="pending" />
-      <div v-else>
-        <div>
-          {{ $t('youRegisteredTheFollowingSubdomain')}} :
-        </div>
+      <div v-else-if="subdomain !== null">
+        <div>{{ $t('youRegisteredTheFollowingSubdomain') }} :</div>
 
         <div class="pa-8">
           <b>{{ subdomain.subdomain }}</b>
@@ -20,9 +18,7 @@
             </v-icon>
             {{ $t('subdomainIsCurrentlyActive') }}
           </div>
-          <div v-else>
-            {{ $t('doYouWantToActivateThisSubdomain') }} ?
-          </div>
+          <div v-else>{{ $t('doYouWantToActivateThisSubdomain') }} ?</div>
         </div>
 
         <div class="mt-6 d-flex flex-row">
@@ -30,7 +26,7 @@
             {{ $t('back') }}
           </v-btn>
           <div v-if="!subdomain.active">
-            <v-btn color="primary" @click="activateAndQuit" >
+            <v-btn color="primary" @click="activateAndQuit">
               {{ $t('activate') }}
             </v-btn>
           </div>
@@ -41,48 +37,47 @@
 </template>
 
 <script setup lang="ts">
-  import Subdomain from "~/models/Organization/Subdomain";
-  import {useEntityFetch} from "~/composables/data/useEntityFetch";
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import {useEntityManager} from "~/composables/data/useEntityManager";
-  import {usePageStore} from "~/stores/page";
-  import {TYPE_ALERT} from "~/types/enum/enums";
-  import {useRefreshProfile} from "~/composables/data/useRefreshProfile";
-
-  const { em } = useEntityManager()
-  const { fetch } = useEntityFetch()
-  const organizationProfile = useOrganizationProfileStore()
-
-  const router = useRouter()
-  const route = useRoute()
+import type { Ref } from 'vue'
+import Subdomain from '~/models/Organization/Subdomain'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useEntityManager } from '~/composables/data/useEntityManager'
+import { usePageStore } from '~/stores/page'
+import { TYPE_ALERT } from '~/types/enum/enums'
+import { useRefreshProfile } from '~/composables/data/useRefreshProfile'
 
-  const { refreshProfile } = useRefreshProfile()
+const { em } = useEntityManager()
+const { fetch } = useEntityFetch()
 
-  if (!route.params.id || isNaN(route.params.id as any)) {
-    throw new Error('no id found')
-  }
-  const id: number = parseInt(route.params.id as string)
+const router = useRouter()
+const route = useRoute()
 
-  const { data: subdomain, pending } = fetch(Subdomain, id)
+const { refreshProfile } = useRefreshProfile()
 
-  const activationPending: Ref<boolean> = ref(false)
+if (!route.params.id || /\d+/.test(route.params.id as string)) {
+  throw new Error('no id found')
+}
+const id: number = parseInt(route.params.id as string)
 
-  const pageStore = usePageStore()
+const { data: subdomain, pending } = fetch(Subdomain, id)
 
-  const activateAndQuit = async () => {
-    activationPending.value = true
-    pageStore.loading = true
-    await em.patch(Subdomain, id, { active: true} )
-    await refreshProfile()
-    usePageStore().addAlert(TYPE_ALERT.SUCCESS, ['subdomain_activated_and_available_in_a_few_minutes'])
-    quit()
-  }
+const activationPending: Ref<boolean> = ref(false)
 
-  const quit = () => {
-    router.push('/parameters/website')
-    activationPending.value = false
-    pageStore.loading = false
-  }
+const pageStore = usePageStore()
 
+const activateAndQuit = async () => {
+  activationPending.value = true
+  pageStore.loading = true
+  await em.patch(Subdomain, id, { active: true })
+  await refreshProfile()
+  usePageStore().addAlert(TYPE_ALERT.SUCCESS, [
+    'subdomain_activated_and_available_in_a_few_minutes',
+  ])
+  quit()
+}
 
+const quit = () => {
+  router.push('/parameters/website')
+  activationPending.value = false
+  pageStore.loading = false
+}
 </script>

+ 12 - 12
pages/parameters/subdomains/new.vue

@@ -5,7 +5,7 @@
         ref="form"
         :model="Subdomain"
         :entity="subdomain"
-        :submitActions="submitActions"
+        :submit-actions="submitActions"
         :validation-pending="validationPending"
         :refresh-profile="true"
       >
@@ -22,7 +22,7 @@
                 field="subdomain"
                 type="string"
                 :rules="rules()"
-                @update:modelValue="onSubdomainUpdate"
+                @update:model-value="onSubdomainUpdate"
               />
             </v-col>
           </v-row>
@@ -51,30 +51,31 @@
 </template>
 
 <script setup lang="ts">
+import type { Ref } from 'vue'
+import _ from 'lodash'
 import { useEntityManager } from '~/composables/data/useEntityManager'
 import Subdomain from '~/models/Organization/Subdomain'
 import { SUBMIT_TYPE } from '~/types/enum/enums'
 import type { AnyJson } from '~/types/data'
 import SubdomainValidation from '~/services/validation/subdomainValidation'
-import type { Ref } from '@vue/reactivity'
 import { useSubdomainValidation } from '~/composables/form/validation/useSubdomainValidation'
-import _ from 'lodash'
+import Form from '~/components/Ui/Form.vue'
 
 const i18n = useI18n()
 
 const { em } = useEntityManager()
 const { subdomainValidation } = useSubdomainValidation()
 
-//@ts-ignore
-const subdomain: Ref<Subdomain> = ref(em.newInstance(Subdomain) as Subdomain)
+// @ts-expect-error TODO à résoudre quand l'EM pourra gérer les types génériques
+const subdomain: Ref<Subdomain> = ref(em.newInstance(Subdomain))
 
 const submitActions = computed(() => {
-  let actions: AnyJson = {}
+  const actions: AnyJson = {}
   actions[SUBMIT_TYPE.SAVE_AND_BACK] = '/parameters/website'
   return actions
 })
 
-const form: Ref<HTMLCanvasElement | null> = ref(null)
+const form: Ref<typeof Form | null> = ref(null)
 const subdomainAvailable: Ref<boolean | null> = ref(null)
 const validationPending: Ref<boolean> = ref(false)
 
@@ -91,8 +92,7 @@ const checkAvailability = async (subdomain: string) => {
   subdomainAvailable.value = await subdomainValidation.isAvailable(subdomain)
   validationPending.value = false
 
-  //@ts-ignore
-  form.value.validate()
+  form.value!.validate()
 }
 
 /**
@@ -106,7 +106,7 @@ const checkAvailabilityDebounced: _.DebouncedFunc<() => void> = _.debounce(
     }
     await checkAvailability(subdomain.value.subdomain)
   },
-  inputDelay
+  inputDelay,
 )
 
 const onSubdomainUpdate = () => {
@@ -128,7 +128,7 @@ const rules = () => [
   (subdomain: string | null) =>
     SubdomainValidation.isValid(subdomain) ||
     i18n.t('subdomain_can_not_contain_spaces_or_special_cars'),
-  async () =>
+  () =>
     subdomainAvailable.value !== false ||
     i18n.t('this_subdomain_is_already_in_use'),
 ]

+ 35 - 35
pages/parameters/super_admin.vue

@@ -1,41 +1,41 @@
 <template>
   <div>
-      <div class="explanation">
-        <div class="px-6 d-flex flex-row align-center">
-          <v-icon class="theme-primary">fa fa-info</v-icon>
-        </div>
-        <div class="px-2">
-          {{ $t('super_admin_explanation_text')}}
-        </div>
+    <div class="explanation">
+      <div class="px-6 d-flex flex-row align-center">
+        <v-icon class="theme-primary">fa fa-info</v-icon>
       </div>
+      <div class="px-2">
+        {{ $t('super_admin_explanation_text') }}
+      </div>
+    </div>
 
-      <UiLoadingPanel v-if="pending"/>
-      <UiForm
-          v-else-if="adminAccess"
-          ref="form"
-          :model="AdminAccess"
-          :entity="adminAccess"
-          class="w-100"
-          action-position="bottom"
-      >
-        <v-table class="mb-4">
-          <tbody>
-            <tr>
-              <td>{{ $t('username') }} : </td>
-              <td>{{ adminAccess.username }}</td>
-            </tr>
-          </tbody>
-        </v-table>
+    <UiLoadingPanel v-if="pending" />
+    <UiForm
+      v-else-if="adminAccess"
+      ref="form"
+      :model="AdminAccess"
+      :entity="adminAccess"
+      class="w-100"
+      action-position="bottom"
+    >
+      <v-table class="mb-4">
+        <tbody>
+          <tr>
+            <td>{{ $t('username') }} :</td>
+            <td>{{ adminAccess.username }}</td>
+          </tr>
+        </tbody>
+      </v-table>
 
-        <UiInputText
-            field="email"
-            v-model="adminAccess.email"
-            :rules="rules()"
-            class="mx-4"
-            variant="underlined"
-        />
-      </UiForm>
-      <span v-else>{{ $t('no_admin_access_recorded') }}</span>
+      <UiInputText
+        v-model="adminAccess.email"
+        field="email"
+        :rules="rules()"
+        class="mx-4"
+        variant="underlined"
+      />
+    </UiForm>
+    <span v-else>{{ $t('no_admin_access_recorded') }}</span>
   </div>
 </template>
 
@@ -43,7 +43,7 @@
 import { useEntityFetch } from '~/composables/data/useEntityFetch'
 import { useAccessProfileStore } from '~/stores/accessProfile'
 import AdminAccess from '~/models/Access/AdminAccess'
-import {useValidationUtils} from "~/composables/utils/useValidationUtils";
+import { useValidationUtils } from '~/composables/utils/useValidationUtils'
 
 const { fetch } = useEntityFetch()
 
@@ -60,7 +60,7 @@ const validationUtils = useValidationUtils()
 
 const rules = () => [
   (email: string | null) =>
-    (email && validationUtils.validEmail(email)) || i18n.t('email_error')
+    (email && validationUtils.validEmail(email)) || i18n.t('email_error'),
 ]
 </script>
 

+ 38 - 29
pages/parameters/teaching.vue

@@ -2,10 +2,10 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else-if="parameters !== null"
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-table>
         <thead>
@@ -16,18 +16,22 @@
         </thead>
 
         <tbody>
-          <tr v-for="enumItem in cycleEnum">
+          <tr v-for="enumItem in cycleEnum" :key="enumItem.value">
             <td>{{ $t(enumItem.value) }}</td>
             <td class="cycle-editable-cell">
-              {{ orderedCycles[enumItem.value] ? orderedCycles[enumItem.value].label : $t(enumItem.value) }}
+              {{
+                orderedCycles[enumItem.value]
+                  ? orderedCycles[enumItem.value].label
+                  : $t(enumItem.value)
+              }}
             </td>
-            <td style="max-width: 24px;">
+            <td style="max-width: 24px">
               <v-btn
-                  v-if="orderedCycles[enumItem.value]"
-                  :flat="true"
-                  icon="fa fa-pen"
-                  class="cycle-edit-icon"
-                  @click="goToCycleEditPage(orderedCycles[enumItem.value].id)"
+                v-if="orderedCycles[enumItem.value]"
+                :flat="true"
+                icon="fa fa-pen"
+                class="cycle-edit-icon"
+                @click="goToCycleEditPage(orderedCycles[enumItem.value].id)"
               />
             </td>
           </tr>
@@ -35,22 +39,24 @@
       </v-table>
 
       <UiInputCheckbox
-          v-model="parameters.showEducationIsACollectivePractice"
-          field="showEducationIsACollectivePractice"
-          label="allow_to_configure_teachings_with_played_instrument_choice"
+        v-model="parameters.showEducationIsACollectivePractice"
+        field="showEducationIsACollectivePractice"
+        label="allow_to_configure_teachings_with_played_instrument_choice"
       />
     </UiForm>
   </LayoutContainer>
 </template>
 
 <script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import Cycle from "~/models/Education/Cycle";
-import type {AsyncData} from "#app";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import type {AnyJson} from "~/types/data";
-import {useEnumFetch} from "~/composables/data/useEnumFetch";
+import type { AsyncData } from '#app'
+import type { ComputedRef } from 'vue'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import Cycle from '~/models/Education/Cycle'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import type { AnyJson } from '~/types/data'
+import { useEnumFetch } from '~/composables/data/useEnumFetch'
+import ApiResource from '~/models/ApiResource'
 
 const organizationProfile = useOrganizationProfileStore()
 
@@ -61,28 +67,32 @@ if (organizationProfile.parametersId === null) {
 const { fetch, fetchCollection } = useEntityFetch()
 const { fetch: fetchEnum } = useEnumFetch()
 
-
 const { data: cycleEnum, pending: enumPending } = fetchEnum('education_cycle')
 
-const { data: parameters, pending: parametersPending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+const { data: parameters, pending: parametersPending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<ApiResource | null, Error | null>
 
 const { data: cycles, pending: cyclesPending } = fetchCollection(Cycle)
 
-const pending: ComputedRef<boolean> = computed(() => enumPending.value || parametersPending.value || cyclesPending.value)
+const pending: ComputedRef<boolean> = computed(
+  () => enumPending.value || parametersPending.value || cyclesPending.value,
+)
 
 const orderedCycles: ComputedRef<AnyJson> = computed(() => {
-  if (pending.value) {
+  if (pending.value || cycleEnum.value === null || cycles.value === null) {
     return []
   }
 
-  let orderedCycles: AnyJson = {}
+  const orderedCycles: AnyJson = {}
 
   for (const enumItem of cycleEnum.value) {
     orderedCycles[enumItem.value] = null
   }
 
   for (const cycle of cycles.value.items) {
-    if (!orderedCycles.hasOwnProperty(cycle.cycleEnum)) {
+    if (!Object.prototype.hasOwnProperty.call(orderedCycles, cycle.cycleEnum)) {
       console.error('Unknown cycle enum : ' + cycle.cycleEnum)
       continue
     }
@@ -93,7 +103,6 @@ const orderedCycles: ComputedRef<AnyJson> = computed(() => {
   return orderedCycles
 })
 
-
 const goToCycleEditPage = (id: number) => {
   navigateTo(`/parameters/cycles/${id}`)
 }

+ 80 - 64
pages/parameters/website.vue

@@ -2,15 +2,15 @@
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
     <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-        action-position="bottom"
+      v-else-if="parameters !== null"
+      :model="Parameters"
+      :entity="parameters"
+      action-position="bottom"
     >
       <v-row>
         <v-col cols="12">
           <div class="my-5">
-            <span>{{ $t('your_opentalent_website_address_is')}} : </span>
+            <span>{{ $t('your_opentalent_website_address_is') }} : </span>
             <strong class="ml-2">
               <a :href="organizationProfile.website ?? '#'" target="_blank">
                 {{ organizationProfile.website }}
@@ -20,105 +20,115 @@
 
           <!-- les publicationDirectors sont des entités Access -->
           <UiInputAutocompleteAccesses
-              v-model="parameters.publicationDirectors"
-              field="publicationDirectors"
-              multiple
-              chips
-              variant="underlined"
-              class="my-4"
+            v-model="parameters.publicationDirectors"
+            field="publicationDirectors"
+            multiple
+            chips
+            variant="underlined"
+            class="my-4"
           />
 
           <div>
             <UiInputText
-                v-model="parameters.otherWebsite"
-                field="otherWebsite"
-                variant="underlined"
-                class="my-4"
+              v-model="parameters.otherWebsite"
+              field="otherWebsite"
+              variant="underlined"
+              class="my-4"
             />
           </div>
 
-          <v-divider class="my-10"/>
+          <v-divider class="my-10" />
 
           <div class="mb-6">
-            <div><h4>{{ $t('your_subdomains') }} : </h4></div>
+            <div>
+              <h4>{{ $t('your_subdomains') }} :</h4>
+            </div>
             <UiLoadingPanel v-if="subdomainsPending" />
             <div v-else>
               <v-table v-if="subdomains" class="subdomains-table my-2">
                 <tbody>
                   <tr
-                      v-for="subdomain in subdomains.items"
-                      :key="subdomain.id"
-                      :title="subdomain.subdomain"
-                      :class="'subdomainItem' + (subdomain.active ? ' active' : '')"
-                      @click="goToEditPage(subdomain.id)"
+                    v-for="subdomain in subdomains.items"
+                    :key="subdomain.id"
+                    :title="subdomain.subdomain"
+                    :class="
+                      'subdomainItem' + (subdomain.active ? ' active' : '')
+                    "
+                    @click="goToEditPage(subdomain.id)"
                   >
                     <td>{{ subdomain.subdomain }}</td>
                     <td>
-                        <span v-if="subdomain.active">
-                          <v-icon class="text-success icon">
-                            fa-solid fa-check
-                          </v-icon> {{ $t('active') }}
-                        </span>
+                      <span v-if="subdomain.active">
+                        <v-icon class="text-success icon">
+                          fa-solid fa-check
+                        </v-icon>
+                        {{ $t('active') }}
+                      </span>
                     </td>
-
                   </tr>
-
                 </tbody>
-
               </v-table>
               <span v-else>{{ $t('no_recorded_subdomain') }}</span>
 
               <div class="d-flex flex-row justify-center w-100">
                 <v-btn
-                    :disabled="!canAddNewSubdomain"
-                    class="my-5"
-                    @click="onAddSubdomainClick"
+                  :disabled="!canAddNewSubdomain"
+                  class="my-5"
+                  @click="onAddSubdomainClick"
                 >
-                  {{ $t('record_a_new_subdomain')}}
+                  {{ $t('record_a_new_subdomain') }}
                 </v-btn>
               </div>
             </div>
           </div>
 
           <div v-if="!organizationProfile.isCmf">
-            <v-divider class="my-10"/>
+            <v-divider class="my-10" />
 
-            <div class="my-8 d-flex flex-row justify-center w-100" v-if="!organizationProfile.isCmf">
+            <div
+              v-if="!organizationProfile.isCmf"
+              class="my-8 d-flex flex-row justify-center w-100"
+            >
               <v-btn
                 v-if="!parameters.desactivateOpentalentSiteWeb"
                 color="error"
-                @click="showWebsiteDeactivationDialog=true"
+                @click="showWebsiteDeactivationDialog = true"
               >
                 {{ $t('deactivateOpentalentSiteWeb') }}
               </v-btn>
-              <v-btn
-                v-else
-                color="primary"
-                @click="reactivateWebsite"
-              >
+              <v-btn v-else color="primary" @click="reactivateWebsite">
                 {{ $t('reactivateOpentalentSiteWeb') }}
               </v-btn>
 
               <LazyLayoutDialog :show="showWebsiteDeactivationDialog">
                 <template #dialogTitle>
-                  {{ $t('please_confirm')}}
+                  {{ $t('please_confirm') }}
                 </template>
                 <template #dialogText>
                   <v-col>
-                    <div>{{ $t('yourOpentalentWebsiteWillBeDeactivatedOnceYouLlHaveSaved')}}.</div>
-                    <span>{{ $t('doYouWantToContinue')}} ?</span>
+                    <div>
+                      {{
+                        $t(
+                          'yourOpentalentWebsiteWillBeDeactivatedOnceYouLlHaveSaved',
+                        )
+                      }}.
+                    </div>
+                    <span>{{ $t('doYouWantToContinue') }} ?</span>
                   </v-col>
                 </template>
                 <template #dialogBtn>
                   <v-btn
-                      class="theme-neutral-soft mr-4"
-                      @click="showWebsiteDeactivationDialog=false"
+                    class="theme-neutral-soft mr-4"
+                    @click="showWebsiteDeactivationDialog = false"
                   >
                     {{ $t('cancel') }}
                   </v-btn>
                   <v-btn
-                      class="theme-primary"
-                      @click="showWebsiteDeactivationDialog=false; deactivateWebsite()"
+                    class="theme-primary"
+                    @click="
+                      showWebsiteDeactivationDialog = false
+                      deactivateWebsite()
+                    "
                   >
                     {{ $t('yes') }}
                   </v-btn>
@@ -133,13 +143,13 @@
 </template>
 
 <script setup lang="ts">
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import type {AsyncData} from "#app";
-import Subdomain from "~/models/Organization/Subdomain";
-
-const i18n = useI18n()
+import type { AsyncData } from '#app'
+import type { ComputedRef, Ref } from 'vue'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import Subdomain from '~/models/Organization/Subdomain'
+import ApiResource from '~/models/ApiResource'
 
 const { fetch, fetchCollection } = useEntityFetch()
 
@@ -149,11 +159,20 @@ if (organizationProfile.parametersId === null) {
   throw new Error('Missing organization parameters id')
 }
 
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId,
+) as AsyncData<ApiResource | null, Error | null>
 
-const { data: subdomains, pending: subdomainsPending } = fetchCollection(Subdomain, null, ref({ 'organization' : organizationProfile.id }) )
+const { data: subdomains, pending: subdomainsPending } = fetchCollection(
+  Subdomain,
+  null,
+  ref({ organization: organizationProfile.id }),
+)
 
-const canAddNewSubdomain: ComputedRef<boolean> = computed(() => subdomains.value && subdomains.value.items.length < 3)
+const canAddNewSubdomain: ComputedRef<boolean> = computed(
+  () => subdomains.value !== null && subdomains.value.items.length < 3,
+)
 
 const goToEditPage = (id: number) => {
   console.log(parameters.value)
@@ -169,18 +188,16 @@ const onAddSubdomainClick = () => {
 
 const showWebsiteDeactivationDialog: Ref<boolean> = ref(false)
 
-
 const deactivateWebsite = () => {
-  parameters.value.desactivateOpentalentSiteWeb = true
+  parameters.value!.desactivateOpentalentSiteWeb = true
 }
 
 const reactivateWebsite = () => {
-  parameters.value.desactivateOpentalentSiteWeb = false
+  parameters.value!.desactivateOpentalentSiteWeb = false
 }
 </script>
 
 <style scoped lang="scss">
-
 .subdomains-table {
   max-width: 450px;
 }
@@ -203,5 +220,4 @@ const reactivateWebsite = () => {
 .subdomainItem.active td:first-child {
   border-left: solid 2px rgb(var(--v-theme-primary));
 }
-
 </style>

+ 5 - 6
pages/poc.vue

@@ -2,16 +2,15 @@
   <main>
     <div class="pa-8">
       <h1>POC</h1>
-      <NuxtPage/>
+      <NuxtPage />
     </div>
   </main>
 </template>
 
-<script setup lang="ts">
-</script>
+<script setup lang="ts"></script>
 
 <style scoped lang="scss">
-  h1 {
-    color: rgb(var(--v-theme-primary))
-  }
+h1 {
+  color: rgb(var(--v-theme-primary));
+}
 </style>

部分文件因文件數量過多而無法顯示