From 1909ebec6812694bd926262b551c08230beee41d Mon Sep 17 00:00:00 2001 From: Juan Felipe Zapata Moreno Date: Wed, 25 Feb 2026 13:37:02 -0600 Subject: [PATCH] feat: agregar subcategorias y modificar vistas --- src/lang/es.js | 10 +- src/pages/POS/Category/CreateModal.vue | 4 +- src/pages/POS/Category/Index.vue | 10 + .../Category/Subcategories/CreateModal.vue | 131 ++++++++++ .../Category/Subcategories/DeleteModal.vue | 117 +++++++++ .../POS/Category/Subcategories/EditModal.vue | 142 +++++++++++ .../POS/Category/Subcategories/Index.vue | 236 ++++++++++++++++++ src/pages/POS/Inventory/CreateModal.vue | 115 +++++++-- src/pages/POS/Inventory/EditModal.vue | 45 +++- src/pages/POS/Inventory/Index.vue | 25 +- src/router/Index.js | 5 + 11 files changed, 812 insertions(+), 28 deletions(-) create mode 100644 src/pages/POS/Category/Subcategories/CreateModal.vue create mode 100644 src/pages/POS/Category/Subcategories/DeleteModal.vue create mode 100644 src/pages/POS/Category/Subcategories/EditModal.vue create mode 100644 src/pages/POS/Category/Subcategories/Index.vue diff --git a/src/lang/es.js b/src/lang/es.js index c15bdb8..62503a1 100644 --- a/src/lang/es.js +++ b/src/lang/es.js @@ -452,7 +452,7 @@ export default { pos: { title: 'Punto de Venta', subtitle: 'Gestión de ventas y caja', - category: 'Categorías', + category: 'Clasificaciones', bundles: 'Paquetes', inventory: 'Productos', prices: 'Precios', @@ -512,13 +512,13 @@ export default { inactive: 'Inactivo', }, category: { - title: 'Gestión De Categorías', - description: 'Administra las categorías de productos.', + title: 'Gestión De Clasificaciones', + description: 'Administra las clasificaciones de productos.', create: { - title: 'Nueva Categoría', + title: 'Nueva Clasificación', }, edit: { - title: 'Editar Categoría', + title: 'Editar Clasificación', }, }, prices: { diff --git a/src/pages/POS/Category/CreateModal.vue b/src/pages/POS/Category/CreateModal.vue index f50a37f..c33b8c0 100644 --- a/src/pages/POS/Category/CreateModal.vue +++ b/src/pages/POS/Category/CreateModal.vue @@ -23,9 +23,9 @@ const form = useForm({ /** Métodos */ const createCategory = () => { form.post(apiURL('categorias'), { - onSuccess: () => { + onSuccess: (data) => { Notify.success('Categoría creada exitosamente'); - emit('created'); + emit('created', data?.model); closeModal(); }, onError: () => { diff --git a/src/pages/POS/Category/Index.vue b/src/pages/POS/Category/Index.vue index 23702ad..9c5ea48 100644 --- a/src/pages/POS/Category/Index.vue +++ b/src/pages/POS/Category/Index.vue @@ -1,5 +1,6 @@ + + diff --git a/src/pages/POS/Category/Subcategories/DeleteModal.vue b/src/pages/POS/Category/Subcategories/DeleteModal.vue new file mode 100644 index 0000000..e4f286b --- /dev/null +++ b/src/pages/POS/Category/Subcategories/DeleteModal.vue @@ -0,0 +1,117 @@ + + + diff --git a/src/pages/POS/Category/Subcategories/EditModal.vue b/src/pages/POS/Category/Subcategories/EditModal.vue new file mode 100644 index 0000000..065d107 --- /dev/null +++ b/src/pages/POS/Category/Subcategories/EditModal.vue @@ -0,0 +1,142 @@ + + + diff --git a/src/pages/POS/Category/Subcategories/Index.vue b/src/pages/POS/Category/Subcategories/Index.vue new file mode 100644 index 0000000..8261684 --- /dev/null +++ b/src/pages/POS/Category/Subcategories/Index.vue @@ -0,0 +1,236 @@ + + + diff --git a/src/pages/POS/Inventory/CreateModal.vue b/src/pages/POS/Inventory/CreateModal.vue index 74041a0..54ead43 100644 --- a/src/pages/POS/Inventory/CreateModal.vue +++ b/src/pages/POS/Inventory/CreateModal.vue @@ -5,6 +5,9 @@ import { useForm, apiURL } from '@Services/Api'; import Modal from '@Holos/Modal.vue'; import FormInput from '@Holos/Form/Input.vue'; import FormError from '@Holos/Form/Elements/Error.vue'; +import GoogleIcon from '@Shared/GoogleIcon.vue'; +import CategoryCreateModal from '@Pages/POS/Category/CreateModal.vue'; +import SubcategoryCreateModal from '@Pages/POS/Category/Subcategories/CreateModal.vue'; /** Eventos */ const emit = defineEmits(['close', 'created']); @@ -16,7 +19,10 @@ const props = defineProps({ /** Estado */ const categories = ref([]); +const subcategories = ref([]); const units = ref([]); +const showCategoryCreate = ref(false); +const showSubcategoryCreate = ref(false); /** Formulario */ const form = useForm({ @@ -25,6 +31,7 @@ const form = useForm({ sku: '', barcode: '', category_id: '', + subcategory_id: '', unit_of_measure_id: null, retail_price: 0, tax: 16, @@ -60,6 +67,12 @@ const loadCategories = async () => { } }; +const onCategoryChange = () => { + const selected = categories.value.find(c => c.id == form.category_id); + subcategories.value = selected?.subcategories ?? []; + form.subcategory_id = ''; +}; + const loadUnits = async () => { try { const response = await fetch(apiURL('unidades-medida/active'), { @@ -117,8 +130,23 @@ const createProduct = () => { }); }; +const onCategoryCreated = async (newCategory) => { + if (newCategory) { + form.category_id = newCategory.id; + } + await loadCategories(); +}; + +const onSubcategoryCreated = (newSubcategory) => { + if (newSubcategory) { + subcategories.value.push(newSubcategory); + form.subcategory_id = newSubcategory.id; + } +}; + const closeModal = () => { form.reset(); + subcategories.value = []; emit('close'); }; @@ -216,28 +244,72 @@ watch(() => form.track_serials, () => { - +
- - {{ category.name }} - - + + + + +
+ +
+ +
+ + +
+ +
+
+ + + + diff --git a/src/pages/POS/Inventory/EditModal.vue b/src/pages/POS/Inventory/EditModal.vue index ada0859..af51d65 100644 --- a/src/pages/POS/Inventory/EditModal.vue +++ b/src/pages/POS/Inventory/EditModal.vue @@ -21,6 +21,7 @@ const props = defineProps({ /** Estado */ const categories = ref([]); +const subcategories = ref([]); const units = ref([]); const activeTab = ref('general'); @@ -40,6 +41,7 @@ const form = useForm({ sku: '', barcode: '', category_id: '', + subcategory_id: '', unit_of_measure_id: null, retail_price: 0, tax: 16, @@ -92,12 +94,23 @@ const loadCategories = async () => { const result = await response.json(); if (result.data && result.data.categories && result.data.categories.data) { categories.value = result.data.categories.data; + // Actualizar subcategorías si ya hay una categoría seleccionada + if (form.category_id) { + const selected = categories.value.find(c => c.id == form.category_id); + subcategories.value = selected?.subcategories ?? []; + } } } catch (error) { console.error('Error loading categories:', error); } }; +const onCategoryChange = () => { + const selected = categories.value.find(c => c.id == form.category_id); + subcategories.value = selected?.subcategories ?? []; + form.subcategory_id = ''; +}; + const loadUnits = async () => { try { const response = await fetch(apiURL('unidades-medida/active'), { @@ -275,6 +288,7 @@ watch(() => props.product, (newProduct) => { form.sku = newProduct.sku || ''; form.barcode = newProduct.barcode || ''; form.category_id = newProduct.category_id || ''; + form.subcategory_id = newProduct.subcategory_id || ''; form.unit_of_measure_id = newProduct.unit_of_measure_id || null; form.cost = parseFloat(newProduct.price?.cost || 0); form.retail_price = parseFloat(newProduct.price?.retail_price || 0); @@ -285,11 +299,11 @@ watch(() => props.product, (newProduct) => { } }, { immediate: true }); -watch(() => props.show, (newValue) => { +watch(() => props.show, async (newValue) => { if (newValue) { - loadCategories(); loadUnits(); activeTab.value = 'general'; + await loadCategories(); } }); @@ -424,14 +438,15 @@ watch(activeTab, (tab) => {
+ + + + +
+
- + {{ model.category?.name || '-' }} +
diff --git a/src/router/Index.js b/src/router/Index.js index db4a6bf..ed5f6bf 100644 --- a/src/router/Index.js +++ b/src/router/Index.js @@ -37,6 +37,11 @@ const router = createRouter({ name: 'pos.category.index', component: () => import('@Pages/POS/Category/Index.vue') }, + { + path: 'category/:id/subcategories', + name: 'pos.category.subcategories', + component: () => import('@Pages/POS/Category/Subcategories/Index.vue') + }, { path: 'bundles', name: 'pos.bundles.index',