Pārlūkot izejas kodu

complete parameters page layout

Olivier Massot 2 gadi atpakaļ
vecāks
revīzija
4884c56df4

+ 9 - 2
components/Layout/Header.vue

@@ -11,10 +11,14 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
   >
     <template #prepend>
       <v-app-bar-nav-icon
-          v-if="hasMainMenu"
+          v-if="hasMainMenu && layoutStore.name !== 'parameters'"
           :icon="isMainMenuOpened ? 'mdi:mdi-menu-open' : 'mdi:mdi-menu'"
           @click="toggleMainMenu"
       />
+      <v-app-bar-nav-icon
+        v-if="layoutStore.name === 'parameters'"
+        icon="fa fa-gear"
+      />
     </template>
 
     <v-toolbar-title v-if="mdAndUp" v-text="title"/>
@@ -39,7 +43,7 @@ Contient entre autres le nom de l'organisation, l'accès à l'aide et aux préf
 
     <a
         :href="runtimeConfig.supportUrl"
-        class="text-body pa-3 ml-2 theme-secondary text-decoration-none"
+        class="text-body px-3 py-4 ml-2 theme-secondary text-decoration-none"
         target="_blank"
     >
       <span class="d-none d-sm-none d-md-flex">{{ $t('help_access') }}</span>
@@ -55,6 +59,7 @@ import {useMenu} from "~/composables/layout/useMenu";
 import {useAbility} from "@casl/vue";
 import {useDisplay, useTheme} from 'vuetify'
 import {useOrganizationProfileStore} from "~/stores/organizationProfile";
+import {useLayoutStore} from "~/stores/layout";
 
 const organizationProfile = useOrganizationProfileStore()
 const runtimeConfig = useRuntimeConfig()
@@ -81,6 +86,8 @@ const showUniversalButton =
     || ability.can('display', 'message_send_page')
     || ability.can('manage', 'equipments')
 
+const layoutStore = useLayoutStore()
+
 </script>
 
 <style scoped>

+ 2 - 4
components/Layout/Header/HomeBtn.vue

@@ -14,11 +14,9 @@
 <script setup lang="ts">
   import {ref} from "@vue/reactivity";
   import {useDisplay} from "vuetify";
-  import UrlUtils from "~/services/utils/urlUtils";
-
-  const runtimeConfig = useRuntimeConfig()
-  const homeUrl = UrlUtils.join(runtimeConfig.baseUrlAdminLegacy, '#', 'dashboard')
+  import {useHomeUrl} from "~/composables/utils/useHomeUrl";
 
+  const { homeUrl } = useHomeUrl()
   const { mdAndUp } = useDisplay()
 
   const btn = ref(null);

+ 48 - 1
components/Layout/ParametersMenu.vue

@@ -11,15 +11,30 @@
           v-for="(item, i) in menu.children"
           :key="i"
           :title="$t(item.label)"
+          :prepend-icon="item.icon.name"
           :to="item.to">
       </v-list-item>
     </v-list>
+
+    <template v-slot:append>
+      <v-btn
+          :href="homeUrl"
+          prepend-icon="fa fa-right-from-bracket"
+          :flat="true"
+          color="on-neutral-very-soft"
+          class="cancel-btn py-2"
+      >
+        {{ $t('exit') }}
+      </v-btn>
+    </template>
   </v-navigation-drawer>
 </template>
 
 <script setup lang="ts">
   import {useMenu} from "~/composables/layout/useMenu";
   import {useLayoutStore} from "~/stores/layout";
+  import UrlUtils from "~/services/utils/urlUtils";
+  import {useHomeUrl} from "~/composables/utils/useHomeUrl";
 
   const { getMenu, hasMenu } = useMenu()
 
@@ -28,6 +43,8 @@
   const displayMenu = computed(() => {
     return menu !== null && hasMenu('Parameters')
   })
+
+  const { homeUrl } = useHomeUrl()
 </script>
 
 <style scoped lang="scss">
@@ -43,7 +60,7 @@
   }
 
   .v-navigation-drawer {
-    background-color: white;
+    background-color: rgb(var(--v-theme-neutral-very-soft));
     border-right: solid 1px rgb(var(--v-theme-neutral-strong));
   }
 
@@ -65,4 +82,34 @@
     font-weight: 800 !important;
   }
 
