276 lines
9.9 KiB
Vue

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { useToast } from 'primevue/usetoast';
import { useConfirm } from 'primevue/useconfirm';
import Breadcrumb from 'primevue/breadcrumb';
import Button from 'primevue/button';
import Card from 'primevue/card';
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';
import Tag from 'primevue/tag';
import Toast from 'primevue/toast';
import ConfirmDialog from 'primevue/confirmdialog';
import ProgressSpinner from 'primevue/progressspinner';
import { useUnitOfMeasureStore } from '../../stores/unitOfMeasureStore';
import UnitsForm from './UnitsForm.vue';
import type { UnitOfMeasure, CreateUnitOfMeasureData } from '../../types/unit-measure.interfaces';
const router = useRouter();
const toast = useToast();
const confirm = useConfirm();
const unitStore = useUnitOfMeasureStore();
// Breadcrumb
const breadcrumbItems = ref([
{ label: 'Catálogo', route: '/catalog' },
{ label: 'Unidades de Medida' }
]);
const home = ref({
icon: 'pi pi-home',
route: '/'
});
// State
const showDialog = ref(false);
const isEditing = ref(false);
const selectedUnit = ref<UnitOfMeasure | null>(null);
// Computed
const units = computed(() => unitStore.units);
const loading = computed(() => unitStore.loading);
const getStatusConfig = (isActive: number) => {
return isActive === 1
? { label: 'Activa', severity: 'success' }
: { label: 'Inactiva', severity: 'secondary' };
};
// Methods
const openCreateDialog = () => {
isEditing.value = false;
selectedUnit.value = null;
showDialog.value = true;
};
const openEditDialog = (unit: UnitOfMeasure) => {
isEditing.value = true;
selectedUnit.value = unit;
showDialog.value = true;
};
const handleSaveUnit = async (data: CreateUnitOfMeasureData) => {
try {
if (isEditing.value && selectedUnit.value) {
await unitStore.updateUnit(selectedUnit.value.id, data);
toast.add({
severity: 'success',
summary: 'Actualización Exitosa',
detail: 'La unidad de medida ha sido actualizada correctamente.',
life: 3000
});
} else {
await unitStore.createUnit(data);
toast.add({
severity: 'success',
summary: 'Creación Exitosa',
detail: 'La unidad de medida ha sido creada correctamente.',
life: 3000
});
}
showDialog.value = false;
} catch (error) {
console.error('Error saving unit:', error);
toast.add({
severity: 'error',
summary: 'Error',
detail: 'No se pudo guardar la unidad de medida. Por favor, intenta nuevamente.',
life: 3000
});
}
};
const confirmDelete = (unit: UnitOfMeasure) => {
confirm.require({
message: `¿Estás seguro de eliminar la unidad de medida "${unit.name}"?`,
header: 'Confirmar Eliminación',
icon: 'pi pi-exclamation-triangle',
acceptLabel: 'Sí, eliminar',
rejectLabel: 'Cancelar',
acceptClass: 'p-button-danger',
accept: () => deleteUnit(unit.id)
});
};
const deleteUnit = async (id: number) => {
try {
await unitStore.deleteUnit(id);
toast.add({
severity: 'success',
summary: 'Eliminación Exitosa',
detail: 'La unidad de medida ha sido eliminada correctamente.',
life: 3000
});
} catch (error) {
console.error('Error deleting unit:', error);
toast.add({
severity: 'error',
summary: 'Error',
detail: 'No se pudo eliminar la unidad de medida. Puede estar en uso.',
life: 3000
});
}
};
// Lifecycle
onMounted(async () => {
await unitStore.fetchUnits();
});
</script>
<template>
<div class="space-y-6">
<!-- Toast & Confirm Dialog -->
<Toast position="bottom-right" />
<ConfirmDialog />
<!-- Breadcrumb -->
<div class="flex flex-col gap-2">
<Breadcrumb :home="home" :model="breadcrumbItems">
<template #item="{ item }">
<a
v-if="item.route"
:href="item.route"
@click.prevent="router.push(item.route)"
class="text-primary hover:underline"
>
{{ item.label }}
</a>
<span v-else class="text-surface-600 dark:text-surface-400">
{{ item.label }}
</span>
</template>
</Breadcrumb>
<!-- Title -->
<div class="flex justify-between items-center">
<h1 class="text-3xl font-black leading-tight tracking-tight text-surface-900 dark:text-white">
Unidades de Medida
</h1>
<Button
label="Nueva Unidad"
icon="pi pi-plus"
@click="openCreateDialog"
/>
</div>
</div>
<!-- Main Card -->
<Card>
<template #content>
<!-- Loading State -->
<div v-if="loading && units.length === 0" class="flex justify-center items-center py-12">
<ProgressSpinner
style="width: 50px; height: 50px"
strokeWidth="4"
animationDuration="1s"
/>
</div>
<!-- Data Table -->
<DataTable
v-else
:value="units"
:loading="loading"
stripedRows
responsiveLayout="scroll"
:paginator="true"
:rows="10"
:rowsPerPageOptions="[5, 10, 20, 50]"
paginatorTemplate="FirstPageLink PrevPageLink PageLinks NextPageLink LastPageLink RowsPerPageDropdown CurrentPageReport"
currentPageReportTemplate="Mostrando {first} a {last} de {totalRecords} unidades"
>
<Column field="abbreviation" header="Abreviatura" sortable style="width: 150px">
<template #body="slotProps">
<span class="font-mono font-semibold text-primary">
{{ slotProps.data.abbreviation }}
</span>
</template>
</Column>
<Column field="name" header="Nombre" sortable>
<template #body="slotProps">
<span class="font-medium text-surface-900 dark:text-white">
{{ slotProps.data.name }}
</span>
</template>
</Column>
<Column field="sat_unit.name" header="Unidad SAT" sortable style="width: 200px">
<template #body="slotProps">
<span class="text-sm text-surface-600 dark:text-surface-400">
{{ slotProps.data.sat_unit?.name || 'N/A' }}
</span>
</template>
</Column>
<Column field="sat_unit.code" header="Código SAT" sortable style="width: 120px">
<template #body="slotProps">
<span class="font-mono text-sm text-surface-600 dark:text-surface-400">
{{ slotProps.data.sat_unit?.code || 'N/A' }}
</span>
</template>
</Column>
<Column field="is_active" header="Estado" sortable style="width: 120px">
<template #body="slotProps">
<Tag
:value="getStatusConfig(slotProps.data.is_active).label"
:severity="getStatusConfig(slotProps.data.is_active).severity"
/>
</template>
</Column>
<Column header="Acciones" style="width: 150px">
<template #body="slotProps">
<div class="flex gap-2">
<Button
icon="pi pi-pencil"
text
rounded
severity="secondary"
@click="openEditDialog(slotProps.data)"
v-tooltip.top="'Editar'"
/>
<Button
icon="pi pi-trash"
text
rounded
severity="danger"
@click="confirmDelete(slotProps.data)"
v-tooltip.top="'Eliminar'"
/>
</div>
</template>
</Column>
<template #empty>
<div class="text-center py-8 text-surface-500 dark:text-surface-400">
No hay unidades de medida registradas.
</div>
</template>
</DataTable>
</template>
</Card>
<!-- Create/Edit Form Dialog -->
<UnitsForm
v-model:visible="showDialog"
:unit="selectedUnit"
:is-editing="isEditing"
@save="handleSaveUnit"
/>
</div>
</template>