Przeglądaj źródła

convert parameters menu from tabs to vertical menu

Olivier Massot 2 lat temu
rodzic
commit
126af22181

Plik diff jest za duży
+ 12 - 0
assets/css/global.scss


+ 0 - 45
components/Layout/Parameters/Attendances.vue

@@ -1,45 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm v-else :model="Parameters" :entity="parameters">
-      <v-row>
-        <v-col cols="6">
-          <UiInputCheckbox
-            v-model="parameters.sendAttendanceEmail"
-            field="sendAttendanceEmail"
-            label="sendAttendanceEmail"
-          />
-
-          <UiInputCheckbox
-            v-model="parameters.sendAttendanceSms"
-            field="sendAttendanceSms"
-          />
-
-          <UiInputCheckbox
-            v-model="parameters.notifyAdministrationAbsence"
-            field="notifyAdministrationAbsence"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
-</template>
-<script setup lang="ts">
-import Parameters from '~/models/Organization/Parameters'
-import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import { useOrganizationProfileStore } from '~/stores/organizationProfile'
-import { AsyncData } from '#app'
-
-const { fetch } = useEntityFetch()
-
-const organizationProfile = useOrganizationProfileStore()
-
-if (organizationProfile.parametersId === null) {
-  throw new Error('Missing organization parameters id')
-}
-
-const { data: parameters, pending } = fetch(
-  Parameters,
-  organizationProfile.parametersId
-) as AsyncData<Parameters, Parameters | true>
-</script>

+ 0 - 90
components/Layout/Parameters/Bulletin.vue

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

+ 0 - 79
components/Layout/Parameters/EducationNotation.vue

@@ -1,79 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-    >
-      <v-row>
-        <v-col cols="6">
-          <UiInputCheckbox
-              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"
-          />
-
-          <UiInputAutocompleteWithEnum
-              v-if="organizationProfile.hasModule('AdvancedEducationNotation')"
-              v-model="parameters.advancedEducationNotationType"
-              enum-name="advanced_education_notation"
-              field="advancedEducationNotationType"
-          />
-        </v-col>
-
-        <v-col cols="6">
-          <UiInputCheckbox
-              v-model="parameters.requiredValidation"
-              field="requiredValidation"
-              label="mandatory_validation_for_evaluations"
-          />
-
-          <UiInputAutocompleteWithEnum
-              v-model="parameters.educationPeriodicity"
-              enum-name="education_periodicity"
-              field="educationPeriodicity"
-          />
-
-          <UiInputNumber
-            v-model="parameters.average"
-            field="average"
-            label="max_note_for_pedagogical_followup"
-            :default="20"
-            :min="1"
-            :max="100"
-            class="mt-2"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import {AsyncData} from "#app";
-
-const i18n = useI18n()
-const { fetch } = useEntityFetch()
-
-const organizationProfile = useOrganizationProfileStore()
-
-if (organizationProfile.parametersId === null) {
-  throw new Error('Missing organization parameters id')
-}
-
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
-
-</script>
-
-<style scoped lang="scss">
-
-</style>

+ 0 - 101
components/Layout/Parameters/EducationTimings.vue

@@ -1,101 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <v-container v-else style="width: 500px;">
-      <v-col cols="12">
-        <v-row class="justify-center">
-          <v-table class="w-100">
-            <thead>
-              <tr>
-                <td>{{ $t('educationTimings') }}</td>
-                <td></td>
-              </tr>
-            </thead>
-            <tbody>
-              <tr v-if="educationTimings.length > 0" 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)"
-                  />
-                  <UiButtonDelete
-                      :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>
-                <td></td>
-              </tr>
-            </tbody>
-          </v-table>
-        </v-row>
-        <v-row class="justify-end">
-          <v-btn
-              :flat="true"
-              prepend-icon="fa fa-plus"
-              class="theme-primary mt-2"
-              @click="goToCreatePage"
-          >
-            {{ $t('add') }}
-          </v-btn>
-        </v-row>
-      </v-col>
-    </v-container>
-  </LayoutContainer>
-
-</template>
-
-<script setup lang="ts">
-import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import EducationTiming from '~/models/Education/EducationTiming'
-import { useRepo } from 'pinia-orm'
-import EducationTimingsRepository from '~/stores/repositories/EducationTimingsRepository'
-import {ComputedRef} from "vue";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import UrlUtils from "~/services/utils/urlUtils";
-
-const organizationProfile = useOrganizationProfileStore()
-
-if (organizationProfile.parametersId === null) {
-  throw new Error('Missing organization parameters id')
-}
-
-const { fetch, fetchCollection } = useEntityFetch()
-
-const { pending } = fetchCollection(EducationTiming)
-
-const educationTimingRepo = useRepo(EducationTimingsRepository)
-
-/**
- * On récupère les timings via le store
- * (sans ça, les mises à jour SSE ne seront pas prises en compte)
- */
-const educationTimings: ComputedRef<Array<EducationTiming>> = computed(() => {
-  return educationTimingRepo.getEducationTimings()
-})
-
-const goToEditPage = (id: number) => {
-  navigateTo(UrlUtils.join('/parameters/education_timings', id))
-}
-
-const goToCreatePage = () => {
-  navigateTo('/parameters/education_timings/new')
-}
-</script>
-
-<style scoped lang="scss">
-// TODO: voir à factoriser ces styles, ptêt en faisant un component de ces boutons?
-:deep(.cycle-edit-icon .v-icon) {
-  color: rgb(var(--v-theme-primary));
-  font-size: 18px;
-}
-</style>

+ 0 - 96
components/Layout/Parameters/General.vue