+  :deep(.v-list-item-title), :deep(.v-icon)
+  {
+    font-size: 14px;
+    color: rgb(var(--v-theme-on-neutral-very-soft));
+  }
+
+  :deep(.v-list-item__prepend) {
+    margin: 10px 0;
+    margin-right: 10px !important;
+  }
+
+  :deep(.v-list-item .v-icon) {
+    margin-right: 10px;
+  }
+
+  .cancel-btn {
+    height: 42px;
+    color: rgb(var(--v-theme-on-neutral-very-soft));
+    background-color: transparent;
+    width: 100%;
+    border-top: solid 1px rgb(var(--v-theme-on-neutral-very-soft));
+    display: flex;
+    flex-direction: row;
+    justify-content: start;
+  }
+
+  :deep(.cancel-btn .v-btn__prepend) {
+    margin: 0 16px 4px 2px;
+  }
+
 </style>

+ 15 - 2
components/Ui/Form.vue

@@ -17,7 +17,11 @@ de quitter si des données ont été modifiées.
       @update:entity="onFormChange"
     >
       <!-- Top action bar -->
-      <v-container :fluid="true" class="container btnActions">
+      <v-container
+          v-if="actionPosition === 'both' || actionPosition === 'top'"
+          :fluid="true"
+          class="container btnActions"
+      >
         <v-row>
           <v-col cols="12" sm="12">
             <slot name="form.button"/>
@@ -36,7 +40,11 @@ de quitter si des données ont été modifiées.
       <slot v-bind="{model, entity}"/>
 
       <!-- Bottom action bar -->
-      <v-container :fluid="true" class="container btnActions">
+      <v-container
+          v-if="actionPosition === 'both' || actionPosition === 'bottom'"
+          :fluid="true"
+          class="container btnActions mt-6"
+      >
         <v-row>
           <v-col cols="12" sm="12">
             <slot name="form.button"/>
@@ -147,6 +155,11 @@ const props = defineProps({
     type: Boolean,
     required: false,
     default: false
+  },
+  actionPosition: {
+    type: String as PropType<'top' | 'bottom' | 'both'>,
+    required: false,
+    default: 'both'
   }
 })
 

+ 9 - 0
components/Ui/Input/Autocomplete.vue

@@ -27,6 +27,7 @@ Liste déroulante avec autocompletion, à placer dans un composant `UiForm`
         :chips="chips"
         :hide-no-data="hideNoData"
         :no-data-text="isLoading ? $t('please_wait') : $t('no_result_matching_your_request')"
+        :variant="variant"
         @update:model-value="onUpdate"
         @update:search="emit('update:search', $event)"
     >
@@ -208,6 +209,14 @@ const props = defineProps({
     type: String,
     required: false,
     default: null
+  },
+  /**
+   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
+   */
+  variant: {
+    type: String,
+    required: false,
+    default: 'filled'
   }
 })
 

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

@@ -19,6 +19,7 @@ Champs autocomplete dédié à la recherche des access d'une structure
         :auto-select-first="false"
         prependIcon="fas fa-magnifying-glass"
         :return-object="false"
+        :variant="variant"
         @update:model-value="onUpdateModelValue"
         @update:search="onUpdateSearch"
     />
@@ -101,6 +102,14 @@ const props = defineProps({
   clearSearchAfterUpdate: {
     type: Boolean,
     default: false
+  },
+  /**
+   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
+   */
+  variant: {
+    type: String,
+    required: false,
+    default: 'filled'
   }
 })
 

+ 9 - 0
components/Ui/Input/AutocompleteWithEnum.vue

@@ -8,6 +8,7 @@
       :return-object="false"
       item-title="label"
       item-value="value"
+      :variant="variant"
       @update:model-value="$emit('update:model-value', $event)"
   />
 </template>
@@ -39,6 +40,14 @@ const props = defineProps({
     type: String,
     required: false,
     default: null
+  },
+  /**
+   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
+   */
+  variant: {
+    type: String,
+    required: false,
+    default: 'filled'
   }
 })
 

+ 9 - 13
components/Ui/Input/Checkbox.vue

@@ -5,19 +5,15 @@ Case à cocher, à placer dans un composant `UiForm`
 -->
 
 <template>
