feat: enhance authentication flow with improved user data handling and API integration
This commit is contained in:
parent
e1521ef9c7
commit
857c149b87
9
.env.example
Normal file
9
.env.example
Normal file
@ -0,0 +1,9 @@
|
||||
# API Configuration
|
||||
VITE_API_URL=http://localhost:3000/api
|
||||
|
||||
# Environment
|
||||
VITE_APP_ENV=development
|
||||
|
||||
# App Configuration
|
||||
VITE_APP_NAME=GOLS Control
|
||||
VITE_APP_VERSION=1.0.0
|
||||
2
components.d.ts
vendored
2
components.d.ts
vendored
@ -28,6 +28,8 @@ declare module 'vue' {
|
||||
KpiCard: typeof import('./src/components/shared/KpiCard.vue')['default']
|
||||
Menu: typeof import('primevue/menu')['default']
|
||||
Message: typeof import('primevue/message')['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']
|
||||
TopBar: typeof import('./src/components/layout/TopBar.vue')['default']
|
||||
|
||||
@ -17,8 +17,8 @@ const toggleUserMenu = (event: Event) => {
|
||||
};
|
||||
|
||||
// Función de logout
|
||||
const handleLogout = () => {
|
||||
logout();
|
||||
const handleLogout = async () => {
|
||||
await logout();
|
||||
router.push('/login');
|
||||
};
|
||||
|
||||
@ -129,10 +129,10 @@ const userMenuItems = ref([
|
||||
aria-controls="user_menu"
|
||||
>
|
||||
<div class="w-8 h-8 rounded-full bg-primary flex items-center justify-center text-white text-sm font-semibold">
|
||||
{{ user?.name.split(' ').map(n => n[0]).join('').toUpperCase() || 'U' }}
|
||||
{{ user?.name?.charAt(0).toUpperCase() || 'U' }}
|
||||
</div>
|
||||
<span class="text-sm font-medium text-surface-900 dark:text-surface-0 hidden md:block">
|
||||
{{ user?.name || 'Usuario' }}
|
||||
{{ user?.full_name || user?.name || 'Usuario' }}
|
||||
</span>
|
||||
<i class="pi pi-angle-down text-sm text-surface-500 hidden md:block"></i>
|
||||
</button>
|
||||
@ -147,7 +147,7 @@ const userMenuItems = ref([
|
||||
>
|
||||
<template #start>
|
||||
<div class="px-4 py-3 border-b border-surface-200 dark:border-surface-700">
|
||||
<p class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ user?.name }}</p>
|
||||
<p class="text-sm font-semibold text-surface-900 dark:text-surface-0">{{ user?.full_name || user?.name }}</p>
|
||||
<p class="text-xs text-surface-500 dark:text-surface-400">{{ user?.email }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -8,7 +8,6 @@ const { login, isLoading } = useAuth();
|
||||
|
||||
const email = ref('');
|
||||
const password = ref('');
|
||||
const remember = ref(false);
|
||||
const showPassword = ref(false);
|
||||
const errorMessage = ref('');
|
||||
|
||||
@ -92,8 +91,8 @@ const handleKeyPress = (event: KeyboardEvent) => {
|
||||
<Message severity="info" :closable="false">
|
||||
<div class="text-sm">
|
||||
<p class="font-semibold mb-1">Credenciales de prueba:</p>
|
||||
<p><strong>Email:</strong> admin@gols.com</p>
|
||||
<p><strong>Password:</strong> admin123</p>
|
||||
<p><strong>Email:</strong> admin@golsystems.com.mx</p>
|
||||
<p><strong>Password:</strong> P8YeFQggR06r</p>
|
||||
</div>
|
||||
</Message>
|
||||
|
||||
@ -149,23 +148,6 @@ const handleKeyPress = (event: KeyboardEvent) => {
|
||||
</IconField>
|
||||
</div>
|
||||
|
||||
<!-- Remember me y Forgot password -->
|
||||
<!-- <div class="flex items-center justify-between">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox
|
||||
inputId="remember-me"
|
||||
v-model="remember"
|
||||
:binary="true"
|
||||
/>
|
||||
<label for="remember-me" class="text-sm text-surface-700 dark:text-surface-300 cursor-pointer">
|
||||
Remember Me
|
||||
</label>
|
||||
</div>
|
||||
<a href="#" class="text-sm font-medium text-primary hover:text-primary/80">
|
||||
Forgot Password?
|
||||
</a>
|
||||
</div> -->
|
||||
|
||||
<!-- Botón de Login -->
|
||||
<Button
|
||||
type="submit"
|
||||
|
||||
@ -1,11 +1,46 @@
|
||||
import { ref, computed } from 'vue';
|
||||
import authService from '../services/authService';
|
||||
|
||||
export interface Role {
|
||||
id: number;
|
||||
name: string;
|
||||
guard_name: string;
|
||||
description: string;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export interface User {
|
||||
id: number;
|
||||
name: string;
|
||||
paternal: string;
|
||||
maternal: string;
|
||||
phone: string | null;
|
||||
email: string;
|
||||
avatar?: string;
|
||||
role: string;
|
||||
email_verified_at: string | null;
|
||||
current_team_id: number | null;
|
||||
profile_photo_path: string | null;
|
||||
department_id: number | null;
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
deleted_at: string | null;
|
||||
rfc: string | null;
|
||||
curp: string | null;
|
||||
nss: string | null;
|
||||
hire_date: string | null;
|
||||
gender_ek: string | null;
|
||||
birthdate: string | null;
|
||||
entity_id: number | null;
|
||||
municipality_id: number | null;
|
||||
marital_status_ek: string | null;
|
||||
children: number | null;
|
||||
address: string | null;
|
||||
vacation_days_available: number;
|
||||
status_ek: string;
|
||||
full_name: string;
|
||||
last_name: string;
|
||||
profile_photo_url: string;
|
||||
roles?: Role[];
|
||||
}
|
||||
|
||||
export interface LoginCredentials {
|
||||
@ -20,63 +55,68 @@ const isLoading = ref(false);
|
||||
|
||||
// Inicializar desde localStorage
|
||||
const initAuth = () => {
|
||||
const storedToken = localStorage.getItem('auth_token');
|
||||
const storedUser = localStorage.getItem('auth_user');
|
||||
|
||||
if (storedToken && storedUser) {
|
||||
token.value = storedToken;
|
||||
user.value = JSON.parse(storedUser);
|
||||
try {
|
||||
const storedToken = localStorage.getItem('auth_token');
|
||||
const storedUser = localStorage.getItem('auth_user');
|
||||
|
||||
if (storedToken && storedUser && storedUser !== 'undefined') {
|
||||
token.value = storedToken;
|
||||
user.value = JSON.parse(storedUser);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error al inicializar autenticación:', error);
|
||||
// Limpiar localStorage si hay datos corruptos
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('auth_user');
|
||||
}
|
||||
};
|
||||
|
||||
// Computeds
|
||||
const isAuthenticated = computed(() => !!user.value && !!token.value);
|
||||
|
||||
// Función de login (simulada)
|
||||
// Función de login usando authService
|
||||
const login = async (credentials: LoginCredentials): Promise<{ success: boolean; error?: string }> => {
|
||||
isLoading.value = true;
|
||||
|
||||
try {
|
||||
// Simular llamada a API
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
// Llamar al servicio de autenticación
|
||||
const response = await authService.login(credentials);
|
||||
|
||||
// Validación simple (en producción esto vendría del backend)
|
||||
if (credentials.email === 'admin@gols.com' && credentials.password === 'admin123') {
|
||||
const mockUser: User = {
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
email: credentials.email,
|
||||
avatar: '',
|
||||
role: 'admin'
|
||||
};
|
||||
|
||||
const mockToken = 'mock-jwt-token-' + Date.now();
|
||||
|
||||
// Guardar en estado
|
||||
user.value = mockUser;
|
||||
token.value = mockToken;
|
||||
|
||||
// Persistir en localStorage
|
||||
localStorage.setItem('auth_token', mockToken);
|
||||
localStorage.setItem('auth_user', JSON.stringify(mockUser));
|
||||
|
||||
return { success: true };
|
||||
} else {
|
||||
return { success: false, error: 'Credenciales inválidas' };
|
||||
}
|
||||
} catch (error) {
|
||||
return { success: false, error: 'Error al iniciar sesión' };
|
||||
// Guardar en estado
|
||||
user.value = response.user;
|
||||
token.value = response.token;
|
||||
|
||||
// Persistir en localStorage
|
||||
localStorage.setItem('auth_token', response.token);
|
||||
localStorage.setItem('auth_user', JSON.stringify(response.user));
|
||||
|
||||
return { success: true };
|
||||
} catch (error: any) {
|
||||
console.error('Error en login:', error);
|
||||
return {
|
||||
success: false,
|
||||
error: error.message || 'Error al iniciar sesión'
|
||||
};
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// Función de logout
|
||||
const logout = () => {
|
||||
const logout = async () => {
|
||||
// Limpiar estado local inmediatamente para reactividad
|
||||
user.value = null;
|
||||
token.value = null;
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('auth_user');
|
||||
|
||||
try {
|
||||
// Notificar al backend (en segundo plano)
|
||||
await authService.logout();
|
||||
} catch (error) {
|
||||
console.error('Error al cerrar sesión en backend:', error);
|
||||
// El logout local ya se completó, solo loggeamos el error
|
||||
}
|
||||
};
|
||||
|
||||
// Composable para usar en componentes
|
||||
|
||||
@ -1,9 +1,13 @@
|
||||
import api from '../../../services/api';
|
||||
import type { User, LoginCredentials } from '../composables/useAuth';
|
||||
import type { User, LoginCredentials, Role } from '../composables/useAuth';
|
||||
|
||||
export interface LoginResponse {
|
||||
user: User;
|
||||
token: string;
|
||||
status: string;
|
||||
data: {
|
||||
user: User;
|
||||
userRoles: Role[];
|
||||
token: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface RegisterData {
|
||||
@ -17,31 +21,29 @@ class AuthService {
|
||||
/**
|
||||
* Iniciar sesión
|
||||
*/
|
||||
async login(credentials: LoginCredentials): Promise<LoginResponse> {
|
||||
async login(credentials: LoginCredentials): Promise<{ user: User; token: string }> {
|
||||
try {
|
||||
// En producción, esto haría una llamada real al backend
|
||||
// const response = await api.post<LoginResponse>('/auth/login', credentials);
|
||||
|
||||
// Simulación para desarrollo
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
if (credentials.email === 'admin@gols.com' && credentials.password === 'admin123') {
|
||||
resolve({
|
||||
user: {
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
email: credentials.email,
|
||||
avatar: '',
|
||||
role: 'admin'
|
||||
},
|
||||
token: 'mock-jwt-token-' + Date.now()
|
||||
});
|
||||
} else {
|
||||
reject(new Error('Credenciales inválidas'));
|
||||
}
|
||||
}, 1000);
|
||||
const response = await api.post<LoginResponse>('/api/auth/login', {
|
||||
email: credentials.email,
|
||||
password: credentials.password
|
||||
});
|
||||
|
||||
// Retornar solo user y token para mantener compatibilidad con useAuth
|
||||
return {
|
||||
user: response.data.data.user,
|
||||
token: response.data.data.token
|
||||
};
|
||||
} catch (error: any) {
|
||||
console.error('Error en login:', error);
|
||||
|
||||
// Manejar errores de validación (422)
|
||||
if (error.response?.status === 422 && error.response?.data?.errors) {
|
||||
const errors = error.response.data.errors;
|
||||
const firstError = Object.values(errors)[0] as string[];
|
||||
throw new Error(firstError[0] || error.response.data.message);
|
||||
}
|
||||
|
||||
// Manejar otros errores
|
||||
throw new Error(error.response?.data?.message || 'Error al iniciar sesión');
|
||||
}
|
||||
}
|
||||
@ -63,10 +65,21 @@ class AuthService {
|
||||
*/
|
||||
async logout(): Promise<void> {
|
||||
try {
|
||||
// Notificar al backend que se cerró sesión
|
||||
await api.post('/auth/logout');
|
||||
} catch (error) {
|
||||
const response = await api.post<{
|
||||
status: string;
|
||||
data: {
|
||||
is_revoked: boolean;
|
||||
};
|
||||
}>('/api/auth/logout');
|
||||
|
||||
console.log('Respuesta logout:', response.data);
|
||||
|
||||
if (response.data.status === 'success' && response.data.data.is_revoked) {
|
||||
console.log('Token revocado exitosamente');
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('Error al cerrar sesión:', error);
|
||||
// No lanzar error para que el logout local continúe aunque falle el backend
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user