Ver Fonte

feat: remove ExclusiveCheckboxPair component; refactor ProposalPricingTab to use DefaultInput for registration and classes

alvesantos há 2 semanas atrás
pai
commit
761ea4ecc8

+ 6 - 37
src/pages/packages/components/AddEditPackageDialog.vue

@@ -10,11 +10,12 @@
         <DefaultForm ref="formRef" @submit="onOKClick">
         <DefaultForm ref="formRef" @submit="onOKClick">
           <q-scroll-area ref="scrollAreaRef" class="form-dialog-scroll-area">
           <q-scroll-area ref="scrollAreaRef" class="form-dialog-scroll-area">
             <q-card-section class="row q-col-gutter-md q-pt-none">
             <q-card-section class="row q-col-gutter-md q-pt-none">
-              <CustomTabComponent
-                v-model:active-tab="currentTab"
-                class="col-12 q-mb-md"
-                :tabs="tabs"
-              />
+              <div class="col-12 q-mb-md">
+                <CustomTabComponent
+                  v-model:active-tab="currentTab"
+                  :tabs="tabs"
+                />
+              </div>
 
 
               <div
               <div
                 v-show="currentTab === 'dados'"
                 v-show="currentTab === 'dados'"
@@ -168,19 +169,10 @@ const scrollAreaRef = ref(null);
 
 
 const defaultPricingBlock = (withCondition = false) => ({
 const defaultPricingBlock = (withCondition = false) => ({
   classes_discount_percentage: null,
   classes_discount_percentage: null,
-  classes_included_in_course: false,
-  classes_installments_allowed: false,
-  classes_max_installments: 13,
   classes_value: null,
   classes_value: null,
 
 
   materials: [{ price: null, product_id: null, quantity: 1 }],
   materials: [{ price: null, product_id: null, quantity: 1 }],
-  materials_included_in_course: false,
-  materials_installments_allowed: false,
-  materials_max_installments: 13,
 
 
-  registration_included_in_course: false,
-  registration_installments_allowed: false,
-  registration_max_installments: 13,
   registration_value: null,
   registration_value: null,
 
 
   total_max_installments: 13,
   total_max_installments: 13,
@@ -232,9 +224,6 @@ const tabs = [
 
 
 const mapPricingBlockFromApi = (data, existingMaterials) => ({
 const mapPricingBlockFromApi = (data, existingMaterials) => ({
   classes_discount_percentage: data.classes_discount_percentage,
   classes_discount_percentage: data.classes_discount_percentage,
-  classes_included_in_course: data.classes_included_in_course,
-  classes_installments_allowed: data.classes_installments_allowed,
-  classes_max_installments: data.classes_max_installments,
   classes_value: data.classes_value,
   classes_value: data.classes_value,
 
 
   materials: data.materials?.length
   materials: data.materials?.length
@@ -244,13 +233,7 @@ const mapPricingBlockFromApi = (data, existingMaterials) => ({
         quantity: material.quantity,
         quantity: material.quantity,
       }))
       }))
     : existingMaterials,
     : existingMaterials,
-  materials_included_in_course: data.materials_included_in_course,
-  materials_installments_allowed: data.materials_installments_allowed,
-  materials_max_installments: data.materials_max_installments,
 
 
-  registration_included_in_course: data.registration_included_in_course,
-  registration_installments_allowed: data.registration_installments_allowed,
-  registration_max_installments: data.registration_max_installments,
   registration_value: data.registration_value,
   registration_value: data.registration_value,
 
 
   total_max_installments: data.total_max_installments,
   total_max_installments: data.total_max_installments,
@@ -265,9 +248,6 @@ const numOrNull = (value) =>
 const toPricingPayload = (block, { withCondition = false } = {}) => {
 const toPricingPayload = (block, { withCondition = false } = {}) => {
   const payload = {
   const payload = {
     classes_discount_percentage: numOrNull(block.classes_discount_percentage),
     classes_discount_percentage: numOrNull(block.classes_discount_percentage),
-    classes_included_in_course: block.classes_included_in_course,
-    classes_installments_allowed: block.classes_installments_allowed,
-    classes_max_installments: Number(block.classes_max_installments) || 13,
     classes_value: numOrNull(block.classes_value),
     classes_value: numOrNull(block.classes_value),
 
 
     materials: block.materials
     materials: block.materials
@@ -277,13 +257,7 @@ const toPricingPayload = (block, { withCondition = false } = {}) => {
         product_id: material.product_id,
         product_id: material.product_id,
         quantity: Number(material.quantity),
         quantity: Number(material.quantity),
       })),
       })),
-    materials_included_in_course: block.materials_included_in_course,
-    materials_installments_allowed: block.materials_installments_allowed,
-    materials_max_installments: Number(block.materials_max_installments) || 13,
 
 
-    registration_included_in_course: block.registration_included_in_course,
-    registration_installments_allowed: block.registration_installments_allowed,
-    registration_max_installments: Number(block.registration_max_installments) || 13,
     registration_value: numOrNull(block.registration_value),
     registration_value: numOrNull(block.registration_value),
 
 
     total_max_installments: Number(block.total_max_installments) || 13,
     total_max_installments: Number(block.total_max_installments) || 13,
@@ -346,9 +320,6 @@ const onOKClick = async () => {
           delete updated.class_duration_hours;
           delete updated.class_duration_hours;
         }
         }
 
 
-        // Pavão/Irrecusável têm validação cruzada entre campos (par exclusivo
-        // de checkbox) e um sinal explícito de "removido" (null) — não é
-        // seguro fatiar isso num diff parcial, vão sempre inteiros.
         updated.pavao = payload.pavao;
         updated.pavao = payload.pavao;
         updated.irrecusavel = payload.irrecusavel;
         updated.irrecusavel = payload.irrecusavel;
 
 
@@ -361,8 +332,6 @@ const onOKClick = async () => {
       return createPackage(payload);
       return createPackage(payload);
     });
     });
   } finally {
   } finally {
-    // Se o erro veio de dentro de Pavão/Irrecusável, troca pra aba certa —
-    // senão a mensagem fica escondida numa tab que não está visível.
     const errorKeys = Object.keys(validationErrors);
     const errorKeys = Object.keys(validationErrors);
 
 
     if (errorKeys.some((key) => key.startsWith("pavao."))) {
     if (errorKeys.some((key) => key.startsWith("pavao."))) {

+ 0 - 44
src/pages/packages/components/ExclusiveCheckboxPair.vue

@@ -1,44 +0,0 @@
-<template>
-  <div class="row q-col-gutter-md">
-    <div class="col-6">
-      <q-checkbox
-        color="primary"
-        label="Incluso no valor do curso"
-        :model-value="included"
-        @update:model-value="onToggle('included', $event)"
-      />
-    </div>
-
-    <div class="col-6">
-      <q-checkbox
-        color="primary"
-        label="Permite Parcelar"
-        :model-value="installments"
-        @update:model-value="onToggle('installments', $event)"
-      />
-    </div>
-  </div>
-</template>
-
-<script setup>
-defineProps({
-  included: {
-    default: false,
-    type: Boolean,
-  },
-  installments: {
-    default: false,
-    type: Boolean,
-  },
-});
-
-const emit = defineEmits(["update:included", "update:installments"]);
-
-// Comportam-se como um par exclusivo (um dos dois é sempre obrigatório):
-// marcar um desmarca o outro; desmarcar direto é ignorado.
-const onToggle = (which, value) => {
-  if (!value) return;
-
-  emit(which === "included" ? "update:included" : "update:installments", true);
-};
-</script>

+ 11 - 143
src/pages/packages/components/ProposalPricingTab.vue

@@ -16,32 +16,6 @@
       label="Valor da Matrícula"
       label="Valor da Matrícula"
     />
     />
 
 
-    <ExclusiveCheckboxPair
-      class="col-12"
-      :included="modelValue.registration_included_in_course"
-      :installments="modelValue.registration_installments_allowed"
-      @update:included="setExclusive('registration', 'included')"
-      @update:installments="setExclusive('registration', 'installments')"
-    />
-
-    <div v-if="sectionError('registration')" class="col-12 text-negative text-caption">
-      {{ sectionError('registration') }}
-    </div>
-
-    <template v-if="modelValue.registration_installments_allowed">
-      <DefaultInput
-        v-model="modelValue.registration_max_installments"
-        class="col-6"
-        label="Quantidade de Parcelas Máximas"
-        min="1"
-        type="number"
-      />
-
-      <div class="col-6 installment-hint">
-        Valor da Parcela: {{ formatInstallment(registrationInstallmentValue) }}
-      </div>
-    </template>
-
     <q-separator class="col-12" />
     <q-separator class="col-12" />
 
 
     <!-- Aulas -->
     <!-- Aulas -->
@@ -51,41 +25,15 @@
       label="Valor de Aulas"
       label="Valor de Aulas"
     />
     />
 
 
-    <ExclusiveCheckboxPair
+    <DefaultInput
+      v-model="modelValue.classes_discount_percentage"
       class="col-12"
       class="col-12"
-      :included="modelValue.classes_included_in_course"
-      :installments="modelValue.classes_installments_allowed"
-      @update:included="setExclusive('classes', 'included')"
-      @update:installments="setExclusive('classes', 'installments')"
+      label="Desconto de Aulas (%)"
+      max="100"
+      min="0"
+      type="number"
     />
     />
 
 
-    <div v-if="sectionError('classes')" class="col-12 text-negative text-caption">
-      {{ sectionError('classes') }}
-    </div>
-
-    <template v-if="modelValue.classes_installments_allowed">
-      <DefaultInput
-        v-model="modelValue.classes_max_installments"
-        class="col-4"
-        label="Quantidade de Parcelas Máximas"
-        min="1"
-        type="number"
-      />
-
-      <div class="col-4 installment-hint">
-        Valor da Parcela: {{ formatInstallment(classesInstallmentValue) }}
-      </div>
-
-      <DefaultInput
-        v-model="modelValue.classes_discount_percentage"
-        class="col-4"
-        label="Desconto (%)"
-        max="100"
-        min="0"
-        type="number"
-      />
-    </template>
-
     <q-separator class="col-12" />
     <q-separator class="col-12" />
 
 
     <!-- Materiais -->
     <!-- Materiais -->
@@ -144,32 +92,6 @@
       </div>
       </div>
     </div>
     </div>
 
 
-    <ExclusiveCheckboxPair
-      class="col-12"
-      :included="modelValue.materials_included_in_course"
-      :installments="modelValue.materials_installments_allowed"
-      @update:included="setExclusive('materials', 'included')"
-      @update:installments="setExclusive('materials', 'installments')"
-    />
-
-    <div v-if="sectionError('materials')" class="col-12 text-negative text-caption">
-      {{ sectionError('materials') }}
-    </div>
-
-    <template v-if="modelValue.materials_installments_allowed">
-      <DefaultInput
-        v-model="modelValue.materials_max_installments"
-        class="col-6"
-        label="Quantidade de Parcelas Máximas"
-        min="1"
-        type="number"
-      />
-
-      <div class="col-6 installment-hint">
-        Valor da Parcela: {{ formatInstallment(materialsInstallmentValue) }}
-      </div>
-    </template>
-
     <q-separator class="col-12" />
     <q-separator class="col-12" />
 
 
     <!-- Valor Total do Curso -->
     <!-- Valor Total do Curso -->
@@ -179,6 +101,7 @@
       v-model="modelValue.total_value"
       v-model="modelValue.total_value"
       class="col-12"
       class="col-12"
       label="Valor Total do Curso"
       label="Valor Total do Curso"
+      readonly
     />
     />
 
 
     <DefaultInput
     <DefaultInput
@@ -216,7 +139,6 @@ import { computed, watch } from "vue";
 import { formatToBRLCurrency } from "src/helpers/utils";
 import { formatToBRLCurrency } from "src/helpers/utils";
 import { useInputRules } from "src/composables/useInputRules";
 import { useInputRules } from "src/composables/useInputRules";
 
 
-import ExclusiveCheckboxPair from "./ExclusiveCheckboxPair.vue";
 import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
 import DefaultCurrencyInput from "src/components/defaults/DefaultCurrencyInput.vue";
 import DefaultInput from "src/components/defaults/DefaultInput.vue";
 import DefaultInput from "src/components/defaults/DefaultInput.vue";
 import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
 import DefaultSelect from "src/components/defaults/DefaultSelect.vue";
@@ -249,9 +171,6 @@ const props = defineProps({
   },
   },
 });
 });
 
 