-  <v-container
-    class="px-0"
-    :fluid="true"
-  >
-    <v-checkbox
-      :model-value="modelValue"
-      :label="$t(fieldLabel)"
-      :disabled="readonly"
-      :error="error || !!fieldViolations"
-      :error-messages="errorMessage || fieldViolations ? $t(fieldViolations) : ''"
-      @update:model-value="onUpdate"
-    />
-  </v-container>
+  <v-checkbox
+    :model-value="modelValue"
+    :label="$t(fieldLabel)"
+    :disabled="readonly"
+    :error="error || !!fieldViolations"
+    :error-messages="errorMessage || fieldViolations ? $t(fieldViolations) : ''"
+    class="py-1"
+    @update:model-value="onUpdate"
+  />
 </template>
 
 <script setup lang="ts">

+ 9 - 0
components/Ui/Input/Number.vue

@@ -10,6 +10,7 @@ An input for numeric values
       hide-details
       :density="density"
       type="number"
+      :variant="variant"
       @update:modelValue="modelValue = keepInRange(cast($event)); emitUpdate()"
   />
 </template>
@@ -62,6 +63,14 @@ const props = defineProps({
     type: String as PropType<Density>,
     required: false,
     default: 'default'
+  },
+  /**
+   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
+   */
+  variant: {
+    type: String as PropType<"filled" | "outlined" | "plain" | "underlined" | "solo" | "solo-inverted" | "solo-filled" | undefined>,
+    required: false,
+    default: 'filled'
   }
 })
 

+ 9 - 0
components/Ui/Input/Text.vue

@@ -14,6 +14,7 @@ Champs de saisie de texte, à placer dans un composant `UiForm`
     :error="error || !!fieldViolations"
     :error-messages="errorMessage || (fieldViolations ? $t(fieldViolations) : '')"
     :append-icon="type === 'password' ? (show ? 'mdi-eye' : 'mdi-eye-off') : ''"
+    :variant="variant"
     @click:append="show = !show"
     @update:model-value="onUpdate($event)"
     @change="onChange($event)"
@@ -108,6 +109,14 @@ const props = defineProps({
     type: [Array, Boolean],
     required: false,
     default: false
+  },
+  /**
+   * @see https://vuetifyjs.com/en/api/v-autocomplete/#props-variant
+   */
+  variant: {
+    type: String,
+    required: false,
+    default: 'filled'
   }
 })
 

+ 11 - 0
composables/utils/useHomeUrl.ts

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

+ 1 - 1
config/theme.ts

@@ -76,7 +76,7 @@ export const lightTheme: Theme = {
         'neutral-soft': '#f2f2f2',
         'on-neutral-soft': '#333333',
 
-        'neutral-very-soft': '#ecf0f5',
+        'neutral-very-soft': '#ffffff',
         'on-neutral-very-soft': '#333333',
 
         // Content

+ 2 - 1
lang/fr.json

@@ -662,5 +662,6 @@
   "create_a_new_residence_area": "Créer une nouvelle zone de résidence",
   "residence_areas_breadcrumbs": "Zones de résidence",
   "super_admin_explanation_text": "Le compte super-admin possède tous les droits de gestion sur votre logiciel. On l’utilise entre autre pour la gestion de votre site internet, pour créer les comptes des membres de votre structure à la première connexion au logiciel, ou dans des situations de dépannage.",
-  "cycles_breadcrumbs": "Enseignements"
+  "cycles_breadcrumbs": "Enseignements",
+  "exit": "Quitter"
 }

+ 3 - 0
layouts/default.vue

@@ -27,5 +27,8 @@
 </template>
 
 <script setup lang="ts">
+import {useLayoutStore} from "~/stores/layout";
 
+const layoutStore = useLayoutStore()
+layoutStore.name = 'default'
 </script>

+ 4 - 0
layouts/error.vue

@@ -8,6 +8,10 @@
 
 <script setup lang="ts">
   import UrlUtils from "~/services/utils/urlUtils";
+  import {useLayoutStore} from "~/stores/layout";
+
+  const layoutStore = useLayoutStore()
+  layoutStore.name = 'error'
 
   const props = defineProps({
     error: {

+ 21 - 1
layouts/parameters.vue

@@ -17,7 +17,9 @@
         <LayoutAlertBar class="mt-1" />
 
         <!-- Page will be rendered here-->
-        <slot />
+        <v-card class="parameters-page-card">
+          <slot />
+        </v-card>
       </v-main>
 
       <LazyLayoutAlertContainer />
@@ -25,3 +27,21 @@
     </v-app>
   </div>
 </template>
+
+<script setup lang="ts">
+
+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: 18px 24px;
+  }
+</style>

+ 7 - 2
pages/parameters/attendances.vue

@@ -1,9 +1,14 @@
 <template>
   <LayoutContainer>
     <UiLoadingPanel v-if="pending" />
-    <UiForm v-else :model="Parameters" :entity="parameters">
+    <UiForm
+        v-else
+        :model="Parameters"
+        :entity="parameters"
+        action-position="bottom"
+    >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <UiInputCheckbox
             v-model="parameters.sendAttendanceEmail"
             field="sendAttendanceEmail"

+ 2 - 3
pages/parameters/bulletin.vue

@@ -6,9 +6,10 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <UiInputCheckbox
               v-model="parameters.bulletinWithTeacher"
               field="bulletinWithTeacher"
@@ -33,9 +34,7 @@
               v-model="parameters.bulletinEditWithoutEvaluation"
               field="bulletinEditWithoutEvaluation"
           />
-        </v-col>
 
-        <v-col cols="6">
           <UiInputCheckbox
               v-model="parameters.bulletinPrintAddress"
               field="bulletinPrintAddress"

+ 6 - 3
pages/parameters/education_notation.vue

@@ -5,14 +5,16 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <UiInputCheckbox
               v-model="parameters.periodValidation"
               field="periodValidation"
               label="define_validation_periods_for_teachers"
           />
+
           <UiInputCheckbox
               v-model="parameters.editCriteriaNotationByAdminOnly"
               field="editCriteriaNotationByAdminOnly"
@@ -24,10 +26,9 @@
               v-model="parameters.advancedEducationNotationType"
               enum-name="advanced_education_notation"
               field="advancedEducationNotationType"
+              variant="underlined"
           />
-        </v-col>
 
-        <v-col cols="6">
           <UiInputCheckbox
               v-model="parameters.requiredValidation"
               field="requiredValidation"
@@ -38,6 +39,7 @@
               v-model="parameters.educationPeriodicity"
               enum-name="education_periodicity"
               field="educationPeriodicity"
+              variant="underlined"
           />
 
           <UiInputNumber
@@ -48,6 +50,7 @@
             :min="1"
             :max="100"
             class="mt-2"
+            variant="underlined"
           />
         </v-col>
       </v-row>

+ 3 - 3
pages/parameters/general_parameters.vue

@@ -5,9 +5,10 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <UiInputDatePicker
               v-if="organizationProfile.isSchool"
               v-model="parameters.financialDate"
@@ -34,10 +35,9 @@
               v-model="parameters.timezone"
               enum-name="timezone"
               field="timezone"
+              variant="underlined"
           />
-        </v-col>
 
-        <v-col cols="6">
           <UiInputDatePicker
               v-if="organizationProfile.isSchool"
               v-model="parameters.musicalDate"

+ 2 - 3
pages/parameters/intranet.vue

@@ -5,9 +5,10 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <UiInputCheckbox
               v-model="parameters.generateAttendanceReport"
               field="generateAttendanceReport"
@@ -25,9 +26,7 @@
               field="allowMembersToChangeGivenNameAndName"
               label="allow_members_to_change_their_names_and_firstnames"
           />
-        </v-col>
 
-        <v-col cols="6">
           <UiInputCheckbox
               v-model="parameters.createCourse"
               field="createCourse"

+ 25 - 2
pages/parameters/sms.vue

@@ -1,6 +1,10 @@
 <template>
   <div>
-    <UiForm :model="Parameters" :entity="parameters">
+    <UiForm
+        :model="Parameters"
+        :entity="parameters"
+        action-position="bottom"
+    >
       <v-row>
         <v-col cols="12">
           <UiInputText
@@ -19,7 +23,7 @@
           <UiInputText
               v-model="parameters.passwordSMS"
               field="passwordSMS"
-              type="password"
+              class="password"
           />
         </v-col>
       </v-row>
@@ -45,3 +49,22 @@ const { data: parameters, pending } = fetch(
   organizationProfile.parametersId
 ) as AsyncData<Parameters, Parameters | true>
 </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
+*/
+@font-face {
+  font-family: 'password';
+  font-style: normal;
+  font-weight: 400;
+  font-display: block;
+  src: url(https://jsbin-user-assets.s3.amazonaws.com/rafaelcastrocouto/password.ttf);
+}
+:deep(.password input) {
+  font-family: 'password';
+}
+
+</style>

+ 1 - 0
pages/parameters/super_admin.vue

@@ -28,6 +28,7 @@
                 :model="AdminAccess"
                 :entity="adminAccess"
                 class="w-100"
+                action-position="bottom"
             >
               <div class="d-flex flex-row mx-4 my-6">
                 <span>{{ $t('username') }} :</span> <pre> {{ adminAccess.username }}</pre>

+ 1 - 0
pages/parameters/teaching.vue

@@ -5,6 +5,7 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-table>
         <thead>

+ 4 - 3
pages/parameters/website.vue

@@ -5,9 +5,10 @@
         v-else
         :model="Parameters"
         :entity="parameters"
+        action-position="bottom"
     >
       <v-row>
-        <v-col cols="6">
+        <v-col cols="12">
           <div class="mb-6">
             <div>{{ $t('your_opentalent_website_address_is')}} : </div>
             <div class="ma-2 text-primary">
@@ -52,15 +53,14 @@
               </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
+                variant="underlined"
             />
 
           <div class="my-8" v-if="!organizationProfile.isCmf">
@@ -110,6 +110,7 @@
             <UiInputText
                 v-model="parameters.otherWebsite"
                 field="otherWebsite"
+                variant="underlined"
             />
           </div>
         </v-col>

+ 10 - 10
services/layout/menuBuilder/parametersMenuBuilder.ts

@@ -19,23 +19,23 @@ export default class ParametersMenuBuilder extends AbstractMenuBuilder {
     }
 
     children.push(this.createItem('general_parameters', {name: 'fas fa-gears'},`/parameters/general_parameters`))
-    children.push(this.createItem('website', {name: 'fas fa-gears'},`/parameters/website`))
+    children.push(this.createItem('website', {name: 'fas fa-globe-americas'},`/parameters/website`))
 
     if (this.organizationProfile.isSchool) {
-      children.push(this.createItem('teaching', {name: 'fas fa-gears'}, `/parameters/teaching`))
-      children.push(this.createItem('intranet_access', {name: 'fas fa-gears'}, `/parameters/intranet`))
-      children.push(this.createItem('educationNotations', {name: 'fas fa-gears'}, `/parameters/education_notation`))
-      children.push(this.createItem('bulletin', {name: 'fas fa-gears'}, `/parameters/bulletin`))
-      children.push(this.createItem('educationTimings', {name: 'fas fa-gears'}, `/parameters/education_timings`))
-      children.push(this.createItem('attendances', {name: 'fas fa-gears'}, `/parameters/attendances`))
-      children.push(this.createItem('residenceAreas', {name: 'fas fa-gears'}, `/parameters/residence_areas`))
+      children.push(this.createItem('teaching', {name: 'fas fa-school'}, `/parameters/teaching`))
+      children.push(this.createItem('intranet_access', {name: 'fas fa-arrows-down-to-people'}, `/parameters/intranet`))
+      children.push(this.createItem('educationNotations', {name: 'fas fa-graduation-cap'}, `/parameters/education_notation`))
+      children.push(this.createItem('bulletin', {name: 'fas fa-file-lines'}, `/parameters/bulletin`))
+      children.push(this.createItem('educationTimings', {name: 'fas fa-clock'}, `/parameters/education_timings`))
+      children.push(this.createItem('attendances', {name: 'fas fa-user-times'}, `/parameters/attendances`))
+      children.push(this.createItem('residenceAreas', {name: 'fas fa-location-dot'}, `/parameters/residence_areas`))
     }
 
     if (this.organizationProfile.hasModule('Sms')) {
-      children.push(this.createItem('sms_option', {name: 'fas fa-gears'}, `/parameters/sms`))
+      children.push(this.createItem('sms_option', {name: 'fas fa-mobile'}, `/parameters/sms`))
     }
 
-    children.push(this.createItem('super_admin', {name: 'fas fa-gears'},`/parameters/super_admin`))
+    children.push(this.createItem('super_admin', {name: 'fas fa-user-gear'},`/parameters/super_admin`))
 
     // Voir nouveau découpage?
     // if (this.ability.can('display', 'parameters_page')) {

+ 6 - 0
stores/layout.ts

@@ -3,10 +3,16 @@ import {Ref, ref} from "@vue/reactivity";
 import {MenuGroup, MenuItem} from "~/types/layout";
 
 export const useLayoutStore = defineStore('layout', () => {
+    /**
+     * Le nom du layout actif
+     */
+    const name: Ref<string> = ref('default')
+
     const menus: Ref<Record<string, MenuGroup | MenuItem>> = ref({})
     const menusOpened: Ref<Record<string, boolean>> = ref({})
 
     return {
+        name,
         menus,
         menusOpened,
     }