edgar.mendez 1189b7b02e 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
2026-03-21 20:04:40 -06:00

181 lines
7.3 KiB
Vue

<script setup lang="ts">
import { ref, computed } from 'vue';
import { useAuth } from '../composables/useAuth';
import { useRoute, useRouter } from 'vue-router';
const router = useRouter();
const route = useRoute();
const { login, isLoading } = useAuth();
const email = ref('');
const password = ref('');
const showPassword = ref(false);
const errorMessage = ref('');
const isFormValid = computed(() => {
return email.value.trim() !== '' && password.value.trim() !== '';
});
const handleLogin = async () => {
errorMessage.value = '';
const result = await login({
email: email.value,
password: password.value
});
if (result.success) {
const requestedRedirect = typeof route.query.redirect === 'string' ? route.query.redirect : '/';
const redirect = requestedRedirect.startsWith('/') ? requestedRedirect : '/';
router.push(redirect);
} else {
errorMessage.value = result.error || 'Error al iniciar sesión';
}
};
const handleKeyPress = (event: KeyboardEvent) => {
if (event.key === 'Enter' && isFormValid.value) {
handleLogin();
}
};
</script>
<template>
<div class="flex min-h-screen bg-surface-50 dark:bg-surface-950">
<!-- Left Column: Image Panel -->
<div
class="relative hidden lg:flex lg:w-1/2 flex-col justify-between p-8 text-white bg-cover bg-center bg-no-repeat"
style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuBTdTKvMQYppS4BwZhrgylD8oKxLJa13js2PIRUwAQF5XbEqqNU2VCYyvJEQTWdKdoCTYHiAm_RGoobkJ3Rt4xd-XJUcshYKfCBrF0fZcBJOFR9UJ0Vh2WuLIA8g_xmKA8Fn7hdh4KPqJC7X_IOg5UPi5RRzqxB-Xn2tMbaEk-n1h8mYXfnKSIV7C0up-YreeZdP9GeznZN6DCzjy8TLyxw03gXdmhHziZC6hzahwWemMrtS8W5sX028-G1tnxivu1v2o8oETFyh5Wi')"
>
<!-- Overlay oscuro -->
<div class="absolute inset-0 bg-black/50"></div>
<!-- Logo y nombre -->
<div class="relative z-10 flex items-center gap-3">
<i class="pi pi-box text-4xl"></i>
<span class="text-xl font-bold">Golscontrols ERP</span>
</div>
<!-- Texto principal -->
<div class="relative z-10">
<h1 class="text-4xl font-black leading-tight tracking-tight">
Sistema Integral de Gestión Empresarial
</h1>
<p class="mt-2 max-w-md text-lg text-white/80">
Optimiza tus operaciones, gestiona inventarios y aumenta la eficiencia desde un solo panel.
</p>
</div>
</div>
<!-- Right Column: Form Panel -->
<div class="flex w-full lg:w-1/2 flex-col items-center justify-center px-4 py-12">
<div class="w-full max-w-md space-y-8">
<!-- Header -->
<div>
<h1 class="text-3xl font-bold tracking-tight text-surface-900 dark:text-white">
Bienvenido de nuevo
</h1>
<p class="mt-2 text-base text-surface-600 dark:text-surface-300">
Ingresa tus credenciales para acceder a tu cuenta.
</p>
</div>
<!-- Mensaje de error -->
<Message
v-if="errorMessage"
severity="error"
:closable="true"
@close="errorMessage = ''"
>
{{ errorMessage }}
</Message>
<!-- Info de prueba (solo para desarrollo) -->
<Message severity="info" :closable="false">
<div class="text-sm">
<p class="font-semibold mb-1">Credenciales de prueba:</p>
<p><strong>Email:</strong> admin@golsystems.com.mx</p>
<p><strong>Password:</strong> P8YeFQggR06r</p>
</div>
</Message>
<!-- Formulario -->
<form @submit.prevent="handleLogin" class="space-y-6">
<!-- Email -->
<div class="flex flex-col gap-2">
<label for="email" class="text-sm font-medium text-surface-900 dark:text-surface-200">
Correo Electrónico
</label>
<IconField>
<InputIcon class="pi pi-user" />
<InputText
id="email"
v-model="email"
type="email"
placeholder="you@example.com"
:disabled="isLoading"
@keypress="handleKeyPress"
class="w-full"
autocomplete="email"
size="large"
/>
</IconField>
</div>
<!-- Password -->
<div class="flex flex-col gap-2">
<label for="password" class="text-sm font-medium text-surface-900 dark:text-surface-200">
Contraseña
</label>
<IconField>
<InputIcon class="pi pi-lock" />
<InputText
id="password"
v-model="password"
:type="showPassword ? 'text' : 'password'"
placeholder="Ingresa tu contraseña"
:disabled="isLoading"
@keypress="handleKeyPress"
class="w-full pr-10"
autocomplete="current-password"
size="large"
/>
<button
type="button"
@click="showPassword = !showPassword"
class="absolute right-3 top-1/2 -translate-y-1/2 text-surface-400 hover:text-surface-600 dark:hover:text-surface-200"
tabindex="-1"
>
<i :class="showPassword ? 'pi pi-eye-slash' : 'pi pi-eye'"></i>
</button>
</IconField>
</div>
<!-- Botón de Login -->
<Button
type="submit"
label="Iniciar Sesión"
:loading="isLoading"
:disabled="!isFormValid || isLoading"
class="w-full"
size="large"
severity="primary"
/>
</form>
<!-- Copyright -->
<p class="text-center text-sm text-surface-500 dark:text-surface-400">
© 2025 Grupo Golsystems, Todos los derechos reservados.
</p>
</div>
</div>
</div>
</template>
<style scoped>
/* Ajuste para el botón de toggle password */
.p-iconfield {
position: relative;
}
</style>