comal-pagos/src/components/App/ConceptSection.vue

362 lines
10 KiB
Vue

<script setup>
import { onMounted, ref, computed } from "vue";
import { useForm, api, apiURL } from "@Services/Api";
import Input from "@Holos/Form/Input.vue";
import Textarea from "@Holos/Form/Textarea.vue";
import Selectable from "@Holos/Form/Selectable.vue";
/** Eventos */
const emit = defineEmits(["concept-created"]);
const form = useForm({
direction_id: null,
unit_id: null,
short_name: "",
name: "",
legal_instrument: "",
article: "",
content: "",
min_amount_uma: "",
max_amount_uma: "",
min_amount_peso: "",
max_amount_peso: "",
unit_cost_uma: "",
unit_cost_peso: "",
charge_type: null,
concept_type: null,
});
const resetForm = () => {
form.direction_id = null;
form.unit_id = null;
form.short_name = "";
form.name = "";
form.legal_instrument = "";
form.article = "";
form.content = "";
form.min_amount_uma = "";
form.max_amount_uma = "";
form.min_amount_peso = "";
form.max_amount_peso = "";
form.unit_cost_uma = "";
form.unit_cost_peso = "";
form.charge_type = null;
form.concept_type = null;
};
const isFormOpen = ref(false);
const addresses = ref([]);
const units = ref([]);
const chargeTypeId = computed(() => {
return typeof form.charge_type === "object" && form.charge_type?.id
? form.charge_type.id
: form.charge_type;
});
const chargeTypesOptions = ref([
{ id: "uma_range", name: "Rango por UMA" },
{ id: "peso_range", name: "Rango en pesos (MXN$)" },
{ id: "uma_fixed", name: "Tabulador en UMA" },
{ id: "peso_fixed", name: "Tabulador en pesos (MXN$)" },
]);
const conceptTypesOptions = ref([
{ id: "membership", name: "Membresía" },
{ id: "fine", name: "Multa" }
]);
const showUmaFields = computed(() => {
return (
chargeTypeId.value === "uma_range" || chargeTypeId.value === "uma_fixed"
);
});
const showPesoFields = computed(() => {
return (
chargeTypeId.value === "peso_range" || chargeTypeId.value === "peso_fixed"
);
});
const isRangeType = computed(() => {
return (
chargeTypeId.value === "uma_range" || chargeTypeId.value === "peso_range"
);
});
const isFixedType = computed(() => {
return (
chargeTypeId.value === "uma_fixed" || chargeTypeId.value === "peso_fixed"
);
});
/** Cargar cosas */
onMounted(async () => {
api.get(apiURL("directions"), {
onSuccess: (data) => {
addresses.value = data.models?.data || data.data || [];
},
});
api.get(apiURL("units"), {
onSuccess: (data) => {
units.value = data.models?.data || data.data || [];
},
});
});
/** Métodos */
const handleSubmit = () => {
const baseData = {
direction_id:
typeof form.direction_id === "object"
? form.direction_id.id
: Number(form.direction_id),
unit_id:
typeof form.unit_id === "object" ? form.unit_id.id : Number(form.unit_id),
concept_type:
typeof form.concept_type === "object"
? form.concept_type.id
: String(form.concept_type),
short_name: form.short_name,
name: form.name,
legal_instrument: form.legal_instrument,
article: form.article,
content: form.content,
charge_type: chargeTypeId.value,
};
// Agregar campos condicionales solo si tienen valor
if (showUmaFields.value && isRangeType.value) {
if (form.min_amount_uma)
baseData.min_amount_uma = Number(form.min_amount_uma);
if (form.max_amount_uma)
baseData.max_amount_uma = Number(form.max_amount_uma);
}
if (showPesoFields.value && isRangeType.value) {
if (form.min_amount_peso)
baseData.min_amount_peso = Number(form.min_amount_peso);
if (form.max_amount_peso)
baseData.max_amount_peso = Number(form.max_amount_peso);
}
if (showUmaFields.value && isFixedType.value && form.unit_cost_uma) {
baseData.unit_cost_uma = Number(form.unit_cost_uma);
}
if (showPesoFields.value && isFixedType.value && form.unit_cost_peso) {
baseData.unit_cost_peso = Number(form.unit_cost_peso);
}
emit("concept-created", baseData);
resetForm();
isFormOpen.value = false;
};
</script>
<template>
<div class="mx-4 mt-4 mb-6">
<button
v-if="!isFormOpen"
@click="isFormOpen = true"
class="w-full bg-primary hover:bg-primary/90 text-white font-semibold py-3 px-6 rounded-lg transition-all duration-200 shadow-md hover:shadow-lg flex items-center justify-center space-x-2"
>
<span class="text-2xl">+</span>
<span>Nuevo Concepto</span>
</button>
<div
v-if="isFormOpen"
class="bg-white rounded-lg p-8 mx-4 mt-4 mb-6 shadow-md"
>
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-gray-900">
{{ $t("concept.create.title") }}
</h2>
<button
@click="isFormOpen = false"
class="text-gray-400 hover:text-gray-600 transition-colors"
title="Cerrar"
>
<span class="text-2xl">&times;</span>
</button>
</div>
<form @submit.prevent="handleSubmit">
<div class="mb-5 grid grid-cols-2 gap-4">
<Selectable
v-model="form.concept_type"
label="name"
value="id"
:title="$t('concept.conceptType')"
:options="conceptTypesOptions"
required
/>
<Selectable
v-model="form.direction_id"
label="name"
value="id"
:title="$t('concept.direction')"
:options="addresses"
required
/>
<Input
v-model="form.short_name"
class="col-span-2"
:id="$t('concept.shortName')"
type="text"
:onError="form.errors.short_name"
required
/>
<Input
v-model="form.name"
class="col-span-2"
:id="$t('concept.name')"
type="text"
:onError="form.errors.name"
required
/>
</div>
<div class="mb-5 grid grid-cols-2 gap-4 py-2">
<Input
v-model="form.legal_instrument"
class="col-span-2"
:id="$t('concept.legal')"
type="text"
:onError="form.errors.legal_instrument"
required
/>
<Input
v-model="form.article"
class="col-span-2"
:id="$t('concept.article')"
type="text"
:onError="form.errors.article"
required
/>
</div>
<Textarea
v-model="form.content"
class="col-span-2"
:id="$t('concept.description')"
:onError="form.errors.content"
required
/>
<div class="">
<hr class="my-6 border-gray-300" />
<h4 class="text-lg font-semibold mb-4 text-gray-700">
{{ $t("concept.defineChargeType") }}
</h4>
<div class="mb-5 grid grid-cols-3 gap-4 py-2">
<Selectable
v-model="form.charge_type"
label="name"
value="id"
:title="$t('concept.chargeType')"
:options="chargeTypesOptions"
required
/>
</div>
</div>
<div
v-if="showUmaFields && isRangeType"
class="mb-5 grid grid-cols-3 gap-4 py-2"
>
<Selectable
v-model="form.unit_id"
label="name"
value="id"
:title="$t('concept.sizeUnit')"
:options="units"
required
/>
<Input
v-model="form.min_amount_uma"
:id="$t('concept.minimumAmountUma')"
type="number"
step="0.01"
:onError="form.errors.min_amount_uma"
/>
<Input
v-model="form.max_amount_uma"
:id="$t('concept.maximumAmountUma')"
type="number"
step="0.01"
:onError="form.errors.max_amount_uma"
/>
</div>
<div
v-if="showPesoFields && isRangeType"
class="mb-5 grid grid-cols-3 gap-4 py-2"
>
<Selectable
v-model="form.unit_id"
label="name"
value="id"
:title="$t('concept.sizeUnit')"
:options="units"
required
/>
<Input
v-model="form.min_amount_peso"
:id="$t('concept.minimumAmountPeso')"
type="number"
step="0.01"
:onError="form.errors.min_amount_peso"
/>
<Input
v-model="form.max_amount_peso"
:id="$t('concept.maximumAmountPeso')"
type="number"
step="0.01"
:onError="form.errors.max_amount_peso"
/>
</div>
<div v-if="isFixedType">
<hr class="my-4 border-gray-300" />
<h4 class="text-lg font-semibold mb-4 text-gray-700">
{{ $t("concept.tabulator") }}
</h4>
<div class="mb-5 grid grid-cols-3 gap-4 py-2">
<Selectable
v-model="form.unit_id"
label="name"
value="id"
:title="$t('concept.sizeUnit')"
:options="units"
required
/>
<Input
v-if="showUmaFields"
v-model="form.unit_cost_uma"
:id="$t('concept.costUnitUma')"
type="number"
step="0.01"
:onError="form.errors.unit_cost_uma"
/>
<Input
v-if="showPesoFields"
v-model="form.unit_cost_peso"
:id="$t('concept.costUnitPeso')"
type="number"
step="0.01"
:onError="form.errors.unit_cost_peso"
/>
</div>
</div>
<div class="flex justify-center mt-4">
<button
type="submit"
:disabled="form.processing"
class="w-full max-w-xs bg-primary hover:bg-primary/90 text-white font-semibold py-3 rounded-md transition-all duration-200 shadow-sm hover:shadow-md disabled:opacity-50"
>
{{ form.processing ? "Guardando..." : "Guardar" }}
</button>
</div>
</form>
</div>
</div>
</template>