From 1189b7b02e4634399e55e8b79f159d86e3fc6266 Mon Sep 17 00:00:00 2001 From: "edgar.mendez" Date: Sat, 21 Mar 2026 20:04:40 -0600 Subject: [PATCH] feat(auth): enhance authentication service with error handling and session management - Added methods to normalize permissions and roles from API responses. - Implemented a centralized error handling method for authentication errors. - Updated API endpoints for login, registration, and user profile management. - Introduced session refresh functionality to retrieve user roles and permissions. feat(catalog): improve companies and units management with permissions and filters - Integrated permission checks for creating, updating, and deleting companies. - Added user role and permission checks to the Companies component. - Enhanced the Units component with search and status filters. - Refactored unit creation and update logic to handle validation errors. fix(catalog): update unit measure services and mapping logic - Improved API service methods for fetching, creating, and updating units of measure. - Added mapping functions to convert API responses to internal data structures. - Enhanced error handling in unit measure services. chore(auth): refactor authentication storage utilities - Created utility functions for managing authentication tokens and user data in local storage. - Updated API interceptor to use new storage utility functions for session management. style: clean up code formatting and improve readability across components and services --- src/components/layout/Sidebar.vue | 59 ++++++- src/main.ts | 12 +- src/modules/auth/components/Login.vue | 7 +- src/modules/auth/composables/useAuth.ts | 97 ++++++++-- src/modules/auth/services/authService.ts | 165 ++++++++++++++---- src/modules/auth/utils/authStorage.ts | 66 +++++++ .../components/companies/Companies.vue | 73 +++++++- .../catalog/components/units/Units.vue | 148 +++++++++++++--- .../catalog/components/units/UnitsForm.vue | 82 +++++++-- .../catalog/services/sat-units.services.ts | 11 +- .../catalog/services/unit-measure.mapper.ts | 40 +++++ .../catalog/services/unit-measure.services.ts | 96 ++++++++-- .../catalog/stores/unitOfMeasureStore.ts | 64 ++++--- .../catalog/types/unit-measure.interfaces.ts | 44 +++-- src/services/api.ts | 17 +- 15 files changed, 823 insertions(+), 158 deletions(-) create mode 100644 src/modules/auth/utils/authStorage.ts create mode 100644 src/modules/catalog/services/unit-measure.mapper.ts diff --git a/src/components/layout/Sidebar.vue b/src/components/layout/Sidebar.vue index dc22bfd..9a34fb0 100644 --- a/src/components/layout/Sidebar.vue +++ b/src/components/layout/Sidebar.vue @@ -1,15 +1,18 @@ diff --git a/src/modules/catalog/components/units/Units.vue b/src/modules/catalog/components/units/Units.vue index e6e4568..ae09f1a 100644 --- a/src/modules/catalog/components/units/Units.vue +++ b/src/modules/catalog/components/units/Units.vue @@ -12,6 +12,8 @@ import Tag from 'primevue/tag'; import Toast from 'primevue/toast'; import ConfirmDialog from 'primevue/confirmdialog'; import ProgressSpinner from 'primevue/progressspinner'; +import InputText from 'primevue/inputtext'; +import Select from 'primevue/select'; import { useUnitOfMeasureStore } from '../../stores/unitOfMeasureStore'; import UnitsForm from './UnitsForm.vue'; import type { UnitOfMeasure, CreateUnitOfMeasureData } from '../../types/unit-measure.interfaces'; @@ -36,18 +38,40 @@ const home = ref({ const showDialog = ref(false); const isEditing = ref(false); const selectedUnit = ref(null); +const unitsFormRef = ref | null>(null); +const search = ref(''); +const statusFilter = ref<'all' | 'active' | 'inactive'>('all'); // Computed const units = computed(() => unitStore.units); const loading = computed(() => unitStore.loading); +const statusFilterOptions = [ + { label: 'Todas', value: 'all' }, + { label: 'Activas', value: 'active' }, + { label: 'Inactivas', value: 'inactive' }, +]; -const getStatusConfig = (isActive: number) => { - return isActive === 1 +const getStatusConfig = (isActive: boolean) => { + return isActive ? { label: 'Activa', severity: 'success' } : { label: 'Inactiva', severity: 'secondary' }; }; // Methods +const buildFilters = () => ({ + paginate: true, + per_page: 25, + search: search.value.trim(), + is_active: statusFilter.value === 'all' ? undefined : statusFilter.value === 'active', +}); + +const loadUnits = async (force = false) => { + await unitStore.fetchUnits({ + force, + ...buildFilters(), + }); +}; + const openCreateDialog = () => { isEditing.value = false; selectedUnit.value = null; @@ -60,33 +84,67 @@ const openEditDialog = (unit: UnitOfMeasure) => { showDialog.value = true; }; +const handleCreateUnit = async (data: CreateUnitOfMeasureData) => { + await unitStore.createUnit(data); + toast.add({ + severity: 'success', + summary: 'Creación Exitosa', + detail: 'La unidad de medida ha sido creada correctamente.', + life: 3000, + }); +}; + +const handleUpdateUnit = async (id: number, data: CreateUnitOfMeasureData) => { + await unitStore.updateUnit(id, data); + toast.add({ + severity: 'success', + summary: 'Actualización Exitosa', + detail: 'La unidad de medida ha sido actualizada correctamente.', + life: 3000, + }); +}; + 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 - }); + await handleUpdateUnit(selectedUnit.value.id, data); } else { - await unitStore.createUnit(data); - toast.add({ - severity: 'success', - summary: 'Creación Exitosa', - detail: 'La unidad de medida ha sido creada correctamente.', - life: 3000 - }); + await handleCreateUnit(data); } + showDialog.value = false; + selectedUnit.value = null; + unitsFormRef.value?.resetSubmitting(); } catch (error) { console.error('Error saving unit:', error); + + const requestError = error as { + response?: { + status?: number; + data?: { + errors?: Record; + message?: string; + }; + }; + }; + + if (requestError.response?.status === 422 && requestError.response.data?.errors) { + unitsFormRef.value?.setValidationErrors(requestError.response.data.errors); + toast.add({ + severity: 'warn', + summary: 'Errores de validación', + detail: 'Corrige los campos marcados para continuar.', + life: 4000, + }); + return; + } + + unitsFormRef.value?.resetSubmitting(); toast.add({ severity: 'error', summary: 'Error', - detail: 'No se pudo guardar la unidad de medida. Por favor, intenta nuevamente.', - life: 3000 + detail: requestError.response?.data?.message || 'No se pudo guardar la unidad de medida. Por favor, intenta nuevamente.', + life: 3000, }); } }; @@ -106,11 +164,12 @@ const confirmDelete = (unit: UnitOfMeasure) => { const deleteUnit = async (id: number) => { try { await unitStore.deleteUnit(id); + await loadUnits(true); toast.add({ severity: 'success', summary: 'Eliminación Exitosa', detail: 'La unidad de medida ha sido eliminada correctamente.', - life: 3000 + life: 3000, }); } catch (error) { console.error('Error deleting unit:', error); @@ -118,14 +177,24 @@ const deleteUnit = async (id: number) => { severity: 'error', summary: 'Error', detail: 'No se pudo eliminar la unidad de medida. Puede estar en uso.', - life: 3000 + life: 3000, }); } }; +const applyFilters = async () => { + await loadUnits(true); +}; + +const clearFilters = async () => { + search.value = ''; + statusFilter.value = 'all'; + await loadUnits(true); +}; + // Lifecycle onMounted(async () => { - await unitStore.fetchUnits(); + await loadUnits(); }); @@ -169,6 +238,42 @@ onMounted(async () => {