@@ -1,96 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-    >
-      <v-row>
-        <v-col cols="6">
-          <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.financialDate"
-              field="financialDate"
-              label="start_date_of_financial_season"
-              class="my-2"
-          />
-
-          <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.startCourseDate"
-              field="startCourseDate"
-              label="start_date_of_courses"
-              class="my-2"
-          />
-
-          <UiInputCheckbox
-              v-model="parameters.showAdherentList"
-              field="showAdherentList"
-              label="show_adherents_list_and_their_coordinates"
-          />
-
-          <UiInputAutocompleteWithEnum
-              v-model="parameters.timezone"
-              enum-name="timezone"
-              field="timezone"
-          />
-        </v-col>
-
-        <v-col cols="6">
-          <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.musicalDate"
-              field="musicalDate"
-              label="start_date_of_activity_season"
-              class="my-2"
-          />
-
-          <UiInputDatePicker
-              v-if="organizationProfile.isSchool"
-              v-model="parameters.endCourseDate"
-              field="endCourseDate"
-              label="end_date_of_courses"
-              class="my-2"
-          />
-
-          <UiInputCheckbox
-              v-if="organizationProfile.isSchool && organizationProfile.isAssociation"
-              v-model="parameters.studentsAreAdherents"
-              field="studentsAreAdherents"
-              label="students_are_also_association_members"
-          />
-
-          <!-- TODO: reprendre l'UiInput -->
-          <UiInputImage
-              v-model="parameters['qrCode_id']"
-              field="qrCode_id"
-              label="licenceQrCode"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import {AsyncData} from "#app";
-
-const { fetch } = useEntityFetch()
-
-const organizationProfile = useOrganizationProfileStore()
-
-if (organizationProfile.parametersId === null) {
-  throw new Error('Missing organization parameters id')
-}
-
-const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
-</script>
-
-<style scoped lang="scss">
-
-
-</style>

+ 0 - 73
components/Layout/Parameters/Intranet.vue

@@ -1,73 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-    >
-      <v-row>
-        <v-col cols="6">
-          <UiInputCheckbox
-              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"
-          />
-
-          <UiInputCheckbox
-              v-model="parameters.allowMembersToChangeGivenNameAndName"
-              field="allowMembersToChangeGivenNameAndName"
-              label="allow_members_to_change_their_names_and_firstnames"
-          />
-        </v-col>
-
-        <v-col cols="6">
-          <UiInputCheckbox
-              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"
-          />
-
-          <UiInputCheckbox
-              v-model="parameters.showAdherentList"
-              field="showAdherentList"
-              label="allow_students_to_consult_their_pedagogical_followup"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-  import Parameters from "~/models/Organization/Parameters";
-  import {useEntityFetch} from "~/composables/data/useEntityFetch";
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-  import {AsyncData} from "#app";
-
-  const { fetch } = useEntityFetch()
-
-  const organizationProfile = useOrganizationProfileStore()
-
-  if (organizationProfile.parametersId === null) {
-    throw new Error('Missing organization parameters id')
-  }
-
-  const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
-</script>
-
-<style scoped lang="scss">
-
-</style>

+ 27 - 0
components/Layout/Parameters/Menu.vue

@@ -0,0 +1,27 @@
+<template>
+  <v-navigation-drawer>
+    <v-list>
+      <v-list-item :title="$t('general_parameters')" to="/parameters/general_parameters"></v-list-item>
+      <v-list-item :title="$t('website')" to="/parameters/website"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('teaching')" to="/parameters/teaching"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('intranet_access')" to="/parameters/intranet"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('educationNotations')" to="/parameters/education_notation"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('bulletin')" to="/parameters/bulletin"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('educationTimings')" to="/parameters/education_timings"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('attendances')" to="/parameters/attendances"></v-list-item>
+      <v-list-item v-if="organizationProfile.isSchool" :title="$t('residenceAreas')" to="/parameters/residence_areas"></v-list-item>
+      <v-list-item v-if="organizationProfile.hasModule('Sms')" :title="$t('sms_option')" to="/parameters/sms"></v-list-item>
+      <v-list-item :title="$t('super_admin')" to="/parameters/super_admin"></v-list-item>
+    </v-list>
+  </v-navigation-drawer>
+</template>
+
+<script setup lang="ts">
+  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+
+  const organizationProfile = useOrganizationProfileStore()
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 0 - 95
components/Layout/Parameters/ResidenceAreas.vue

@@ -1,95 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <v-container v-else style="width: 500px;">
-      <v-col cols="12">
-        <v-row class="justify-center">
-          <v-table class="w-100">
-            <thead>
-            <tr>
-              <td>{{ $t('residenceAreas') }}</td>
-              <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
-                    :flat="true"
-                    icon="fa fa-pen"
-                    class="cycle-edit-icon mr-3"
-                    @click="goToEditPage(residenceArea.id as number)"
-                />
-                <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>
-            </tbody>
-          </v-table>
-        </v-row>
-        <v-row class="justify-end">
-          <v-btn
-              :flat="true"
-              prepend-icon="fa fa-plus"
-              class="theme-primary mt-2"
-              @click="goToCreatePage"
-          >
-            {{ $t('add') }}
-          </v-btn>
-        </v-row>
-      </v-col>
-    </v-container>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-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 EducationTiming from "~/models/Education/EducationTiming";
-const residenceAreasRepo = useRepo(ResidenceAreasRepository)
-
-const router = useRouter()
-const { fetchCollection } = useEntityFetch()
-const i18n = useI18n()
-
-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(() => {
-  return residenceAreasRepo.getResidenceAreas()
-})
-
-const goToEditPage = (id: number) => {
-  navigateTo(UrlUtils.join('/parameters/residence_areas', id))
-}
-
-const goToCreatePage = () => {
-  navigateTo(`/parameters/residence_areas/new`)
-}
-</script>
-
-<style scoped lang="scss">
-// TODO: voir à factoriser ces styles, ptêt en faisant un component de ces boutons?
-:deep(.cycle-edit-icon .v-icon) {
-  color: rgb(var(--v-theme-primary));
-  font-size: 18px;
-}
-</style>

+ 0 - 47
components/Layout/Parameters/Sms.vue

@@ -1,47 +0,0 @@
-<template>
-  <div>
-    <UiForm :model="Parameters" :entity="parameters">
-      <v-row>
-        <v-col cols="12">
-          <UiInputText
-            v-model="parameters.smsSenderName"
-            field="smsSenderName"
-          />
-        </v-col>
-        <v-col cols="12">
-          <UiInputText
-            v-model="parameters.usernameSMS"
-            field="usernameSMS"
-            label="Nom d'utilisateur SMS"
-          />
-        </v-col>
-        <v-col cols="12">
-          <UiInputText
-              v-model="parameters.passwordSMS"
-              field="passwordSMS"
-              type="password"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </div>
-</template>
-<script setup lang="ts">
-import Parameters from '~/models/Organization/Parameters'
-import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import { useOrganizationProfileStore } from '~/stores/organizationProfile'
-import { AsyncData } from '#app'
-
-const { fetch } = useEntityFetch()
-
-const organizationProfile = useOrganizationProfileStore()
-
-if (organizationProfile.parametersId === null) {
-  throw new Error('Missing organization parameters id')
-}
-
-const { data: parameters, pending } = fetch(
-  Parameters,
-  organizationProfile.parametersId
-) as AsyncData<Parameters, Parameters | true>
-</script>