-const sectionError = (section) =>
-  props.validationErrors[`${props.fieldPrefix}.${section}_included_in_course`];
-
 const materialsTotalValue = computed(() =>
 const materialsTotalValue = computed(() =>
   modelValue.value.materials.reduce(
   modelValue.value.materials.reduce(
     (sum, material) =>
     (sum, material) =>
@@ -260,27 +179,6 @@ const materialsTotalValue = computed(() =>
   ),
   ),
 );
 );
 
 
-const registrationInstallmentValue = computed(() =>
-  installmentValue(
-    modelValue.value.registration_value,
-    modelValue.value.registration_max_installments,
-  ),
-);
-
-const classesInstallmentValue = computed(() =>
-  installmentValue(
-    modelValue.value.classes_value,
-    modelValue.value.classes_max_installments,
-  ),
-);
-
-const materialsInstallmentValue = computed(() =>
-  installmentValue(
-    materialsTotalValue.value,
-    modelValue.value.materials_max_installments,
-  ),
-);
-
 const totalInstallmentValue = computed(() =>
 const totalInstallmentValue = computed(() =>
   installmentValue(
   installmentValue(
     modelValue.value.total_value,
     modelValue.value.total_value,
@@ -288,52 +186,22 @@ const totalInstallmentValue = computed(() =>
   ),
   ),
 );
 );
 
 
-// Soma sugerida do Valor Total, considerando só as seções marcadas como
-// "incluso no valor do curso". O campo continua editável: só reaplicamos a
-// sugestão enquanto o usuário não tiver digitado um valor diferente dela.
-let lastSuggestedTotal = null;
-
 const suggestedTotal = computed(() => {
 const suggestedTotal = computed(() => {
   let total = 0;
   let total = 0;
-
-  if (modelValue.value.registration_included_in_course) {
-    total += Number(modelValue.value.registration_value) || 0;
-  }
-
-  if (modelValue.value.classes_included_in_course) {
-    total += Number(modelValue.value.classes_value) || 0;
-  }
-
-  if (modelValue.value.materials_included_in_course) {
-    total += materialsTotalValue.value;
-  }
-
+  total += Number(modelValue.value.registration_value) || 0;
+  total += Number(modelValue.value.classes_value) || 0;
+  total += materialsTotalValue.value;
   return total;
   return total;
 });
 });
 
 
 watch(
 watch(
   suggestedTotal,
   suggestedTotal,
   (newTotal) => {
   (newTotal) => {
-    if (
-      modelValue.value.total_value === null ||
-      modelValue.value.total_value === lastSuggestedTotal
-    ) {
-      modelValue.value.total_value = newTotal;
-    }
-
-    lastSuggestedTotal = newTotal;
+    modelValue.value.total_value = newTotal;
   },
   },
   { immediate: true },
   { immediate: true },
 );
 );
 
 
-const setExclusive = (section, option) => {
-  const includedKey = `${section}_included_in_course`;
-  const installmentsKey = `${section}_installments_allowed`;
-
-  modelValue.value[includedKey] = option === "included";
-  modelValue.value[installmentsKey] = option === "installments";
-};
-
 const addMaterial = () => {
 const addMaterial = () => {
   modelValue.value.materials.push({
   modelValue.value.materials.push({
     price: null,
     price: null,