Przeglądaj źródła

feat: add indicador de obrigatoriedade nos campos que tem rules.required + add use submit handler com use form update tracker e use scroll nos formularios

Gustavo Mantovani 17 godzin temu
rodzic
commit
9b6961533a
33 zmienionych plików z 174 dodań i 91 usunięć
  1. 3 1
      src/boot/defaultPropsComponents.js
  2. 23 0
      src/components/defaults/DefaultForm.vue
  3. 6 1
      src/components/defaults/DefaultInput.vue
  4. 6 1
      src/components/defaults/DefaultSelect.vue
  5. 12 2
      src/composables/useFormUpdateTracker.js
  6. 70 32
      src/composables/useScroll.js
  7. 2 2
      src/pages/apis/ApisPage.vue
  8. 2 2
      src/pages/classes/components/AddEditClassDialog.vue
  9. 2 2
      src/pages/financial/AccountsPayablePage.vue
  10. 2 2
      src/pages/financial/AccountsReceivablePage.vue
  11. 2 2
      src/pages/financial/AsaasIntegrationPage.vue
  12. 2 2
      src/pages/financial/ChartOfAccountsPage.vue
  13. 2 2
      src/pages/financial/components/AddEditTreasuryAccountDialog.vue
  14. 2 2
      src/pages/financial/components/AddTreasuryLaunchDialog.vue
  15. 2 2
      src/pages/forgot-password/EmailDataStep.vue
  16. 2 2
      src/pages/forgot-password/NewPasswordStep.vue
  17. 2 2
      src/pages/kanban/components/AddEditKanbanDialog.vue
  18. 2 2
      src/pages/login/LoginPage.vue
  19. 2 2
      src/pages/packages/components/AddEditPackageDialog.vue
  20. 2 2
      src/pages/permissions/components/AddEditPermissionGroupDialog.vue
  21. 2 2
      src/pages/students/components/AddEditStudentDialog.vue
  22. 2 2
      src/pages/students/components/AddStudentMediaDialog.vue
  23. 2 2
      src/pages/students/components/EditStudentMediaDialog.vue
  24. 2 2
      src/pages/students/components/ResponsibleDialog.vue
  25. 2 2
      src/pages/students/tabs/StudentDataTab.vue
  26. 2 2
      src/pages/support/components/AddEditReplyDialog.vue
  27. 2 2
      src/pages/support/components/AddEditTicketDialog.vue
  28. 2 2
      src/pages/unit/components/AddEditHistoryDialog.vue
  29. 2 2
      src/pages/unit/components/AddEditPartnerDialog.vue
  30. 2 2
      src/pages/unit/components/AddMediaDialog.vue
  31. 2 2
      src/pages/unit/tabs/UnitDataTab.vue
  32. 2 2
      src/pages/users/UserActionPage.vue
  33. 2 2
      src/pages/users/components/AddEditUserDialog.vue

+ 3 - 1
src/boot/defaultPropsComponents.js

@@ -1,5 +1,6 @@
 import { QDialog, QInput, QSelect, QScrollArea, QCard } from "quasar";
 import { QDialog, QInput, QSelect, QScrollArea, QCard } from "quasar";
 import { defineBoot } from "#q-app/wrappers";
 import { defineBoot } from "#q-app/wrappers";