+ 0 - 97
components/Layout/Parameters/SuperAdmin.vue

@@ -1,97 +0,0 @@
-<template>
-  <div>
-    <v-container>
-      <v-row>
-        <v-col cols="1">
-        </v-col>
-        <v-col cols="4">
-          <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>
-        </v-col>
-        <v-col cols="1" />
-        <v-col cols="6">
-          <v-row>
-
-          </v-row>
-          <v-row v-if="pending">
-            <UiLoadingPanel/>
-          </v-row>
-          <v-row v-else>
-            <UiForm
-                ref="form"
-                :model="AdminAccess"
-                :entity="adminAccess"
-                class="w-100"
-            >
-              <div class="d-flex flex-row mx-4 my-6">
-                <span>{{ $t('username') }} :</span> <pre> {{ adminAccess.username }}</pre>
-              </div>
-
-              <UiInputText
-                  field="email"
-                  v-model="adminAccess.email"
-                  :rules="rules()"
-              />
-            </UiForm>
-          </v-row>
-        </v-col>
-      </v-row>
-    </v-container>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { useEntityFetch } from '~/composables/data/useEntityFetch'
-import { useAccessProfileStore } from '~/stores/accessProfile'
-import AdminAccess from '~/models/Access/AdminAccess'
-import {useValidationUtils} from "~/composables/utils/useValidationUtils";
-
-const { fetch } = useEntityFetch()
-
-const accessProfile = useAccessProfileStore()
-if (accessProfile.id === null) {
-  throw new Error('Missing access profile id')
-}
-
-const { data: adminAccess, pending } = fetch(AdminAccess, accessProfile.id)
-
-const i18n = useI18n()
-
-const validationUtils = useValidationUtils()
-
-
-const rules = () => [
-  (email: string | null) =>
-    (email && validationUtils.validEmail(email)) || i18n.t('email_error')
-]
-</script>
-
-<style scoped lang="scss">
-.explanation {
-  display: flex;
-  flex-direction: row;
-  padding: 60px 26px;
-  text-align: justify;
-  color: rgb(var(--v-theme-neutral-strong));
-
-  .v-icon {
-    background-color: rgb(var(--v-theme-primary));
-    font-size: 22px;
-    border-radius: 16px;
-    margin: 3px;
-    padding: 3px;
-    height: 28px;
-    width: 28px;
-  }
-
-  div:first-child {
-    border-right: solid 1px rgb(var(--v-theme-primary));
-  }
-}
-</style>

+ 0 - 182
components/Layout/Parameters/Website.vue

@@ -1,182 +0,0 @@
-<template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-    >
-      <v-row>
-        <v-col cols="6">
-          <div class="mb-6">
-            <div>{{ $t('your_opentalent_website_address_is')}} : </div>
-            <div class="ma-2 text-primary">
-              <strong>{{ organizationProfile.website }}</strong>
-            </div>
-          </div>
-
-          <div class="mb-6">
-            <div>{{ $t('your_subdomains') }} : </div>
-            <UiLoadingPanel v-if="subdomainsPending" />
-            <div v-else>
-              <v-table class="my-2">
-                <tbody>
-                  <tr
-                      v-for="subdomain in subdomains.items"
-                      :key="subdomain.id"
-                      :title="subdomain.subdomain"
-                      class="subdomainItem"
-                      @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>
-                    </td>
-
-                  </tr>
-
-                </tbody>
-
-              </v-table>
-
-              <v-btn
-                  :disabled="!canAddNewSubdomain"
-                  class="my-2"
-                  @click="onAddSubdomainClick"
-              >
-                {{ $t('record_a_new_subdomain')}}
-              </v-btn>
-            </div>
-          </div>
-        </v-col>
-
-        <v-col cols="6">
-            <!-- les publicationDirectors sont des entités Access -->
-            <UiInputAutocompleteAccesses
-                v-model="parameters.publicationDirectors"
-                field="publicationDirectors"
-                multiple
-                chips
-            />
-
-          <div class="my-8" v-if="!organizationProfile.isCmf">
-            <v-btn
-              v-if="!parameters.desactivateOpentalentSiteWeb"
-              color="error"
-              @click="showWebsiteDeactivationDialog=true"
-            >
-              {{ $t('deactivateOpentalentSiteWeb') }}
-            </v-btn>
-            <v-btn
-              v-else
-              color="primary"
-              @click="reactivateWebsite"
-            >
-              {{ $t('reactivateOpentalentSiteWeb') }}
-            </v-btn>
-
-            <LazyLayoutDialog :show="showWebsiteDeactivationDialog">
-              <template #dialogTitle>
-                {{ $t('please_confirm')}}
-              </template>
-              <template #dialogText>
-                <v-col>
-                  <div>{{ $t('yourOpentalentWebsiteWillBeDeactivatedOnceYouLlHaveSaved')}}.</div>
-                  <span>{{ $t('doYouWantToContinue')}} ?</span>
-                </v-col>
-              </template>
-              <template #dialogBtn>
-                <v-btn
-                    class="theme-neutral-soft mr-4"
-                    @click="showWebsiteDeactivationDialog=false"
-                >
-                  {{ $t('cancel') }}
-                </v-btn>
-                <v-btn
-                    class="theme-primary"
-                    @click="showWebsiteDeactivationDialog=false; deactivateWebsite()"
-                >
-                  {{ $t('yes') }}
-                </v-btn>
-              </template>
-            </LazyLayoutDialog>
-          </div>
-
-          <div>
-            <UiInputText
-                v-model="parameters.otherWebsite"
-                field="otherWebsite"
-            />
-          </div>
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
-</template>
-
-<script setup lang="ts">
-import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-import Parameters from "~/models/Organization/Parameters";
-import {useEntityFetch} from "~/composables/data/useEntityFetch";
-import {AsyncData} from "#app";
-import Subdomain from "~/models/Organization/Subdomain";
-
-const i18n = useI18n()
-
-const { fetch, fetchCollection } = useEntityFetch()
-
-const organizationProfile = useOrganizationProfileStore()
-
-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: subdomains, pending: subdomainsPending } = fetchCollection(Subdomain, null, ref({ 'organization' : organizationProfile.id }) )
-
-const canAddNewSubdomain: ComputedRef<boolean> = computed(() => subdomains.value && subdomains.value.items.length < 3)
-
-const goToEditPage = (id: number) => {
-  console.log(parameters.value)
-  navigateTo(`parameters/subdomains/${id}`)
-}
-
-const onAddSubdomainClick = () => {
-  if (!canAddNewSubdomain) {
-    throw new Error('Max number of subdomains reached')
-  }
-  navigateTo('/parameters/subdomains/new')
-}
-
-const showWebsiteDeactivationDialog: Ref<boolean> = ref(false)
-
-
-const deactivateWebsite = () => {
-  parameters.value.desactivateOpentalentSiteWeb = true
-}
-
-const reactivateWebsite = () => {
-  parameters.value.desactivateOpentalentSiteWeb = false
-}
-</script>
-
-<style scoped lang="scss">
-.v-table {
-  background: transparent;
-}
-.subdomainItem {
-  cursor: pointer;
-}
-.subdomainItem:hover {
-  background: rgb(var(--v-theme-neutral));
-}
-.subdomainItem .icon {
-  font-size: 12px;
-}
-
-</style>

