2025-09-30 21:27:17 +00:00

211 lines
5.6 KiB
Vue

<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { api, useForm } from '@Services/Api';
import { apiTo, viewTo } from './Module';
import GoogleIcon from '@Shared/GoogleIcon.vue';
import PageHeader from '@Holos/PageHeader.vue';
import IconButton from '@Holos/Button/Icon.vue';
import Input from '@Holos/Form/Input.vue';
import Selectable from '@Holos/Form/Selectable.vue';
import PrimaryButton from '@Holos/Button/Primary.vue';
/** Definiciones */
const router = useRouter();
/** Propiedades */
const form = useForm({
petty_cash_id: '',
budget_items: [
{
id: 1,
concept: '',
amount: ''
}
]
});
const pettyCashes = ref([]);
let nextId = 2;
// Computed para calcular el total
const totalBudget = computed(() => {
return form.budget_items.reduce((total, item) => {
return total + (parseFloat(item.amount) || 0);
}, 0);
});
// Función para agregar nueva fila
const addBudgetItem = () => {
form.budget_items.push({
id: nextId++,
concept: '',
amount: 0
});
};
// Función para eliminar fila
const removeBudgetItem = (id) => {
if (form.budget_items.length > 1) {
form.budget_items = form.budget_items.filter(item => item.id !== id);
}
};
// Función para guardar presupuesto
function submit() {
// Filtrar elementos vacíos
const validItems = form.budget_items.filter(item =>
item.concept.trim() !== '' && item.amount > 0
);
if (validItems.length === 0) {
Notify.error('Por favor, agrega al menos un elemento al presupuesto');
return;
}
form.transform(data => ({
...data,
petty_cash_id: form.petty_cash_id?.id,
budget_items: validItems
})).post(apiTo('store-budget', { pettyCash: form.petty_cash_id }), {
onSuccess: () => {
Notify.success('Presupuesto guardado correctamente');
router.push(viewTo({ name: 'index' }));
}
});
}
/** Ciclos */
onMounted(() => {
api.catalog({
'pettyCash:all': null
}, {
onSuccess: (r) => pettyCashes.value = r['pettyCash:all'] ?? []
});
});
</script>
<template>
<PageHeader
title="Asignación de Presupuesto"
>
<RouterLink :to="viewTo({ name: 'index' })">
<IconButton
class="text-white"
icon="arrow_back"
:title="$t('return')"
filled
/>
</RouterLink>
</PageHeader>
<div class="w-full pb-2">
<p class="text-justify text-sm">Gestiona y asigna el presupuesto para una caja chica</p>
</div>
<!-- Card principal -->
<section class="mt-6 bg-white rounded-lg shadow-sm p-6 dark:bg-primary-d dark:border-primary/20 dark:text-primary-dt">
<!-- Formulario de presupuesto -->
<form @submit.prevent="submit" class="space-y-6">
<!-- Campos principales -->
<div class="grid grid-cols-1 gap-6">
<Selectable
v-model="form.petty_cash_id"
id="petty_cash_id"
title="Caja Chica"
:onError="form.errors.petty_cash_id"
:options="pettyCashes"
required
/>
</div>
<!-- Filas de presupuesto -->
<div class="space-y-4">
<h3 class="text-lg font-medium text-gray-900 dark:text-primary-dt">Elementos del Presupuesto</h3>
<div
v-for="(item, index) in form.budget_items"
:key="item.id"
class="grid grid-cols-1 md:grid-cols-12 gap-4 items-end"
>
<!-- Campo Concepto -->
<div class="md:col-span-5">
<Input
v-model="item.concept"
:id="`concept_${item.id}`"
title="Concepto"
/>
</div>
<!-- Campo Monto -->
<div class="md:col-span-4">
<Input
v-model.number="item.amount"
:id="`amount_${item.id}`"
title="Monto"
type="number"
min="0"
step="0.01"
/>
</div>
<!-- Botón de eliminar -->
<div class="md:col-span-3 flex justify-end">
<IconButton
v-if="form.budget_items.length > 1"
@click="removeBudgetItem(item.id)"
icon="delete"
class="text-red-500 hover:text-red-700"
:title="$t('delete')"
/>
</div>
</div>
</div>
<!-- Resumen del presupuesto -->
<div class="mt-8 p-4 bg-gray-50 rounded-lg dark:bg-primary/5">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<GoogleIcon class="text-gray-600 dark:text-primary-dt text-xl" name="account_balance_wallet" />
<span class="text-lg font-medium text-gray-800 dark:text-primary-dt">Total del Presupuesto:</span>
</div>
<div class="text-2xl font-bold text-[#2563eb] dark:text-primary-dt">
${{ totalBudget.toFixed(2) }}
</div>
</div>
</div>
<!-- Botones de acción -->
<div class="flex items-center justify-between pt-6 border-t border-gray-100 dark:border-primary/20">
<!-- Botón Agregar más -->
<IconButton
@click="addBudgetItem"
icon="add"
class="bg-green-600 hover:bg-green-700 text-white"
:title="$t('add')"
>
+ Agregar más
</IconButton>
<!-- Botón Guardar Presupuesto -->
<PrimaryButton
type="submit"
:processing="form.processing"
>
<GoogleIcon class="text-white text-xl mr-2" name="save" />
Guardar Presupuesto
</PrimaryButton>
</div>
</form>
</section>
</template>