+import DefaultForm from "src/components/defaults/DefaultForm.vue";
 
 
 /**
 /**
  * Set some default properties on a component
  * Set some default properties on a component
@@ -14,7 +15,8 @@ const SetComponentDefaults = (component, defaults) => {
   });
   });
 };
 };
 
 
-export default defineBoot(() => {
+export default defineBoot(({ app }) => {
+  app.component("DefaultForm", DefaultForm);
   SetComponentDefaults(QDialog, {
   SetComponentDefaults(QDialog, {
     transitionShow: "slide-up",
     transitionShow: "slide-up",
     transitionHide: "slide-down",
     transitionHide: "slide-down",

+ 23 - 0
src/components/defaults/DefaultForm.vue

@@ -0,0 +1,23 @@
+<template>
+  <DefaultForm ref="formRef" v-bind="$attrs" @validation-error="onValidationError">
+    <slot />
+  </DefaultForm>
+</template>
+
+<script setup>
+import { useTemplateRef } from "vue";
+import { useFormUpdateTracker } from "src/composables/useFormUpdateTracker";
+
+defineOptions({ inheritAttrs: false });
+
+const formRef = useTemplateRef("formRef");
+const { onValidationError } = useFormUpdateTracker({});
+
+const validate = (...args) => formRef.value?.validate(...args);
+const resetValidation = (...args) => formRef.value?.resetValidation(...args);
+const getValidationComponents = (...args) =>
+  formRef.value?.getValidationComponents(...args);
+const submit = (...args) => formRef.value?.submit(...args);
+
+defineExpose({ validate, resetValidation, getValidationComponents, submit });
+</script>

+ 6 - 1
src/components/defaults/DefaultInput.vue

@@ -14,7 +14,7 @@
         :rules
         :rules
         :outlined
         :outlined
         :bg-color
         :bg-color
-        :class="inputClass"
+        :class="[inputClass, { 'required-field': required }]"
         :input-class="nativeInputClass"
         :input-class="nativeInputClass"
         @update:model-value="error = null"
         @update:model-value="error = null"
       >
       >
@@ -125,4 +125,9 @@ defineExpose({
 :deep(.q-field--outlined.q-field--rounded .q-field__control) {
 :deep(.q-field--outlined.q-field--rounded .q-field__control) {
   border-radius: 8px;
   border-radius: 8px;
 }
 }
+
+:deep(.required-field .q-field__label::after) {
+  color: var(--q-negative);
+  content: " *";
+}
 </style>
 </style>

+ 6 - 1
src/components/defaults/DefaultSelect.vue

@@ -13,7 +13,7 @@
         :outlined
         :outlined
         hide-bottom-space
         hide-bottom-space
         :bg-color
         :bg-color
-        :class="inputClass"
+        :class="[inputClass, { 'required-field': required }]"
         :popup-content-class="popupContentClass"
         :popup-content-class="popupContentClass"
         hide-dropdown-icon
         hide-dropdown-icon
         @update:model-value="error = null"
         @update:model-value="error = null"
@@ -117,4 +117,9 @@ defineExpose({
 :deep(.q-field--outlined.q-field--rounded .q-field__control) {
 :deep(.q-field--outlined.q-field--rounded .q-field__control) {
   border-radius: 8px;
   border-radius: 8px;
 }
 }
+
+:deep(.required-field .q-field__label::after) {
+  color: var(--q-negative);
+  content: " *";
+}
 </style>
 </style>

+ 12 - 2
src/composables/useFormUpdateTracker.js

@@ -1,8 +1,10 @@
-import { reactive, computed, toRaw, isReactive, watch } from "vue";
+import { reactive, computed, toRaw, isReactive, nextTick, watch } from "vue";
 import isEqual from "fast-deep-equal";
 import isEqual from "fast-deep-equal";
+import { useScroll } from "src/composables/useScroll";
 
 
-export const useFormUpdateTracker = (initialFormValue) => {
+export const useFormUpdateTracker = (initialFormValue, options = {}) => {
   const form = reactive(deepClone(initialFormValue));
   const form = reactive(deepClone(initialFormValue));
+  const { scrollToComponent } = useScroll();
   let originalForm = deepClone(initialFormValue);
   let originalForm = deepClone(initialFormValue);
   const updatedFields = reactive({});
   const updatedFields = reactive({});
 
 
@@ -50,6 +52,13 @@ export const useFormUpdateTracker = (initialFormValue) => {
     return formData;
     return formData;
   };
   };
 
 
+  const onValidationError = async (invalidComponent) => {
+    if (!invalidComponent) return;
+    await nextTick();
+    invalidComponent.focus?.();
+    scrollToComponent(invalidComponent, options.containerRef);
+  };
+
   return {
   return {
     form,
     form,
     getUpdatedFields,
     getUpdatedFields,
@@ -58,6 +67,7 @@ export const useFormUpdateTracker = (initialFormValue) => {
     setUpdateFormAsOriginal,
     setUpdateFormAsOriginal,
     getFormAsFormData,
     getFormAsFormData,
     getUpdatedFieldsAsFormData,
     getUpdatedFieldsAsFormData,
+    onValidationError,
   };
   };
 };
 };
 
 

+ 70 - 32
src/composables/useScroll.js

@@ -1,6 +1,28 @@
-import { unref } from "vue";
+import { nextTick, unref } from "vue";
 
 
 export const useScroll = () => {
 export const useScroll = () => {
+  const getElement = (value) => {
+    const instance = unref(value);
+    return instance?.$el ?? instance ?? null;
+  };
+
+  const getScrollableParent = (targetElement) => {
+    let parent = targetElement?.parentElement;
+
+    while (parent) {
+      const { overflowY } = window.getComputedStyle(parent);
+      if (
+        ["auto", "scroll"].includes(overflowY) &&
+        parent.scrollHeight > parent.clientHeight
+      ) {
+        return parent;
+      }
+      parent = parent.parentElement;
+    }
+
+    return document.scrollingElement || document.documentElement;
+  };
+
   /**
   /**
    * Scrolls to a target element within a scroll container.
    * Scrolls to a target element within a scroll container.
    * The function is agnostic to whether the container is a QScrollArea or a native DOM element.
    * The function is agnostic to whether the container is a QScrollArea or a native DOM element.
@@ -17,10 +39,9 @@ export const useScroll = () => {
 
 
     const targetInstance = unref(targetRef);
     const targetInstance = unref(targetRef);
     const containerInstance = unref(containerRef);
     const containerInstance = unref(containerRef);
-
-    const targetElement = targetInstance?.$el ?? targetInstance;
-
-    const containerElement = containerInstance?.$el ?? containerInstance;
+    const targetElement = getElement(targetInstance);
+    const containerElement =
+      getElement(containerInstance) || getScrollableParent(targetElement);
 
 
     if (!targetElement || !containerElement) {
     if (!targetElement || !containerElement) {
       console.warn("useScroll: Target or container element not found");
       console.warn("useScroll: Target or container element not found");
@@ -28,30 +49,12 @@ export const useScroll = () => {
     }
     }
 
 
     try {
     try {
-      let currentElement = targetElement;
-      let offsetTop = 0;
-
-      while (currentElement && currentElement !== containerElement) {
-        offsetTop += currentElement.offsetTop;
-        if (
-          containerElement.contains(currentElement.offsetParent) ||
-          containerElement === document.body
-        ) {
-          currentElement = currentElement.offsetParent;
-        } else {
-          currentElement = null;
-        }
-      }
-
-      if (
-        containerElement !== document.body &&
-        !containerElement.contains(targetElement)
-      ) {
-        console.warn("useScroll: Target is not a child of the container");
-        return false;
-      }
-
-      const targetPosition = Math.max(0, offsetTop - offset);
+      const targetRect = targetElement.getBoundingClientRect();
+      const containerRect = containerElement.getBoundingClientRect();
+      const targetPosition = Math.max(
+        0,
+        targetRect.top - containerRect.top + containerElement.scrollTop - offset,
+      );
 
 
       if (typeof containerInstance.setScrollPosition === "function") {
       if (typeof containerInstance.setScrollPosition === "function") {
         containerInstance.setScrollPosition(
         containerInstance.setScrollPosition(
@@ -60,9 +63,7 @@ export const useScroll = () => {
           duration,
           duration,
         );
         );
       } else {
       } else {
-        const scrollable =
-          containerElement === document.body ? window : containerElement;
-        scrollable.scrollTo({
+        containerElement.scrollTo({
           top: targetPosition,
           top: targetPosition,
           behavior: "smooth",
           behavior: "smooth",
         });
         });
@@ -75,7 +76,44 @@ export const useScroll = () => {
     }
     }
   };
   };
 
 
+  const scrollToFirstError = async (formRef, containerRef, options = {}) => {
+    await nextTick();
+    const forms = unref(formRef);
+    const formList = Array.isArray(forms) ? forms : [forms];
+
+    for (const form of formList) {
+      const components = form?.getValidationComponents?.() ?? [];
+      const firstError = components.find((component) => component.hasError);
+
+      if (firstError) {
+        firstError.focus?.();
+        const scrollFn = options.scrollFn || scrollToComponent;
+        scrollFn(firstError, unref(containerRef), options);
+        return true;
+      }
+    }
+
+    return false;
+  };
+
+  const validateAndScroll = async (formRef, containerRef, options = {}) => {
+    const forms = unref(formRef);
+    const formList = (Array.isArray(forms) ? forms : [forms]).filter(Boolean);
+    const results = await Promise.all(
+      formList.map((form) => form.validate?.(true) ?? true),
+    );
+    const valid = results.every(Boolean);
+
+    if (!valid) {
+      await scrollToFirstError(formRef, containerRef, options);
+    }
+
+    return valid;
+  };
+
   return {
   return {
     scrollToComponent,
     scrollToComponent,
+    scrollToFirstError,
+    validateAndScroll,
   };
   };
 };
 };

+ 2 - 2
src/pages/apis/ApisPage.vue

@@ -22,7 +22,7 @@
           cobrança é gerada pelo Asaas.
           cobrança é gerada pelo Asaas.
         </div>
         </div>
 
 
-        <q-form ref="formRef">
+        <DefaultForm ref="formRef">
           <DefaultPasswordInput
           <DefaultPasswordInput
             v-model="apiKey"
             v-model="apiKey"
             v-model:error="validationErrors.asaas_api_key"
             v-model:error="validationErrors.asaas_api_key"
@@ -54,7 +54,7 @@
               @click="onSave"
               @click="onSave"
             />
             />
           </div>
           </div>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </div>
   </div>

+ 2 - 2
src/pages/classes/components/AddEditClassDialog.vue

@@ -7,7 +7,7 @@
           @close="onDialogCancel"
           @close="onDialogCancel"
         />
         />
 
 
-        <q-form ref="formRef" @submit="onSubmit">
+        <DefaultForm ref="formRef" @submit="onSubmit">
           <q-card-section class="q-pt-sm">
           <q-card-section class="q-pt-sm">
             <div class="row q-col-gutter-sm">
             <div class="row q-col-gutter-sm">
               <!-- Nome da Atividade -->
               <!-- Nome da Atividade -->
@@ -120,7 +120,7 @@
               :loading="saving"
               :loading="saving"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </q-dialog>
   </q-dialog>

+ 2 - 2
src/pages/financial/AccountsPayablePage.vue

@@ -141,7 +141,7 @@
       <q-card style="min-width: 360px; max-width: 460px">
       <q-card style="min-width: 360px; max-width: 460px">
         <DefaultDialogHeader title="Nova conta a pagar" @close="createDialog = false" />
         <DefaultDialogHeader title="Nova conta a pagar" @close="createDialog = false" />
 
 
-        <q-form ref="createFormRef">
+        <DefaultForm ref="createFormRef">
           <q-card-section class="q-gutter-sm">
           <q-card-section class="q-gutter-sm">
             <DefaultInput
             <DefaultInput
               v-model="createForm.history"
               v-model="createForm.history"
@@ -181,7 +181,7 @@
               @click="onCreate"
               @click="onCreate"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </q-dialog>
     </q-dialog>
   </div>
   </div>

+ 2 - 2
src/pages/financial/AccountsReceivablePage.vue

@@ -103,7 +103,7 @@
           @close="createDialog = false"
           @close="createDialog = false"
         />
         />
 
 
-        <q-form ref="createFormRef">
+        <DefaultForm ref="createFormRef">
           <q-card-section class="q-gutter-sm">
           <q-card-section class="q-gutter-sm">
             <DefaultInput
             <DefaultInput
               v-model="createForm.history"
               v-model="createForm.history"
@@ -166,7 +166,7 @@
               @click="onCreate"
               @click="onCreate"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </q-dialog>
     </q-dialog>
 
 

+ 2 - 2
src/pages/financial/AsaasIntegrationPage.vue

@@ -21,7 +21,7 @@
           para controle e baixa manual.
           para controle e baixa manual.
         </div>
         </div>
 
 
-        <q-form ref="formRef" @submit.prevent="onSave">
+        <DefaultForm ref="formRef" @submit.prevent="onSave">
           <DefaultPasswordInput
           <DefaultPasswordInput
             v-model="apiKey"
             v-model="apiKey"
             v-model:error="validationErrors.asaas_api_key"
             v-model:error="validationErrors.asaas_api_key"
@@ -51,7 +51,7 @@
               :loading="loading"
               :loading="loading"
             />
             />
           </div>
           </div>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </div>
   </div>

+ 2 - 2
src/pages/financial/ChartOfAccountsPage.vue

@@ -37,7 +37,7 @@
           @close="dialog = false"
           @close="dialog = false"
         />
         />
 
 
-        <q-form ref="formRef">
+        <DefaultForm ref="formRef">
           <q-card-section class="q-gutter-sm">
           <q-card-section class="q-gutter-sm">
             <q-option-group
             <q-option-group
               v-model="accountKind"
               v-model="accountKind"
@@ -103,7 +103,7 @@
               @click="onSave"
               @click="onSave"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </q-dialog>
     </q-dialog>
   </div>
   </div>

+ 2 - 2
src/pages/financial/components/AddEditTreasuryAccountDialog.vue

@@ -3,7 +3,7 @@
     <q-card class="q-dialog-plugin overflow-hidden treasury-dialog-card">
     <q-card class="q-dialog-plugin overflow-hidden treasury-dialog-card">
       <DefaultDialogHeader :title="title" @close="onDialogCancel" />
       <DefaultDialogHeader :title="title" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="row q-col-gutter-md q-px-lg q-pt-md q-pb-sm">
         <q-card-section class="row q-col-gutter-md q-px-lg q-pt-md q-pb-sm">
           <DefaultInput
           <DefaultInput
             v-model="form.name"
             v-model="form.name"
@@ -46,7 +46,7 @@
           <q-btn flat label="Cancelar" color="grey-7" @click="onDialogCancel" />
           <q-btn flat label="Cancelar" color="grey-7" @click="onDialogCancel" />
           <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
           <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/financial/components/AddTreasuryLaunchDialog.vue

@@ -3,7 +3,7 @@
     <q-card class="q-dialog-plugin overflow-hidden treasury-dialog-card">
     <q-card class="q-dialog-plugin overflow-hidden treasury-dialog-card">
       <DefaultDialogHeader :title="launch ? 'Editar movimentação' : 'Nova movimentação'" @close="onDialogCancel" />
       <DefaultDialogHeader :title="launch ? 'Editar movimentação' : 'Nova movimentação'" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="row q-col-gutter-md q-px-lg q-pt-md q-pb-sm">
         <q-card-section class="row q-col-gutter-md q-px-lg q-pt-md q-pb-sm">
           <DefaultSelect
           <DefaultSelect
             v-model="form.transaction_type"
             v-model="form.transaction_type"
@@ -39,7 +39,7 @@
           <q-btn flat label="Cancelar" color="grey-7" @click="onDialogCancel" />
           <q-btn flat label="Cancelar" color="grey-7" @click="onDialogCancel" />
           <q-btn color="primary-2" label="Salvar" type="submit" :loading="loading" />
           <q-btn color="primary-2" label="Salvar" type="submit" :loading="loading" />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/forgot-password/EmailDataStep.vue

@@ -18,7 +18,7 @@
       Informe o seu e-mail para redefinir sua senha
       Informe o seu e-mail para redefinir sua senha
     </div>
     </div>
 
 
-    <q-form class="full-width q-px-lg column q-gutter-y-lg q-mt-xs" @submit.prevent="emit('submit', email)">
+    <DefaultForm class="full-width q-px-lg column q-gutter-y-lg q-mt-xs" @submit.prevent="emit('submit', email)">
       <DefaultInput
       <DefaultInput
         v-model="email"
         v-model="email"
         type="email"
         type="email"
@@ -45,7 +45,7 @@
           </template>
           </template>
         </q-btn>
         </q-btn>
       </div>
       </div>
-    </q-form>
+    </DefaultForm>
   </div>
   </div>
 </template>
 </template>
 
 

+ 2 - 2
src/pages/forgot-password/NewPasswordStep.vue

@@ -18,7 +18,7 @@
       <div class="bg-primary" style="height: 1px; flex: 1"></div>
       <div class="bg-primary" style="height: 1px; flex: 1"></div>
     </div>
     </div>
 
 
-    <q-form
+    <DefaultForm
       class="full-width q-px-lg column q-gutter-y-md q-mt-md"
       class="full-width q-px-lg column q-gutter-y-md q-mt-md"
       @submit.prevent="emit('submit', { password, passwordConfirmation })"
       @submit.prevent="emit('submit', { password, passwordConfirmation })"
     >
     >
@@ -49,7 +49,7 @@
           <q-spinner />
           <q-spinner />
         </template>
         </template>
       </q-btn>
       </q-btn>
-    </q-form>
+    </DefaultForm>
   </div>
   </div>
 </template>
 </template>
 
 

+ 2 - 2
src/pages/kanban/components/AddEditKanbanDialog.vue

@@ -7,7 +7,7 @@
           @close="onDialogCancel"
           @close="onDialogCancel"
         />
         />
 
 
-        <q-form
+        <DefaultForm
           ref="formRef"
           ref="formRef"
           style="flex: 1; display: flex; flex-direction: column; overflow: hidden"
           style="flex: 1; display: flex; flex-direction: column; overflow: hidden"
           @submit="onOKClick"
           @submit="onOKClick"
@@ -190,7 +190,7 @@
             <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
             <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
             <q-btn v-if="canSave" color="primary" label="Salvar" type="submit" :loading="loading" />
             <q-btn v-if="canSave" color="primary" label="Salvar" type="submit" :loading="loading" />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </q-dialog>
   </q-dialog>

+ 2 - 2
src/pages/login/LoginPage.vue

@@ -30,7 +30,7 @@
         </div>
         </div>
       </div>
       </div>
 
 
-      <q-form
+      <DefaultForm
         ref="formRef"
         ref="formRef"
         class="full-width q-px-lg column q-gutter-y-md q-mt-xs"
         class="full-width q-px-lg column q-gutter-y-md q-mt-xs"
         autocorrect="off"
         autocorrect="off"
@@ -79,7 +79,7 @@
             </template>
             </template>
           </q-btn>
           </q-btn>
         </div>
         </div>
-      </q-form>
+      </DefaultForm>
     </div>
     </div>
   </q-page>
   </q-page>
 </template>
 </template>

+ 2 - 2
src/pages/packages/components/AddEditPackageDialog.vue

@@ -7,7 +7,7 @@
           @close="onDialogCancel"
           @close="onDialogCancel"
         />
         />
 
 
-        <q-form ref="formRef" @submit="onOKClick">
+        <DefaultForm ref="formRef" @submit="onOKClick">
           <q-card-section class="q-pt-sm">
           <q-card-section class="q-pt-sm">
             <div class="row q-col-gutter-sm">
             <div class="row q-col-gutter-sm">
               <DefaultInput
               <DefaultInput
@@ -179,7 +179,7 @@
               :loading="loading"
               :loading="loading"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </q-dialog>
   </q-dialog>

+ 2 - 2
src/pages/permissions/components/AddEditPermissionGroupDialog.vue

@@ -2,7 +2,7 @@
   <q-dialog ref="dialogRef" @hide="onDialogHide">
   <q-dialog ref="dialogRef" @hide="onDialogHide">
     <q-card style="min-width: 440px; max-width: 90vw">
     <q-card style="min-width: 440px; max-width: 90vw">
       <DefaultDialogHeader :title="isEdit ? 'Editar Grupo de Permissões' : 'Novo Grupo de Permissões'" @close="onDialogCancel" />
       <DefaultDialogHeader :title="isEdit ? 'Editar Grupo de Permissões' : 'Novo Grupo de Permissões'" @close="onDialogCancel" />
-      <q-form ref="formRef" @submit="onSave">
+      <DefaultForm ref="formRef" @submit="onSave">
         <q-card-section class="column q-gutter-md">
         <q-card-section class="column q-gutter-md">
           <DefaultInput v-model="form.label" label="Nome" autofocus :rules="[inputRules.required]" @update:model-value="onLabelChange" />
           <DefaultInput v-model="form.label" label="Nome" autofocus :rules="[inputRules.required]" @update:model-value="onLabelChange" />
           <DefaultInput v-model="form.slug" label="Código" hint="Identificador único. Ex.: FINANCEIRO" :rules="[inputRules.required, slugRule]" @update:model-value="onSlugTouched" />
           <DefaultInput v-model="form.slug" label="Código" hint="Identificador único. Ex.: FINANCEIRO" :rules="[inputRules.required, slugRule]" @update:model-value="onSlugTouched" />
@@ -11,7 +11,7 @@
           <q-btn label="Cancelar" color="negative" outline @click="onDialogCancel" />
           <q-btn label="Cancelar" color="negative" outline @click="onDialogCancel" />
           <q-btn label="Salvar" color="secondary" type="submit" :loading="loading" />
           <q-btn label="Salvar" color="secondary" type="submit" :loading="loading" />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/students/components/AddEditStudentDialog.vue

@@ -6,7 +6,7 @@
     >
     >
       <DefaultDialogHeader title="Cadastrar Aluno" @close="onDialogCancel" />
       <DefaultDialogHeader title="Cadastrar Aluno" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="flex justify-center q-mb-md">
           <div class="flex justify-center q-mb-md">
             <AvatarImageComponent
             <AvatarImageComponent
@@ -146,7 +146,7 @@
             :loading="loading"
             :loading="loading"
           />
           />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/students/components/AddStudentMediaDialog.vue

@@ -3,7 +3,7 @@
     <q-card class="q-dialog-plugin" style="width: 480px; max-width: 95vw">
     <q-card class="q-dialog-plugin" style="width: 480px; max-width: 95vw">
       <DefaultDialogHeader title="Adicionar Mídia" @close="onDialogCancel" />
       <DefaultDialogHeader title="Adicionar Mídia" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onSubmit">
+      <DefaultForm ref="formRef" @submit="onSubmit">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="column q-gutter-sm">
           <div class="column q-gutter-sm">
             <DefaultInput
             <DefaultInput
@@ -30,7 +30,7 @@
           <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
           <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
           <q-btn color="primary" label="Adicionar" type="submit" :loading="loading" />
           <q-btn color="primary" label="Adicionar" type="submit" :loading="loading" />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/students/components/EditStudentMediaDialog.vue

@@ -3,7 +3,7 @@
     <q-card class="q-dialog-plugin" style="width: 480px; max-width: 95vw">
     <q-card class="q-dialog-plugin" style="width: 480px; max-width: 95vw">
       <DefaultDialogHeader title="Editar Mídia" @close="onDialogCancel" />
       <DefaultDialogHeader title="Editar Mídia" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onSubmit">
+      <DefaultForm ref="formRef" @submit="onSubmit">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="column q-gutter-sm">
           <div class="column q-gutter-sm">
             <DefaultInput
             <DefaultInput
@@ -30,7 +30,7 @@
           <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
           <q-btn outline color="primary" label="Cancelar" @click="onDialogCancel" />
           <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
           <q-btn color="primary" label="Salvar" type="submit" :loading="loading" />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/students/components/ResponsibleDialog.vue

@@ -10,7 +10,7 @@
       />
       />
 
 
       <q-card-section class="q-pt-sm" style="height: 65vh; overflow-y: auto">
       <q-card-section class="q-pt-sm" style="height: 65vh; overflow-y: auto">
-        <q-form ref="formRef">
+        <DefaultForm ref="formRef">
           <div class="row q-col-gutter-sm">
           <div class="row q-col-gutter-sm">
             <DefaultInput
             <DefaultInput
               v-model="form.name"
               v-model="form.name"
@@ -125,7 +125,7 @@
               autogrow
               autogrow
             />
             />
           </div>
           </div>
-        </q-form>
+        </DefaultForm>
       </q-card-section>
       </q-card-section>
 
 
       <q-separator />
       <q-separator />

+ 2 - 2
src/pages/students/tabs/StudentDataTab.vue

@@ -1,5 +1,5 @@
 <template>
 <template>
-  <q-form ref="formRef" @submit="onSave">
+  <DefaultForm ref="formRef" @submit="onSave">
     <div class="flex justify-center q-mb-md">
     <div class="flex justify-center q-mb-md">
       <AvatarImageComponent ref="avatarRef" @update:file="onAvatarChange" />
       <AvatarImageComponent ref="avatarRef" @update:file="onAvatarChange" />
     </div>
     </div>
@@ -123,7 +123,7 @@
       />
       />
     </div>
     </div>
 
 
-  </q-form>
+  </DefaultForm>
 </template>
 </template>
 
 
 <script setup>
 <script setup>

+ 2 - 2
src/pages/support/components/AddEditReplyDialog.vue

@@ -7,7 +7,7 @@
           @close="onDialogCancel"
           @close="onDialogCancel"
         />
         />
 
 
-        <q-form ref="formRef" @submit="onOKClick">
+        <DefaultForm ref="formRef" @submit="onOKClick">
           <q-card-section class="q-pt-sm">
           <q-card-section class="q-pt-sm">
             <DefaultInput
             <DefaultInput
               v-model="replyText"
               v-model="replyText"
@@ -31,7 +31,7 @@
               :loading="loading"
               :loading="loading"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </q-dialog>
   </q-dialog>

+ 2 - 2
src/pages/support/components/AddEditTicketDialog.vue

@@ -7,7 +7,7 @@
           @close="onDialogCancel"
           @close="onDialogCancel"
         />
         />
 
 
-        <q-form
+        <DefaultForm
           ref="formRef"
           ref="formRef"
           style="
           style="
             flex: 1;
             flex: 1;
@@ -141,7 +141,7 @@
               :loading="loading"
               :loading="loading"
             />
             />
           </q-card-actions>
           </q-card-actions>
-        </q-form>
+        </DefaultForm>
       </q-card>
       </q-card>
     </div>
     </div>
   </q-dialog>
   </q-dialog>

+ 2 - 2
src/pages/unit/components/AddEditHistoryDialog.vue

@@ -6,7 +6,7 @@
     >
     >
       <DefaultDialogHeader :title="dialogTitle" @close="onDialogCancel" />
       <DefaultDialogHeader :title="dialogTitle" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="column q-gutter-sm">
           <div class="column q-gutter-sm">
             <DefaultInput
             <DefaultInput
@@ -52,7 +52,7 @@
             :loading="loading"
             :loading="loading"
           />
           />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/unit/components/AddEditPartnerDialog.vue

@@ -6,7 +6,7 @@
     >
     >
       <DefaultDialogHeader :title="dialogTitle" @close="onDialogCancel" />
       <DefaultDialogHeader :title="dialogTitle" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="column items-center q-mb-md">
           <div class="column items-center q-mb-md">
             <AvatarImageComponent
             <AvatarImageComponent
@@ -197,7 +197,7 @@
             :loading="loading"
             :loading="loading"
           />
           />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/unit/components/AddMediaDialog.vue

@@ -6,7 +6,7 @@
     >
     >
       <DefaultDialogHeader title="Adicionar Mídia" @close="onDialogCancel" />
       <DefaultDialogHeader title="Adicionar Mídia" @close="onDialogCancel" />
 
 
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="q-pt-none">
         <q-card-section class="q-pt-none">
           <div class="column q-gutter-sm">
           <div class="column q-gutter-sm">
             <DefaultInput
             <DefaultInput
@@ -49,7 +49,7 @@
             :loading="loading"
             :loading="loading"
           />
           />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>

+ 2 - 2
src/pages/unit/tabs/UnitDataTab.vue

@@ -1,6 +1,6 @@
 <template>
 <template>
   <div class="q-pa-md">
   <div class="q-pa-md">
-    <q-form ref="formRef">
+    <DefaultForm ref="formRef">
       <div class="column justify-center items-center q-mb-lg">
       <div class="column justify-center items-center q-mb-lg">
         <AvatarImageComponent ref="avatarRef" @update:file="onAvatarChange" />
         <AvatarImageComponent ref="avatarRef" @update:file="onAvatarChange" />
 
 
@@ -175,7 +175,7 @@
           />
           />
         </div>
         </div>
       </div>
       </div>
-    </q-form>
+    </DefaultForm>
   </div>
   </div>
 </template>
 </template>
 
 

+ 2 - 2
src/pages/users/UserActionPage.vue

@@ -3,7 +3,7 @@
     <DefaultHeaderPage :title="isEdit ? 'Editar Usuário' : 'Cadastro de Usuário'" />
     <DefaultHeaderPage :title="isEdit ? 'Editar Usuário' : 'Cadastro de Usuário'" />
 
 
     <div class="q-pa-md">
     <div class="q-pa-md">
-      <q-form ref="formRef">
+      <DefaultForm ref="formRef">
         <div class="column items-center q-mb-lg">
         <div class="column items-center q-mb-lg">
           <AvatarImageComponent
           <AvatarImageComponent
             ref="avatarRef"
             ref="avatarRef"
@@ -88,7 +88,7 @@
             @click="onSave"
             @click="onSave"
           />
           />
         </div>
         </div>
-      </q-form>
+      </DefaultForm>
     </div>
     </div>
   </div>
   </div>
 </template>
 </template>

+ 2 - 2
src/pages/users/components/AddEditUserDialog.vue

@@ -2,7 +2,7 @@
   <q-dialog ref="dialogRef" @hide="onDialogHide">
   <q-dialog ref="dialogRef" @hide="onDialogHide">
     <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
     <q-card class="q-dialog-plugin overflow-hidden" style="width: 800px">
       <DefaultDialogHeader :title="title" @close="onDialogCancel" />
       <DefaultDialogHeader :title="title" @close="onDialogCancel" />
-      <q-form ref="formRef" @submit="onOKClick">
+      <DefaultForm ref="formRef" @submit="onOKClick">
         <q-card-section class="row q-col-gutter-sm q-pt-none">
         <q-card-section class="row q-col-gutter-sm q-pt-none">
           <DefaultInput
           <DefaultInput
             v-model="form.name"
             v-model="form.name"
@@ -68,7 +68,7 @@
             :disable="!hasUpdatedFields"
             :disable="!hasUpdatedFields"
           />
           />
         </q-card-actions>
         </q-card-actions>
-      </q-form>
+      </DefaultForm>
     </q-card>
     </q-card>
   </q-dialog>
   </q-dialog>
 </template>
 </template>