+ 4 - 0
components/Ui/Input/Autocomplete/Accesses.vue

@@ -176,6 +176,10 @@ initialized.value = true
 const items: ComputedRef<Array<AccessListItem>> = computed(() => {
   let items = props.modelValue.map(getFromStore).map(accessToItem)
 
+  if (!collection.value) {
+    return []
+  }
+
   //@ts-ignore
   const fetchedItems = collection.value.items.map(accessToItem)
 

+ 9 - 0
lang/fr.json

@@ -404,6 +404,7 @@
   "network": "Réseau",
   "schedule": "Agenda",
   "attendances": "Absences",
+  "attendances_breadcrumbs": "Absences",
   "equipment": "Parc matériel",
   "education_state": "Suivi pédagogique",
   "criteria_notations": "Critères d'évaluation",
@@ -467,6 +468,7 @@
   "right_menu": "Droits version 5.9",
   "tree_menu": "Gestion de l'arbre",
   "website": "Site internet",
+  "website_breadcrumbs": "Site internet",
   "websiteList": "Site(s) internet",
   "advanced_modification": "Administration site internet",
   "simple_modification": "Modifications simplifiées",
@@ -598,15 +600,22 @@
   "show_adherents_list_and_their_coordinates": "Afficher la liste des adhérents et leurs coordonnées",
   "students_are_also_association_members": "Les élèves sont adhérents également de l'association",
   "general_parameters": "Paramètres généraux",
+  "general_parameters_breadcrumbs": "Paramètres généraux",
   "teaching": "Enseignements",
+  "teaching_breadcrumbs": "Enseignements",
   "intranet_access": "Accès intranet (professeurs, élèves...)",
+  "intranet_breadcrumbs": "Accès intranet",
   "educationNotations": "Suivi pédagogique",
+  "education_notation_breadcrumbs": "Suivi pédagogique",
   "bulletin": "Bulletins",
+  "bulletin_breadcrumbs": "Bulletins",
   "educationTimings": "Durée des cours (en minutes)",
   "new_education_timing": "Nouvelle durée de cours",
   "residenceAreas": "Zones de résidence",
   "sms_option": "Option SMS",
+  "sms_breadcrumbs": "SMS",
   "super_admin": "Compte super-admin",
+  "super_admin_breadcrumbs": "Compte super-admin",
   "an_error_happened": "Une erreur s'est produite",
   "your_opentalent_website_address_is": "L'adresse de votre site Opentalent est",
   "record_a_new_subdomain": "Enregistrer un nouveau sous-domaine",

Plik diff jest za duży
+ 0 - 14
layouts/default.vue


+ 27 - 0
layouts/parameters.vue

@@ -0,0 +1,27 @@
+<template>
+  <div>
+    <!-- Show the loading page -->
+    <client-only placeholder-tag="client-only-placeholder" placeholder=" " />
+
+    <v-app>
+      <LayoutLoadingScreen />
+
+      <LayoutHeader />
+
+      <LayoutParametersMenu />
+
+      <v-main class="main">
+
+        <LayoutSubheader />
+
+        <LayoutAlertBar class="mt-1" />
+
+        <!-- Page will be rendered here-->
+        <slot />
+      </v-main>
+
+      <LazyLayoutAlertContainer />
+
+    </v-app>
+  </div>
+</template>

+ 55 - 0
pages/parameters/attendances.vue

@@ -0,0 +1,55 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm v-else :model="Parameters" :entity="parameters">
+        <v-row>
+          <v-col cols="6">
+            <UiInputCheckbox
+              v-model="parameters.sendAttendanceEmail"
+              field="sendAttendanceEmail"
+              label="sendAttendanceEmail"
+            />
+
+            <UiInputCheckbox
+              v-model="parameters.sendAttendanceSms"
+              field="sendAttendanceSms"
+            />
+
+            <UiInputCheckbox
+              v-model="parameters.notifyAdministrationAbsence"
+              field="notifyAdministrationAbsence"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+<script setup lang="ts">
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { AsyncData } from '#app'
+
+/**
+ * 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,
+});
+
+const { fetch } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId
+) as AsyncData<Parameters, Parameters | true>
+</script>

+ 100 - 0
pages/parameters/bulletin.vue

@@ -0,0 +1,100 @@
+
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-row>
+          <v-col cols="6">
+            <UiInputCheckbox
+                v-model="parameters.bulletinWithTeacher"
+                field="bulletinWithTeacher"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinSignatureDirector"
+                field="bulletinSignatureDirector"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinShowEducationWithoutEvaluation"
+                field="bulletinShowEducationWithoutEvaluation"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinShowAbsences"
+                field="bulletinShowAbsences"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinEditWithoutEvaluation"
+                field="bulletinEditWithoutEvaluation"
+            />
+          </v-col>
+
+          <v-col cols="6">
+            <UiInputCheckbox
+                v-model="parameters.bulletinPrintAddress"
+                field="bulletinPrintAddress"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinDisplayLevelAcquired"
+                field="bulletinDisplayLevelAcquired"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinViewTestResults"
+                field="bulletinViewTestResults"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.bulletinShowAverages"
+                field="bulletinShowAverages"
+            />
+
+            <UiInputAutocompleteWithEnum
+                v-model="parameters.bulletinReceiver"
+                field="bulletinReceiver"
+                enum-name="organization_bulletin_send_to"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+import Parameters from "~/models/Organization/Parameters";
+import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import {AsyncData} from "#app";
+
+/**
+ * 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,
+});
+
+const { fetch } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 89 - 0
pages/parameters/education_notation.vue

@@ -0,0 +1,89 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-row>
+          <v-col cols="6">
+            <UiInputCheckbox
+                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"
+            />
+
+            <UiInputAutocompleteWithEnum
+                v-if="organizationProfile.hasModule('AdvancedEducationNotation')"
+                v-model="parameters.advancedEducationNotationType"
+                enum-name="advanced_education_notation"
+                field="advancedEducationNotationType"
+            />
+          </v-col>
+
+          <v-col cols="6">
+            <UiInputCheckbox
+                v-model="parameters.requiredValidation"
+                field="requiredValidation"
+                label="mandatory_validation_for_evaluations"
+            />
+
+            <UiInputAutocompleteWithEnum
+                v-model="parameters.educationPeriodicity"
+                enum-name="education_periodicity"
+                field="educationPeriodicity"
+            />
+
+            <UiInputNumber
+              v-model="parameters.average"
+              field="average"
+              label="max_note_for_pedagogical_followup"
+              :default="20"
+              :min="1"
+              :max="100"
+              class="mt-2"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+import Parameters from "~/models/Organization/Parameters";
+import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import {AsyncData} from "#app";
+
+/**
+ * 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,
+});
+
+const i18n = useI18n()
+const { fetch } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 111 - 0
pages/parameters/education_timings.vue

@@ -0,0 +1,111 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <v-container v-else style="width: 500px;">
+        <v-col cols="12">
+          <v-row class="justify-center">
+            <v-table class="w-100">
+              <thead>
+                <tr>
+                  <td>{{ $t('educationTimings') }}</td>
+                  <td></td>
+                </tr>
+              </thead>
+              <tbody>
+                <tr v-if="educationTimings.length > 0" 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)"
+                    />
+                    <UiButtonDelete
+                        :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>
+                  <td></td>
+                </tr>
+              </tbody>
+            </v-table>
+          </v-row>
+          <v-row class="justify-end">
+            <v-btn
+                :flat="true"
+                prepend-icon="fa fa-plus"
+                class="theme-primary mt-2"
+                @click="goToCreatePage"
+            >
+              {{ $t('add') }}
+            </v-btn>
+          </v-row>
+        </v-col>
+      </v-container>
+    </LayoutContainer>
+  </NuxtLayout>
+
+</template>
+
+<script setup lang="ts">
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import EducationTiming from '~/models/Education/EducationTiming'
+import { useRepo } from 'pinia-orm'
+import EducationTimingsRepository from '~/stores/repositories/EducationTimingsRepository'
+import {ComputedRef} from "vue";
+import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import UrlUtils from "~/services/utils/urlUtils";
+
+/**
+ * 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,
+});
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { fetch, fetchCollection } = useEntityFetch()
+
+const { pending } = fetchCollection(EducationTiming)
+
+const educationTimingRepo = useRepo(EducationTimingsRepository)
+
+/**
+ * On récupère les timings via le store
+ * (sans ça, les mises à jour SSE ne seront pas prises en compte)
+ */
+const educationTimings: ComputedRef<Array<EducationTiming>> = computed(() => {
+  return educationTimingRepo.getEducationTimings()
+})
+
+const goToEditPage = (id: number) => {
+  navigateTo(UrlUtils.join('/parameters/education_timings', id))
+}
+
+const goToCreatePage = () => {
+  navigateTo('/parameters/education_timings/new')
+}
+</script>
+
+<style scoped lang="scss">
+// TODO: voir à factoriser ces styles, ptêt en faisant un component de ces boutons?
+:deep(.cycle-edit-icon .v-icon) {
+  color: rgb(var(--v-theme-primary));
+  font-size: 18px;
+}
+</style>

+ 101 - 0
pages/parameters/general_parameters.vue

@@ -0,0 +1,101 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-row>
+          <v-col cols="6">
+            <UiInputDatePicker
+                v-if="organizationProfile.isSchool"
+                v-model="parameters.financialDate"
+                field="financialDate"
+                label="start_date_of_financial_season"
+                class="my-2"
+            />
+
+            <UiInputDatePicker
+                v-if="organizationProfile.isSchool"
+                v-model="parameters.startCourseDate"
+                field="startCourseDate"
+                label="start_date_of_courses"
+                class="my-2"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.showAdherentList"
+                field="showAdherentList"
+                label="show_adherents_list_and_their_coordinates"
+            />
+
+            <UiInputAutocompleteWithEnum
+                v-model="parameters.timezone"
+                enum-name="timezone"
+                field="timezone"
+            />
+          </v-col>
+
+          <v-col cols="6">
+            <UiInputDatePicker
+                v-if="organizationProfile.isSchool"
+                v-model="parameters.musicalDate"
+                field="musicalDate"
+                label="start_date_of_activity_season"
+                class="my-2"
+            />
+
+            <UiInputDatePicker
+                v-if="organizationProfile.isSchool"
+                v-model="parameters.endCourseDate"
+                field="endCourseDate"
+                label="end_date_of_courses"
+                class="my-2"
+            />
+
+            <UiInputCheckbox
+                v-if="organizationProfile.isSchool && organizationProfile.isAssociation"
+                v-model="parameters.studentsAreAdherents"
+                field="studentsAreAdherents"
+                label="students_are_also_association_members"
+            />
+
+            <!-- TODO: reprendre l'UiInput -->
+            <UiInputImage
+                v-model="parameters['qrCode_id']"
+                field="qrCode_id"
+                label="licenceQrCode"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+import Parameters from "~/models/Organization/Parameters";
+import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import {AsyncData} from "#app";
+
+/**
+ * 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,
+});
+
+const { fetch } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+</script>

+ 5 - 131
pages/parameters/index.vue

@@ -1,136 +1,10 @@
-<!--
-Page Paramètres
--->
 <template>
-  <LayoutContainer>
-    <v-col cols="12" sm="12" md="12">
-      <v-tabs
-          :model-value="currentTab"
-          bg-color="primary"
-          color="on-primary"
-          :grow="true"
-          density="default"
-          @update:model-value="onTabUpdate"
-      >
-        <v-tab v-for="tab in tabs" :value="tab">
-          {{ $t(tab) }}
-        </v-tab>
-      </v-tabs>
-
-      <v-card-text>
-        <v-window v-model="currentTab">
-          <v-window-item value="general_parameters">
-            <LayoutParametersGeneral />
-          </v-window-item>
-
-          <v-window-item value="website">
-            <LayoutParametersWebsite />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="teaching">
-            <LayoutParametersTeaching />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="intranet_access">
-            <LayoutParametersIntranet />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="educationNotations">
-            <LayoutParametersEducationNotation />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="bulletin">
-            <LayoutParametersBulletin />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="educationTimings">
-            <LayoutParametersEducationTimings />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="attendances">
-            <LayoutParametersAttendances />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.isSchool" value="residenceAreas">
-            <LayoutParametersResidenceAreas />
-          </v-window-item>
-
-          <v-window-item v-if="organizationProfile.hasModule('Sms')" value="sms_option">
-            <LayoutParametersSms />
-          </v-window-item>
-
-          <v-window-item value="super_admin">
-            <LayoutParametersSuperAdmin/>
-          </v-window-item>
-        </v-window>
-      </v-card-text>
-
-    </v-col>
-  </LayoutContainer>
 </template>
 
 <script setup lang="ts">
-
-  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
-
-  const organizationProfile = useOrganizationProfileStore()
-
-  const tabs = [
-    'general_parameters',
-    'website',
-    organizationProfile.isSchool ? 'teaching' : null,
-    organizationProfile.isSchool ? 'intranet_access' : null,
-    organizationProfile.isSchool ? 'educationNotations': null,
-    organizationProfile.isSchool ? 'bulletin' : null,
-    organizationProfile.isSchool ? 'educationTimings' : null,
-    organizationProfile.isSchool ? 'attendances' : null,
-    organizationProfile.isSchool ? 'residenceAreas' : null,
-    organizationProfile.hasModule('Sms') ? 'sms_option' : null,
-    'super_admin',
-
-  ].filter((v) => v !== null)
-
-  const router = useRouter()
-  const route = useRoute()
-  let mounted = false
-
-
-  /**
-   * Update the current route's query with a new value for 'tab' parameter
-   * @param tab
-   */
-  const updateQuery = (tab: string) => {
-    router.replace({ query: { ...route.query, tab } })
-  }
-
-  const currentTab: Ref<string | null> = ref(null)
-
-  onMounted(() => {
-    if (!route.query || !route.query.tab || !tabs.includes(route.query.tab as string)) {
-      const tab = tabs[0] ?? 'general_parameters'
-      currentTab.value = tab
-      updateQuery(tab)
-    } else {
-      currentTab.value = route.query.tab as string
-    }
-
-    mounted = true
-  })
-
-  const onTabUpdate = (tab: string) => {
-    if (!mounted) {
-      return
-    }
-    updateQuery(tab)
-    currentTab.value = tab
-  }
+/** Redirect to /parameters/general_parameters */
+const router = useRouter()
+router.push(
+    { path: `/parameters/general_parameters` }
+)
 </script>
-
-<style scoped lang="scss">
-
-:deep(.v-tabs .v-btn__content) {
-  text-transform: capitalize;
-  letter-spacing: 0.04em;
-}
-</style>
-

+ 83 - 0
pages/parameters/intranet.vue

@@ -0,0 +1,83 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-row>
+          <v-col cols="6">
+            <UiInputCheckbox
+                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"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.allowMembersToChangeGivenNameAndName"
+                field="allowMembersToChangeGivenNameAndName"
+                label="allow_members_to_change_their_names_and_firstnames"
+            />
+          </v-col>
+
+          <v-col cols="6">
+            <UiInputCheckbox
+                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"
+            />
+
+            <UiInputCheckbox
+                v-model="parameters.showAdherentList"
+                field="showAdherentList"
+                label="allow_students_to_consult_their_pedagogical_followup"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+  import Parameters from "~/models/Organization/Parameters";
+  import {useEntityFetch} from "~/composables/data/useEntityFetch";
+  import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+  import {AsyncData} from "#app";
+
+  /**
+   * 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,
+  });
+
+  const { fetch } = useEntityFetch()
+
+  const organizationProfile = useOrganizationProfileStore()
+
+  if (organizationProfile.parametersId === null) {
+    throw new Error('Missing organization parameters id')
+  }
+
+  const { data: parameters, pending } = fetch(Parameters, organizationProfile.parametersId) as AsyncData<Parameters, Parameters | true>
+</script>
+
+<style scoped lang="scss">
+
+</style>

+ 105 - 0
pages/parameters/residence_areas.vue

@@ -0,0 +1,105 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <v-container v-else style="width: 500px;">
+        <v-col cols="12">
+          <v-row class="justify-center">
+            <v-table class="w-100">
+              <thead>
+              <tr>
+                <td>{{ $t('residenceAreas') }}</td>
+                <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
+                      :flat="true"
+                      icon="fa fa-pen"
+                      class="cycle-edit-icon mr-3"
+                      @click="goToEditPage(residenceArea.id as number)"
+                  />
+                  <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>
+              </tbody>
+            </v-table>
+          </v-row>
+          <v-row class="justify-end">
+            <v-btn
+                :flat="true"
+                prepend-icon="fa fa-plus"
+                class="theme-primary mt-2"
+                @click="goToCreatePage"
+            >
+              {{ $t('add') }}
+            </v-btn>
+          </v-row>
+        </v-col>
+      </v-container>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+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";
+
+/**
+ * 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,
+});
+
+const residenceAreasRepo = useRepo(ResidenceAreasRepository)
+
+const router = useRouter()
+const { fetchCollection } = useEntityFetch()
+const i18n = useI18n()
+
+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(() => {
+  return residenceAreasRepo.getResidenceAreas()
+})
+
+const goToEditPage = (id: number) => {
+  navigateTo(UrlUtils.join('/parameters/residence_areas', id))
+}
+
+const goToCreatePage = () => {
+  navigateTo(`/parameters/residence_areas/new`)
+}
+</script>
+
+<style scoped lang="scss">
+// TODO: voir à factoriser ces styles, ptêt en faisant un component de ces boutons?
+:deep(.cycle-edit-icon .v-icon) {
+  color: rgb(var(--v-theme-primary));
+  font-size: 18px;
+}
+</style>

+ 57 - 0
pages/parameters/sms.vue

@@ -0,0 +1,57 @@
+<template>
+  <NuxtLayout name="parameters">
+    <div>
+      <UiForm :model="Parameters" :entity="parameters">
+        <v-row>
+          <v-col cols="12">
+            <UiInputText
+              v-model="parameters.smsSenderName"
+              field="smsSenderName"
+            />
+          </v-col>
+          <v-col cols="12">
+            <UiInputText
+              v-model="parameters.usernameSMS"
+              field="usernameSMS"
+              label="Nom d'utilisateur SMS"
+            />
+          </v-col>
+          <v-col cols="12">
+            <UiInputText
+                v-model="parameters.passwordSMS"
+                field="passwordSMS"
+                type="password"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </div>
+  </NuxtLayout>
+</template>
+<script setup lang="ts">
+import Parameters from '~/models/Organization/Parameters'
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useOrganizationProfileStore } from '~/stores/organizationProfile'
+import { AsyncData } from '#app'
+
+/**
+ * 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,
+});
+
+const { fetch } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+if (organizationProfile.parametersId === null) {
+  throw new Error('Missing organization parameters id')
+}
+
+const { data: parameters, pending } = fetch(
+  Parameters,
+  organizationProfile.parametersId
+) as AsyncData<Parameters, Parameters | true>
+</script>

+ 107 - 0
pages/parameters/super_admin.vue

@@ -0,0 +1,107 @@
+<template>
+  <NuxtLayout name="parameters">
+    <div>
+      <v-container>
+        <v-row>
+          <v-col cols="1">
+          </v-col>
+          <v-col cols="4">
+            <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>
+          </v-col>
+          <v-col cols="1" />
+          <v-col cols="6">
+            <v-row>
+
+            </v-row>
+            <v-row v-if="pending">
+              <UiLoadingPanel/>
+            </v-row>
+            <v-row v-else>
+              <UiForm
+                  ref="form"
+                  :model="AdminAccess"
+                  :entity="adminAccess"
+                  class="w-100"
+              >
+                <div class="d-flex flex-row mx-4 my-6">
+                  <span>{{ $t('username') }} :</span> <pre> {{ adminAccess.username }}</pre>
+                </div>
+
+                <UiInputText
+                    field="email"
+                    v-model="adminAccess.email"
+                    :rules="rules()"
+                />
+              </UiForm>
+            </v-row>
+          </v-col>
+        </v-row>
+      </v-container>
+    </div>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+import { useEntityFetch } from '~/composables/data/useEntityFetch'
+import { useAccessProfileStore } from '~/stores/accessProfile'
+import AdminAccess from '~/models/Access/AdminAccess'
+import {useValidationUtils} from "~/composables/utils/useValidationUtils";
+
+/**
+ * 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,
+});
+
+const { fetch } = useEntityFetch()
+
+const accessProfile = useAccessProfileStore()
+if (accessProfile.id === null) {
+  throw new Error('Missing access profile id')
+}
+
+const { data: adminAccess, pending } = fetch(AdminAccess, accessProfile.id)
+
+const i18n = useI18n()
+
+const validationUtils = useValidationUtils()
+
+
+const rules = () => [
+  (email: string | null) =>
+    (email && validationUtils.validEmail(email)) || i18n.t('email_error')
+]
+</script>
+
+<style scoped lang="scss">
+.explanation {
+  display: flex;
+  flex-direction: row;
+  padding: 60px 26px;
+  text-align: justify;
+  color: rgb(var(--v-theme-neutral-strong));
+
+  .v-icon {
+    background-color: rgb(var(--v-theme-primary));
+    font-size: 22px;
+    border-radius: 16px;
+    margin: 3px;
+    padding: 3px;
+    height: 28px;
+    width: 28px;
+  }
+
+  div:first-child {
+    border-right: solid 1px rgb(var(--v-theme-primary));
+  }
+}
+</style>

+ 55 - 45
components/Layout/Parameters/Teaching.vue → pages/parameters/teaching.vue

@@ -1,49 +1,51 @@
 <template>
-  <LayoutContainer>
-    <UiLoadingPanel v-if="pending" />
-    <UiForm
-        v-else
-        :model="Parameters"
-        :entity="parameters"
-    >
-      <v-table>
-        <thead>
-          <tr>
-            <td>{{ $t('originalLabel') }}</td>
-            <td>{{ $t('effectiveLabel') }}</td>
-          </tr>
-        </thead>
-
-        <tbody>
-          <tr v-for="enumItem in cycleEnum">
-            <td>{{ $t(enumItem.value) }}</td>
-            <td class="cycle-editable-cell">
-              {{ orderedCycles[enumItem.value] ? orderedCycles[enumItem.value].label : $t(enumItem.value) }}
-            </td>
-            <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)"
-              />
-            </td>
-          </tr>
-        </tbody>
-      </v-table>
-
-      <v-row>
-        <v-col cols="6">
-          <UiInputCheckbox
-              v-model="parameters.showEducationIsACollectivePractice"
-              field="showEducationIsACollectivePractice"
-              label="allow_to_configure_teachings_with_played_instrument_choice"
-          />
-        </v-col>
-      </v-row>
-    </UiForm>
-  </LayoutContainer>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-table>
+          <thead>
+            <tr>
+              <td>{{ $t('originalLabel') }}</td>
+              <td>{{ $t('effectiveLabel') }}</td>
+            </tr>
+          </thead>
+
+          <tbody>
+            <tr v-for="enumItem in cycleEnum">
+              <td>{{ $t(enumItem.value) }}</td>
+              <td class="cycle-editable-cell">
+                {{ orderedCycles[enumItem.value] ? orderedCycles[enumItem.value].label : $t(enumItem.value) }}
+              </td>
+              <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)"
+                />
+              </td>
+            </tr>
+          </tbody>
+        </v-table>
+
+        <v-row>
+          <v-col cols="6">
+            <UiInputCheckbox
+                v-model="parameters.showEducationIsACollectivePractice"
+                field="showEducationIsACollectivePractice"
+                label="allow_to_configure_teachings_with_played_instrument_choice"
+            />
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
 </template>
 
 <script setup lang="ts">
@@ -55,6 +57,14 @@ import {useOrganizationProfileStore} from "~/stores/organizationProfile";
 import {AnyJson} from "~/types/data";
 import {useEnumFetch} from "~/composables/data/useEnumFetch";
 
+/**
+ * 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,
+});
+
 const organizationProfile = useOrganizationProfileStore()
 
 if (organizationProfile.parametersId === null) {

+ 192 - 0
pages/parameters/website.vue

@@ -0,0 +1,192 @@
+<template>
+  <NuxtLayout name="parameters">
+    <LayoutContainer>
+      <UiLoadingPanel v-if="pending" />
+      <UiForm
+          v-else
+          :model="Parameters"
+          :entity="parameters"
+      >
+        <v-row>
+          <v-col cols="6">
+            <div class="mb-6">
+              <div>{{ $t('your_opentalent_website_address_is')}} : </div>
+              <div class="ma-2 text-primary">
+                <strong>{{ organizationProfile.website }}</strong>
+              </div>
+            </div>
+
+            <div class="mb-6">
+              <div>{{ $t('your_subdomains') }} : </div>
+              <UiLoadingPanel v-if="subdomainsPending" />
+              <div v-else>
+                <v-table class="my-2">
+                  <tbody>
+                    <tr
+                        v-for="subdomain in subdomains.items"
+                        :key="subdomain.id"
+                        :title="subdomain.subdomain"
+                        class="subdomainItem"
+                        @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>
+                      </td>
+
+                    </tr>
+
+                  </tbody>
+
+                </v-table>
+
+                <v-btn
+                    :disabled="!canAddNewSubdomain"
+                    class="my-2"
+                    @click="onAddSubdomainClick"
+                >
+                  {{ $t('record_a_new_subdomain')}}
+                </v-btn>
+              </div>
+            </div>
+          </v-col>
+
+          <v-col cols="6">
+              <!-- les publicationDirectors sont des entités Access -->
+              <UiInputAutocompleteAccesses
+                  v-model="parameters.publicationDirectors"
+                  field="publicationDirectors"
+                  multiple
+                  chips
+              />
+
+            <div class="my-8" v-if="!organizationProfile.isCmf">
+              <v-btn
+                v-if="!parameters.desactivateOpentalentSiteWeb"
+                color="error"
+                @click="showWebsiteDeactivationDialog=true"
+              >
+                {{ $t('deactivateOpentalentSiteWeb') }}
+              </v-btn>
+              <v-btn
+                v-else
+                color="primary"
+                @click="reactivateWebsite"
+              >
+                {{ $t('reactivateOpentalentSiteWeb') }}
+              </v-btn>
+
+              <LazyLayoutDialog :show="showWebsiteDeactivationDialog">
+                <template #dialogTitle>
+                  {{ $t('please_confirm')}}
+                </template>
+                <template #dialogText>
+                  <v-col>
+                    <div>{{ $t('yourOpentalentWebsiteWillBeDeactivatedOnceYouLlHaveSaved')}}.</div>
+                    <span>{{ $t('doYouWantToContinue')}} ?</span>
+                  </v-col>
+                </template>
+                <template #dialogBtn>
+                  <v-btn
+                      class="theme-neutral-soft mr-4"
+                      @click="showWebsiteDeactivationDialog=false"
+                  >
+                    {{ $t('cancel') }}
+                  </v-btn>
+                  <v-btn
+                      class="theme-primary"
+                      @click="showWebsiteDeactivationDialog=false; deactivateWebsite()"
+                  >
+                    {{ $t('yes') }}
+                  </v-btn>
+                </template>
+              </LazyLayoutDialog>
+            </div>
+
+            <div>
+              <UiInputText
+                  v-model="parameters.otherWebsite"
+                  field="otherWebsite"
+              />
+            </div>
+          </v-col>
+        </v-row>
+      </UiForm>
+    </LayoutContainer>
+  </NuxtLayout>
+</template>
+
+<script setup lang="ts">
+import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import Parameters from "~/models/Organization/Parameters";
+import {useEntityFetch} from "~/composables/data/useEntityFetch";
+import {AsyncData} from "#app";
+import Subdomain from "~/models/Organization/Subdomain";
+
+/**
+ * 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,
+});
+
+const i18n = useI18n()
+
+const { fetch, fetchCollection } = useEntityFetch()
+
+const organizationProfile = useOrganizationProfileStore()
+
+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: subdomains, pending: subdomainsPending } = fetchCollection(Subdomain, null, ref({ 'organization' : organizationProfile.id }) )
+
+const canAddNewSubdomain: ComputedRef<boolean> = computed(() => subdomains.value && subdomains.value.items.length < 3)
+
+const goToEditPage = (id: number) => {
+  console.log(parameters.value)
+  navigateTo(`parameters/subdomains/${id}`)
+}
+
+const onAddSubdomainClick = () => {
+  if (!canAddNewSubdomain) {
+    throw new Error('Max number of subdomains reached')
+  }
+  navigateTo('/parameters/subdomains/new')
+}
+
+const showWebsiteDeactivationDialog: Ref<boolean> = ref(false)
+
+
+const deactivateWebsite = () => {
+  parameters.value.desactivateOpentalentSiteWeb = true
+}
+
+const reactivateWebsite = () => {
+  parameters.value.desactivateOpentalentSiteWeb = false
+}
+</script>
+
+<style scoped lang="scss">
+.v-table {
+  background: transparent;
+}
+.subdomainItem {
+  cursor: pointer;
+}
+.subdomainItem:hover {
+  background: rgb(var(--v-theme-neutral));
+}
+.subdomainItem .icon {
+  font-size: 12px;
+}
+
+</style>

Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików