Bläddra i källkod

implement the confirmation dialog before quitting a form

Olivier Massot 2 år sedan
förälder
incheckning
2a29a51a25
3 ändrade filer med 88 tillägg och 29 borttagningar
  1. 4 4
      components/Layout/Dialog.vue
  2. 81 22
      components/Ui/Form.vue
  3. 3 3
      lang/fr.json

+ 4 - 4
components/Layout/Dialog.vue

@@ -83,10 +83,6 @@ const _show = computed(() => props.show) as boolean
     }
   }
 
-  .dialog-container {
-    overflow-x: scroll;
-  }
-
   .dialog-text-container {
     max-height: 70vh;
     overflow: auto;
@@ -103,4 +99,8 @@ const _show = computed(() => props.show) as boolean
       background: rgb(var(--v-theme-warning, #f39c12));
     }
   }
+
+  :deep(.v-card-actions) {
+    min-height: 62px;
+  }
 </style>

+ 81 - 22
components/Ui/Form.vue

@@ -61,6 +61,7 @@ de quitter si des données ont été modifiées.
     <!-- Confirmation dialog -->
     <LazyLayoutDialog
       :show="isConfirmationDialogShowing"
+      :maxWidth="1000"
     >
       <template #dialogText>
         <v-card-title class="text-h5 theme-neutral">
@@ -68,19 +69,24 @@ de quitter si des données ont été modifiées.
         </v-card-title>
         <v-card-text>
           <br>
-          <p>{{ $t('quit_without_saving_warning') }}</p>
+          <p>{{ $t('quit_without_saving_warning') }}.</p>
         </v-card-text>
       </template>
+
       <template #dialogBtn>
-        <v-btn class="mr-4 submitBtn theme-primary" @click="closeConfirmationDialog">
-          {{ $t('back_to_form') }}
-        </v-btn>
-        <v-btn class="mr-4 submitBtn theme-primary" @click="saveAndQuit">
-          {{ $t('save_and_quit') }}
-        </v-btn>
-        <v-btn class="mr-4 submitBtn theme-danger" @click="cancel">
-          {{ $t('quit_form') }}
-        </v-btn>
+        <div class="confirmation-dlg-actions">
+          <v-btn class="theme-primary" @click="closeConfirmationDialog">
+            {{ $t('cancel') }}
+          </v-btn>
+
+          <v-btn class="theme-primary" @click="saveAndQuit">
+            {{ $t('save_and_quit') }}
+          </v-btn>
+
+          <v-btn class="theme-danger" @click="cancel">
+            {{ $t('quit_with_no_saving') }}
+          </v-btn>
+        </div>
       </template>
     </LazyLayoutDialog>
 
@@ -92,7 +98,7 @@ import {computed, ref} from "@vue/reactivity";
 import type {ComputedRef, Ref} from "@vue/reactivity";
 import {FORM_FUNCTION, SUBMIT_TYPE, TYPE_ALERT} from "~/types/enum/enums";
 import { useFormStore } from "~/stores/form";
-import type {Route, RouteLocationRaw} from "@intlify/vue-router-bridge";
+import type {Route, RouteLocationNormalized, RouteLocationRaw} from "@intlify/vue-router-bridge";
 import {useEntityManager} from "~/composables/data/useEntityManager";
 import ApiModel from "~/models/ApiModel";
 import {usePageStore} from "~/stores/page";
@@ -170,6 +176,7 @@ const i18n = useI18n()
 const router = useRouter()
 const { em } = useEntityManager()
 const { refreshProfile } = useRefreshProfile()
+const route = useRoute();
 
 // Le formulaire est-il valide
 const isValid: Ref<boolean> = ref(true)
@@ -180,21 +187,31 @@ const errors: Ref<Array<string>> = ref([])
 // Référence au component v-form
 const form: Ref = ref(null)
 
+const formStore = useFormStore()
+
 // Le formulaire est-il en lecture seule
 const readonly: ComputedRef<boolean> = computed(() => {
-  return useFormStore().readonly
+  return formStore.readonly
 })
 
+/**
+ * Si l'utilisateur veut quitter le formulaire sans enregistrer ses modifications,
+ * on affiche la fenêtre de confirmation. En attendant, on garde en mémoire la route qu'il
+ * voulait suivre au cas où il confirmerait.
+ */
+const requestedLeavingRoute: Ref<RouteLocationNormalized | null> = ref(null)
+
 // La fenêtre de confirmation est-elle affichée
 const isConfirmationDialogShowing: ComputedRef<boolean> = computed(() => {
-  return useFormStore().showConfirmToLeave
+  return formStore.showConfirmToLeave
 })
 
 /**
  * Ferme la fenêtre de confirmation
  */
 const closeConfirmationDialog = () => {
-  useFormStore().setShowConfirmToLeave(false)
+  requestedLeavingRoute.value = null
+  formStore.setShowConfirmToLeave(false)
 }
 
 // ### Actions du formulaire
@@ -251,7 +268,7 @@ const submit = async (next: string|null = null) => {
         fields = Object.assign(fields, {[violation['propertyPath']] : violation['message']})
       }
 
-      useFormStore().addViolation(fields)
+      formStore.addViolation(fields)
 
       usePageStore().addAlert(TYPE_ALERT.ALERT, ['invalid_form'])
     } else {
@@ -280,7 +297,7 @@ const saveAndQuit = async () => {
  * @param id
  */
 function onSaveAction(route: Route, id: number){
-  if (useFormStore().formFunction === FORM_FUNCTION.CREATE) {
+  if (formStore.formFunction === FORM_FUNCTION.CREATE) {
     route.path += id
     navigateTo(route)
   }
@@ -293,23 +310,39 @@ function onSaveAction(route: Route, id: number){
  *
  * @param route
  */
-function onSaveAndQuitAction(route: Route){
+function onSaveAndQuitAction(route: Route) {
   navigateTo(route)
 }
 
+/**
+ * Avant de quitter le formulaire, si le formulaire a été modifié, on demande confirmation
+ */
+onBeforeRouteLeave((to: RouteLocationNormalized, from: RouteLocationNormalized) => {
+  if (formStore.dirty === true) {
+    requestedLeavingRoute.value = to
+    formStore.setShowConfirmToLeave(true)
+    return false
+  }
+  return true
+});
+
 /**
  * Quitte le formulaire sans enregistrer
  */
 const cancel = () => {
   setIsDirty(false)
 
-  useFormStore().setShowConfirmToLeave(false)
+  formStore.setShowConfirmToLeave(false)
 
-  em.reset(props.model, props.entity.value)
+  em.reset(props.model, props.entity)
 
   if (router) {
-    // @ts-ignore
-    router.push(useFormStore().goAfterLeave) // TODO: voir si on peut pas passer ça comme prop du component
+    if (requestedLeavingRoute.value !== null) {
+      navigateTo(requestedLeavingRoute.value)
+    } else {
+      //@ts-ignore
+      router.push(formStore.goAfterLeave) // TODO: voir si on peut pas passer ça comme prop du component
+    }
   }
 }
 
@@ -367,7 +400,7 @@ const preventSubmit = (e: any) => {
  * Applique ou retire l'état dirty (modifié) du formulaire
  */
 const setIsDirty = (dirty: boolean) => {
-  useFormStore().setDirty(dirty)
+  formStore.setDirty(dirty)
 }
 
 defineExpose({ validate })
@@ -378,4 +411,30 @@ defineExpose({ validate })
 .btnActions {
   text-align: right;
 }
+
+.confirmation-dlg-actions {
+  display: flex;
+  flex-direction: row;
+}
+
+.confirmation-dlg-actions .v-btn {
+  min-width: 255px;
+  max-width: 255px;
+  margin: 0 8px;
+}
+
+@media (max-width: 960px) {
+  .confirmation-dlg-actions {
+    width: 100%;
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .confirmation-dlg-actions .v-btn {
+    min-width: 80%;
+    max-width: 80%;
+    margin: 6px 0 !important;
+  }
+}
+
 </style>

+ 3 - 3
lang/fr.json

@@ -318,14 +318,14 @@
   "confirm_to_delete": "Vous êtes sur le point de supprimer un élément.\nVoulez-vous continuer?",
   "saveSuccess": "Sauvegarde effectuée",
   "deleteSuccess": "Suppression effectuée",
-  "quit_form": "Quitter le formulaire",
-  "save_and_quit": "Sauvegarder et quitter le formulaire",
+  "quit_with_no_saving": "Quitter sans enregistrer",
+  "save_and_quit": "Sauvegarder et quitter",
   "back_to_form": "Retourner au formulaire",
   "caution": "Attention",
   "updateMap": "Mise à jour de la carte",
   "start_your_research": "Commencer à écrire pour rechercher...",
   "no_coordinate_corresponding": "Aucune coordonnées GPS ne correspondent à votre adresse",
-  "quit_without_saving_warning": "Vous souhaitez quitter ce formulaire sans avoir enregistré",
+  "quit_without_saving_warning": "Vous vous apprêtez à quitter ce formulaire sans avoir enregistré vos modifications",
   "please_wait": "Veuillez patienter",
   "download": "Télécharger",
   "bulletinEditWithoutEvaluationHelp": "Dans le cas où la case n'est pas cochée, s'il n'y a aucune évaluation dans le bulletin, alors le bulletin n'est pas exporté",