diff --git a/components.d.ts b/components.d.ts
index cc2ea0f..eb03591 100644
--- a/components.d.ts
+++ b/components.d.ts
@@ -13,25 +13,33 @@ declare module 'vue' {
export interface GlobalComponents {
AppConfig: typeof import('./src/components/layout/AppConfig.vue')['default']
AppTopbar: typeof import('./src/components/Holos/AppTopbar.vue')['default']
+ Avatar: typeof import('primevue/avatar')['default']
Badge: typeof import('primevue/badge')['default']
+ Breadcrumb: typeof import('primevue/breadcrumb')['default']
Button: typeof import('primevue/button')['default']
Card: typeof import('primevue/card')['default']
Checkbox: typeof import('primevue/checkbox')['default']
+ Chip: typeof import('primevue/chip')['default']
Column: typeof import('primevue/column')['default']
DataTable: typeof import('primevue/datatable')['default']
+ Dialog: typeof import('primevue/dialog')['default']
+ Dropdown: typeof import('primevue/dropdown')['default']
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
IconField: typeof import('primevue/iconfield')['default']
InputGroup: typeof import('primevue/inputgroup')['default']
InputGroupAddon: typeof import('primevue/inputgroupaddon')['default']
InputIcon: typeof import('primevue/inputicon')['default']
+ InputNumber: typeof import('primevue/inputnumber')['default']
InputText: typeof import('primevue/inputtext')['default']
KpiCard: typeof import('./src/components/shared/KpiCard.vue')['default']
Menu: typeof import('primevue/menu')['default']
Message: typeof import('primevue/message')['default']
+ ProgressSpinner: typeof import('primevue/progressspinner')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
Sidebar: typeof import('./src/components/layout/Sidebar.vue')['default']
Tag: typeof import('primevue/tag')['default']
+ Textarea: typeof import('primevue/textarea')['default']
TopBar: typeof import('./src/components/layout/TopBar.vue')['default']
}
export interface GlobalDirectives {
diff --git a/package-lock.json b/package-lock.json
index facd79e..af3fa98 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -13,6 +13,7 @@
"@tailwindcss/vite": "^4.1.16",
"@vueuse/core": "^14.0.0",
"axios": "^1.13.2",
+ "pinia": "^3.0.4",
"primeicons": "^7.0.0",
"primevue": "^4.4.1",
"tailwindcss-primeui": "^0.6.1",
@@ -1299,6 +1300,30 @@
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
+ "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.7",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
+ "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
"node_modules/@vue/language-core": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-3.1.3.tgz",
@@ -1466,6 +1491,15 @@
"proxy-from-env": "^1.1.0"
}
},
+ "node_modules/birpc": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.7.0.tgz",
+ "integrity": "sha512-tub/wFGH49vNCm0xraykcY3TcRgX/3JsALYq/Lwrtti+bTyFHkCUAWF5wgYoie8P41wYwig2mIKiqoocr1EkEQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -1512,6 +1546,21 @@
"integrity": "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ==",
"license": "MIT"
},
+ "node_modules/copy-anything": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+ "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/csstype": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
@@ -1860,6 +1909,24 @@
"node": ">= 0.4"
}
},
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-what": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+ "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
"node_modules/jiti": {
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
@@ -2174,6 +2241,12 @@
"node": ">= 0.6"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/mlly": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
@@ -2247,6 +2320,12 @@
"integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
"license": "MIT"
},
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -2265,6 +2344,36 @@
"url": "https://github.com/sponsors/jonschlinkert"
}
},
+ "node_modules/pinia": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+ "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^7.7.7"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.5.0",
+ "vue": "^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinia/node_modules/@vue/devtools-api": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
+ "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.7"
+ }
+ },
"node_modules/pkg-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
@@ -2361,6 +2470,12 @@
"url": "https://paulmillr.com/funding/"
}
},
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
"node_modules/rollup": {
"version": "4.52.5",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.5.tgz",
@@ -2411,6 +2526,27 @@
"node": ">=0.10.0"
}
},
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/superjson": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.5.tgz",
+ "integrity": "sha512-zWPTX96LVsA/eVYnqOM2+ofcdPqdS1dAF1LN4TS2/MWuUpfitd9ctTa87wt4xrYnZnkLtS69xpBdSxVBP5Rm6w==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/tailwindcss": {
"version": "4.1.16",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.16.tgz",
diff --git a/package.json b/package.json
index e73582a..1fe36b3 100644
--- a/package.json
+++ b/package.json
@@ -14,6 +14,7 @@
"@tailwindcss/vite": "^4.1.16",
"@vueuse/core": "^14.0.0",
"axios": "^1.13.2",
+ "pinia": "^3.0.4",
"primeicons": "^7.0.0",
"primevue": "^4.4.1",
"tailwindcss-primeui": "^0.6.1",
diff --git a/src/components/layout/Sidebar.vue b/src/components/layout/Sidebar.vue
index 67c7713..6e0bb30 100644
--- a/src/components/layout/Sidebar.vue
+++ b/src/components/layout/Sidebar.vue
@@ -1,5 +1,9 @@
-
-
-
-
-
-
-
-
-
-
-
-
- {{ data.sku }}
-
-
-
-
-
-
-
{{ data.name }}
-
{{ data.category }}
-
-
-
-
-
-
-
-
-
- {{ data.quantity }}
-
-
-
-
-
-
-
- {{ data.minStock }}
-
-
-
-
-
- {{ formatCurrency(data.unitPrice) }}
-
-
-
-
-
- {{ formatDate(data.lastUpdated) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/src/modules/warehouse/components/WarehouseCategory.vue b/src/modules/warehouse/components/WarehouseCategory.vue
new file mode 100644
index 0000000..76a7833
--- /dev/null
+++ b/src/modules/warehouse/components/WarehouseCategory.vue
@@ -0,0 +1,5 @@
+
+
+ Categorías de Almacenes
+
+
diff --git a/src/modules/warehouse/components/WarehouseClassification.vue b/src/modules/warehouse/components/WarehouseClassification.vue
new file mode 100644
index 0000000..ff53033
--- /dev/null
+++ b/src/modules/warehouse/components/WarehouseClassification.vue
@@ -0,0 +1,585 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Administración de Clasificaciones
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No hay clasificaciones creadas
+
+
+
+
+
+
+
+
+
+ {{ category.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Selecciona una clasificación
+
+
+ Elige una clasificación de la lista para ver sus detalles y subclasificaciones
+
+
+
+
+
+
+
+
+
+
+
+
+ Detalles de la Clasificación
+
+
+ {{ selectedCategory.name }}
+
+
+ {{ selectedCategory.description }}
+
+
+
+
+
+
+
+
+
+
+
+
+ Subclasificaciones ({{ selectedCategory.subcategories.length }})
+
+
+
+
+
+
+
+
+
+ {{ slotProps.data.name }}
+
+
+
+
+
+
+
+ {{ new Date(slotProps.data.created_at).toLocaleDateString('es-MX', {
+ year: 'numeric',
+ month: 'short',
+ day: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit'
+ }) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ No hay subclasificaciones
+
+
+ Agrega subclasificaciones para organizar mejor esta categoría
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Código *
+
+
+
+
+
+
+
+ Nombre *
+
+
+
+
+
+
+
+ Descripción
+
+
+
+
+
+
+
+ Clasificación Padre (Opcional)
+
+
+
+ Deja vacío para crear una clasificación raíz
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/warehouse/components/WarehouseDashboard.vue b/src/modules/warehouse/components/WarehouseDashboard.vue
deleted file mode 100644
index c4355a9..0000000
--- a/src/modules/warehouse/components/WarehouseDashboard.vue
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
-
-
-
-
- Módulo Personalizado
-
-
- Este es un módulo en blanco listo para personalizar
-
-
-
-
- Módulo en Blanco
- Personaliza este módulo según tus necesidades
-
-
-
-
-
Espacio para tu contenido
-
- Agrega componentes, tablas, formularios o cualquier funcionalidad aquí
-
-
-
-
-
-
-
diff --git a/src/modules/warehouse/components/WarehouseForm.vue b/src/modules/warehouse/components/WarehouseForm.vue
new file mode 100644
index 0000000..78bbbf6
--- /dev/null
+++ b/src/modules/warehouse/components/WarehouseForm.vue
@@ -0,0 +1,424 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Detalles del Almacén
+
+
+
+
+
+ Nombre del Almacén
+
+
+
+
+
+
+
+ Código de Almacén
+
+
+
+
+
+
+
+ Ubicación / Dirección
+
+
+
+
+
+
+
+ Estado
+
+
+
+
+
+
+
+ Capacidad (Metros Cuadrados)
+
+
+
+
+
+
+
+ Teléfono de Contacto
+
+
+
+
+
+
+
+ Correo de Contacto
+
+
+
+
+
+
+
+
+
+ Categorías del Almacén
+
+
+
+
+
+ Categorías Asignadas
+
+
+
+
+ No hay categorías asignadas
+
+
+
+
+
+
+
+
+ Categoría
+
+
+
+
+
+
+ Subcategoría (Opcional)
+
+
+
+
+
+
+
+
+ O Crear Nueva Categoría
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Personal Asignado
+
+
+
+
+
+
+
+
+
+ {{ staff.name }}
+
+
+ {{ staff.role }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/warehouse/components/WarehouseIndex.vue b/src/modules/warehouse/components/WarehouseIndex.vue
new file mode 100644
index 0000000..bf4ed7f
--- /dev/null
+++ b/src/modules/warehouse/components/WarehouseIndex.vue
@@ -0,0 +1,184 @@
+
+
+
+
+
+
+
+
+ Almacenes
+
+
+ Administra, rastrea y organiza todos tus almacenes en un solo lugar.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ slotProps.data.code }}
+
+
+
+
+
+
+
+
+ {{ slotProps.data.name }}
+
+
+ {{ slotProps.data.description }}
+
+
+
+
+
+
+
+
+ {{ slotProps.data.address }}
+
+
+
+
+
+
+
+
+
+
+
+
+ event.stopPropagation()"
+ />
+
+
+
+
+
+
+
diff --git a/src/modules/warehouse/components/index.html b/src/modules/warehouse/components/index.html
new file mode 100644
index 0000000..cb3ee1e
--- /dev/null
+++ b/src/modules/warehouse/components/index.html
@@ -0,0 +1,323 @@
+
+
+
+
+
+
+
+ Warehouse Management - Category Management
+
+
+
+
+
+
+
+
+
+
+
+
+
+ all_inbox
+
+
Warehouse OS
+
+
+
+
+
+
+
+
+ Warehouse Management
+
+
+
+ search
+
+
+
+ notifications
+
+
+
+
+
+
Alex Turner
+
Administrator
+
+
+
+
+
+
+
+
+
Category
+ Management
+
+
+
+
+
+
+ add
+ Add New Category
+
+
+
+
+
+
+ category
+
+
Electronics
+
+
+ chevron_right
+
+
+
+
+
+
+ book
+
+
+ Books & Media
+
+
+ chevron_right
+
+
+
+
+
+ chair
+
+
+ Home & Furniture
+
+
+ chevron_right
+
+
+
+
+
+ checkroom
+
+
+ Apparel
+
+
+ chevron_right
+
+
+
+
+
+
+
+
+
+
+ Category Details
+
Electronics
+
Items related to consumer
+ electronics, computers, and accessories.
+
+
+
+ edit
+
+
+ delete
+
+
+
+
+
+
+
Subcategories (4)
+
+ add
+ Add Subcategory
+
+
+
+
+
+
+
+
Name
+
Date Created
+
Actions
+
+
+
+
+
+
Smartphones
+
2023-03-15
+
+ edit
+ delete
+
+
+
+
+
+
Laptops & Computers
+
2023-03-15
+
+ edit
+ delete
+
+
+
+
+
+
Audio & Headphones
+
2023-04-01
+
+ edit
+ delete
+
+
+
+
+
+
Cameras & Drones
+
2023-05-20
+
+ edit
+ delete
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/modules/warehouse/composables/useWarehouse.ts b/src/modules/warehouse/composables/useWarehouse.ts
deleted file mode 100644
index 41a44db..0000000
--- a/src/modules/warehouse/composables/useWarehouse.ts
+++ /dev/null
@@ -1,82 +0,0 @@
-import { ref, computed } from 'vue';
-import type { Product, WarehouseStats } from '../types/warehouse';
-import { warehouseService } from '../services/warehouseService';
-
-export function useWarehouse() {
- const products = ref([]);
- const stats = ref(null);
- const loading = ref(false);
- const error = ref(null);
-
- // Computed properties
- const lowStockProducts = computed(() =>
- products.value.filter(p => p.quantity < p.minStock)
- );
-
- const totalInventoryValue = computed(() =>
- products.value.reduce((sum, p) => sum + (p.quantity * p.unitPrice), 0)
- );
-
- // Methods
- const loadProducts = async () => {
- loading.value = true;
- error.value = null;
- try {
- products.value = await warehouseService.getProducts();
- } catch (err) {
- error.value = 'Error al cargar productos';
- console.error(err);
- } finally {
- loading.value = false;
- }
- };
-
- const loadStats = async () => {
- loading.value = true;
- error.value = null;
- try {
- stats.value = await warehouseService.getStats();
- } catch (err) {
- error.value = 'Error al cargar estadísticas';
- console.error(err);
- } finally {
- loading.value = false;
- }
- };
-
- const updateQuantity = async (productId: string, quantity: number) => {
- loading.value = true;
- error.value = null;
- try {
- const updated = await warehouseService.updateProductQuantity(productId, quantity);
- if (updated) {
- const index = products.value.findIndex(p => p.id === productId);
- if (index !== -1) {
- products.value[index] = updated;
- }
- }
- } catch (err) {
- error.value = 'Error al actualizar cantidad';
- console.error(err);
- } finally {
- loading.value = false;
- }
- };
-
- return {
- // State
- products,
- stats,
- loading,
- error,
-
- // Computed
- lowStockProducts,
- totalInventoryValue,
-
- // Methods
- loadProducts,
- loadStats,
- updateQuantity
- };
-}
diff --git a/src/modules/warehouse/services/warehouseClasificationService.ts b/src/modules/warehouse/services/warehouseClasificationService.ts
new file mode 100644
index 0000000..a2f1ed5
--- /dev/null
+++ b/src/modules/warehouse/services/warehouseClasificationService.ts
@@ -0,0 +1,51 @@
+import api from '../../../services/api';
+import type {
+ CreateClassificationData,
+ ClassificationsResponse
+} from '../types/warehouse.clasification';
+
+export const warehouseClassificationService = {
+ async getClassifications() {
+ try {
+ const response = await api.get('/api/catalogs/warehouse-classifications');
+ console.log('Classifications response:', response);
+ return response;
+ } catch (error) {
+ console.error('Error fetching classifications:', error);
+ throw error;
+ }
+ },
+
+ async createClassification(data: CreateClassificationData) {
+ try {
+ const response = await api.post('/api/catalogs/warehouse-classifications', data);
+ console.log('Classification created:', response);
+ return response;
+ } catch (error) {
+ console.error('Error creating classification:', error);
+ throw error;
+ }
+ },
+
+ async updateClassification(id: number, data: CreateClassificationData) {
+ try {
+ const response = await api.put(`/api/catalogs/warehouse-classifications/${id}`, data);
+ console.log('Classification updated:', response);
+ return response;
+ } catch (error) {
+ console.error('Error updating classification:', error);
+ throw error;
+ }
+ },
+
+ async deleteClassification(id: number) {
+ try {
+ const response = await api.delete(`/api/catalogs/warehouse-classifications/${id}`);
+ console.log('Classification deleted:', response);
+ return response;
+ } catch (error) {
+ console.error('Error deleting classification:', error);
+ throw error;
+ }
+ }
+};
diff --git a/src/modules/warehouse/services/warehouseService.ts b/src/modules/warehouse/services/warehouseService.ts
index d1d79a0..5c26432 100644
--- a/src/modules/warehouse/services/warehouseService.ts
+++ b/src/modules/warehouse/services/warehouseService.ts
@@ -1,152 +1,15 @@
-import type { Product, WarehouseMovement, WarehouseStats } from '../types/warehouse';
+import api from '../../../services/api';
+import type { WarehousesResponse } from '../types/warehouse';
-// Simulación de datos de ejemplo
-const mockProducts: Product[] = [
- {
- id: '1',
- name: 'Laptop Dell XPS 15',
- sku: 'LAP-001',
- category: 'Electrónica',
- quantity: 45,
- minStock: 10,
- maxStock: 100,
- unitPrice: 1299.99,
- location: 'A-1-01',
- lastUpdated: new Date('2025-11-01')
- },
- {
- id: '2',
- name: 'Mouse Logitech MX Master',
- sku: 'MOU-001',
- category: 'Accesorios',
- quantity: 8,
- minStock: 15,
- maxStock: 50,
- unitPrice: 99.99,
- location: 'B-2-03',
- lastUpdated: new Date('2025-11-03')
- },
- {
- id: '3',
- name: 'Teclado Mecánico RGB',
- sku: 'KEY-001',
- category: 'Accesorios',
- quantity: 120,
- minStock: 20,
- maxStock: 150,
- unitPrice: 149.99,
- location: 'B-2-05',
- lastUpdated: new Date('2025-11-04')
- }
-];
-
-const mockMovements: WarehouseMovement[] = [
- {
- id: '1',
- productId: '1',
- productName: 'Laptop Dell XPS 15',
- type: 'in',
- quantity: 20,
- reason: 'Compra a proveedor',
- date: new Date('2025-11-01'),
- user: 'Admin'
- },
- {
- id: '2',
- productId: '2',
- productName: 'Mouse Logitech MX Master',
- type: 'out',
- quantity: 5,
- reason: 'Venta',
- date: new Date('2025-11-03'),
- user: 'Admin'
- }
-];
-
-/**
- * Servicio para gestionar operaciones del almacén
- */
export const warehouseService = {
- /**
- * Obtiene todos los productos del inventario
- */
- async getProducts(): Promise {
- // Simular llamada API
- return new Promise((resolve) => {
- setTimeout(() => resolve(mockProducts), 500);
- });
- },
-
- /**
- * Obtiene un producto por ID
- */
- async getProductById(id: string): Promise {
- return new Promise((resolve) => {
- setTimeout(() => {
- const product = mockProducts.find(p => p.id === id);
- resolve(product || null);
- }, 300);
- });
- },
-
- /**
- * Obtiene los movimientos del almacén
- */
- async getMovements(): Promise {
- return new Promise((resolve) => {
- setTimeout(() => resolve(mockMovements), 500);
- });
- },
-
- /**
- * Obtiene las estadísticas del almacén
- */
- async getStats(): Promise {
- return new Promise((resolve) => {
- setTimeout(() => {
- const stats: WarehouseStats = {
- totalProducts: mockProducts.reduce((sum, p) => sum + p.quantity, 0),
- totalValue: mockProducts.reduce((sum, p) => sum + (p.quantity * p.unitPrice), 0),
- lowStockItems: mockProducts.filter(p => p.quantity < p.minStock).length,
- recentMovements: mockMovements.length
- };
- resolve(stats);
- }, 300);
- });
- },
-
- /**
- * Crea un nuevo movimiento de inventario
- */
- async createMovement(movement: Omit): Promise {
- return new Promise((resolve) => {
- setTimeout(() => {
- const newMovement: WarehouseMovement = {
- ...movement,
- id: String(mockMovements.length + 1),
- date: new Date()
- };
- mockMovements.push(newMovement);
- resolve(newMovement);
- }, 500);
- });
- },
-
- /**
- * Actualiza la cantidad de un producto
- */
- async updateProductQuantity(productId: string, quantity: number): Promise {
- return new Promise((resolve) => {
- setTimeout(() => {
- const product = mockProducts.find(p => p.id === productId);
- if (product) {
- product.quantity = quantity;
- product.lastUpdated = new Date();
- resolve(product);
- } else {
- resolve(null);
- }
- }, 500);
- });
+ async getWarehouses() {
+ try {
+ const response = await api.get('/api/warehouses');
+ console.log('Warehouses response:', response);
+ return response;
+ } catch (error) {
+ console.error('Error fetching warehouses:', error);
+ throw error;
+ }
}
};
diff --git a/src/modules/warehouse/types/warehouse.clasification.d.ts b/src/modules/warehouse/types/warehouse.clasification.d.ts
new file mode 100644
index 0000000..f290776
--- /dev/null
+++ b/src/modules/warehouse/types/warehouse.clasification.d.ts
@@ -0,0 +1,46 @@
+export interface CreateClassificationData {
+ code: string;
+ name: string;
+ description: string;
+ parent_id: number | null;
+}
+
+export interface Classification {
+ id: number;
+ code: string;
+ name: string;
+ description: string;
+ is_active: boolean;
+ parent_id: number | null;
+ created_at: string;
+ updated_at: string;
+ deleted_at: string | null;
+ children?: Classification[];
+}
+
+export interface ClassificationsPagination {
+ current_page: number;
+ data: Classification[];
+ first_page_url: string;
+ from: number | null;
+ last_page: number;
+ last_page_url: string;
+ links: {
+ url: string | null;
+ label: string;
+ active: boolean;
+ }[];
+ next_page_url: string | null;
+ path: string;
+ per_page: number;
+ prev_page_url: string | null;
+ to: number | null;
+ total: number;
+}
+
+export interface ClassificationsResponse {
+ status: string;
+ data: {
+ warehouse_classifications: ClassificationsPagination;
+ };
+}
diff --git a/src/modules/warehouse/types/warehouse.d.ts b/src/modules/warehouse/types/warehouse.d.ts
index e6f76e3..f5b265b 100644
--- a/src/modules/warehouse/types/warehouse.d.ts
+++ b/src/modules/warehouse/types/warehouse.d.ts
@@ -1,37 +1,41 @@
-// Types para el módulo Warehouse
-export interface Product {
- id: string;
+export interface Warehouse {
+ id: number;
+ code: string;
name: string;
- sku: string;
- category: string;
- quantity: number;
- minStock: number;
- maxStock: number;
- unitPrice: number;
- location: string;
- lastUpdated: Date;
+ description: string;
+ address: string;
+ is_active: boolean;
+ created_at: string;
+ updated_at: string;
+ deleted_at: string | null;
+ classifications: any[];
}
-export interface WarehouseMovement {
- id: string;
- productId: string;
- productName: string;
- type: 'in' | 'out' | 'adjustment';
- quantity: number;
- reason: string;
- date: Date;
- user: string;
+export interface WarehousePagination {
+ current_page: number;
+ data: Warehouse[];
+ first_page_url: string;
+ from: number;
+ last_page: number;
+ last_page_url: string;
+ links: PaginationLink[];
+ next_page_url: string | null;
+ path: string;
+ per_page: number;
+ prev_page_url: string | null;
+ to: number;
+ total: number;
}
-export interface WarehouseStats {
- totalProducts: number;
- totalValue: number;
- lowStockItems: number;
- recentMovements: number;
+export interface PaginationLink {
+ url: string | null;
+ label: string;
+ active: boolean;
}
-export interface InventoryFilter {
- search?: string;
- category?: string;
- lowStock?: boolean;
+export interface WarehousesResponse {
+ status: string;
+ data: {
+ warehouses: WarehousePagination;
+ };
}
diff --git a/src/router/index.ts b/src/router/index.ts
index 649df1f..de02bd1 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -4,7 +4,10 @@ import { useAuth } from '../modules/auth/composables/useAuth';
// Importar vistas
import Login from '../modules/auth/components/Login.vue';
import MainLayout from '../MainLayout.vue';
-import WarehouseDashboard from '../modules/warehouse/components/WarehouseDashboard.vue';
+import WarehouseIndex from '../modules/warehouse/components/WarehouseIndex.vue';
+import WarehouseForm from '../modules/warehouse/components/WarehouseForm.vue';
+import WarehouseCategory from '../modules/warehouse/components/WarehouseCategory.vue';
+import WarehouseClassification from '../modules/warehouse/components/WarehouseClassification.vue';
const routes: RouteRecordRaw[] = [
{
@@ -26,7 +29,7 @@ const routes: RouteRecordRaw[] = [
{
path: '',
name: 'Dashboard',
- component: WarehouseDashboard,
+ component: WarehouseIndex,
meta: {
title: 'Dashboard',
requiresAuth: true
@@ -35,14 +38,53 @@ const routes: RouteRecordRaw[] = [
{
path: 'warehouse',
name: 'Warehouse',
- redirect: '/warehouse/inventory',
+ meta: {
+ title: 'Almacén',
+ requiresAuth: true
+ },
children: [
+ {
+ path: '',
+ name: 'WarehouseHome',
+ component: WarehouseIndex,
+ meta: {
+ title: 'Almacén - Dashboard',
+ requiresAuth: true
+ }
+ },
+ {
+ path: 'create',
+ name: 'WarehouseCreate',
+ component: WarehouseForm,
+ meta: {
+ title: 'Crear Almacén',
+ requiresAuth: true
+ }
+ },
+ {
+ path: 'classifications',
+ name: 'WarehouseClassifications',
+ component: WarehouseClassification,
+ meta: {
+ title: 'Clasificaciones de Almacén',
+ requiresAuth: true
+ }
+ },
{
path: 'inventory',
name: 'WarehouseInventory',
- component: WarehouseDashboard,
+ component: WarehouseIndex,
meta: {
- title: 'Inventario',
+ title: 'Inventario - Almacén',
+ requiresAuth: true
+ }
+ },
+ {
+ path: 'movements',
+ name: 'WarehouseMovements',
+ component: WarehouseIndex,
+ meta: {
+ title: 'Movimientos - Almacén',
requiresAuth: true
}
}
diff --git a/src/stores/warehouseStore.ts b/src/stores/warehouseStore.ts
new file mode 100644
index 0000000..2e5ca09
--- /dev/null
+++ b/src/stores/warehouseStore.ts
@@ -0,0 +1,85 @@
+import { defineStore } from 'pinia';
+import { ref, computed } from 'vue';
+import { warehouseService } from '../modules/warehouse/services/warehouseService';
+import type { Warehouse } from '../modules/warehouse/types/warehouse';
+
+export const useWarehouseStore = defineStore('warehouse', () => {
+ // State
+ const warehouses = ref([]);
+ const loading = ref(false);
+ const loaded = ref(false);
+ const error = ref(null);
+
+ // Getters
+ const activeWarehouses = computed(() =>
+ warehouses.value.filter(w => w.is_active)
+ );
+
+ const inactiveWarehouses = computed(() =>
+ warehouses.value.filter(w => !w.is_active)
+ );
+
+ const warehouseCount = computed(() => warehouses.value.length);
+
+ const getWarehouseById = computed(() => {
+ return (id: number) => warehouses.value.find(w => w.id === id);
+ });
+
+ const getWarehouseByCode = computed(() => {
+ return (code: string) => warehouses.value.find(w => w.code === code);
+ });
+
+ // Actions
+ const fetchWarehouses = async (force = false) => {
+ // Si ya están cargados y no se fuerza la recarga, no hacer nada
+ if (loaded.value && !force) {
+ console.log('Warehouses already loaded from store');
+ return;
+ }
+
+ try {
+ loading.value = true;
+ error.value = null;
+
+ const response = await warehouseService.getWarehouses();
+ warehouses.value = response.data.data.warehouses.data;
+ loaded.value = true;
+
+ console.log('Warehouses loaded into store:', warehouses.value.length);
+ } catch (err) {
+ error.value = err instanceof Error ? err.message : 'Error loading warehouses';
+ console.error('Error in warehouse store:', err);
+ throw err;
+ } finally {
+ loading.value = false;
+ }
+ };
+
+ const refreshWarehouses = () => {
+ return fetchWarehouses(true);
+ };
+
+ const clearWarehouses = () => {
+ warehouses.value = [];
+ loaded.value = false;
+ error.value = null;
+ };
+
+ return {
+ // State
+ warehouses,
+ loading,
+ loaded,
+ error,
+ // Getters
+ activeWarehouses,
+ inactiveWarehouses,
+ warehouseCount,
+ getWarehouseById,
+ getWarehouseByCode,
+ // Actions
+ fetchWarehouses,
+ refreshWarehouses,
+ clearWarehouses,
+ };
+});