edgar.mendez 1cf8cc3aa5 feat: add unit equivalence management functionality
- Implemented UnitsEquivalenceModal for managing unit equivalences.
- Added unit equivalence store and service for CRUD operations.
- Integrated unit equivalence management into Units.vue component.
- Created new HTML page for unit equivalence management layout.
- Defined TypeScript interfaces for unit equivalences.
- Enhanced user permissions for managing unit equivalences.
2026-03-23 21:43:02 -06:00

246 lines
7.2 KiB
TypeScript

import api from '../../../services/api';
import type { User, LoginCredentials, Role } from '../composables/useAuth';
export interface LoginResponse {
status: string;
data: {
user: User;
userRoles: Role[];
token: string;
};
}
export interface RegisterData {
name: string;
email: string;
password: string;
password_confirmation: string;
}
interface SessionRefreshData {
user: User;
roles: Role[];
permissions: string[];
}
class AuthService {
private extractArrayFromResponse(payload: unknown): unknown[] {
if (Array.isArray(payload)) {
return payload;
}
if (!payload || typeof payload !== 'object') {
return [];
}
const root = payload as Record<string, unknown>;
if (Array.isArray(root.data)) {
return root.data;
}
if (root.data && typeof root.data === 'object') {
const nested = root.data as Record<string, unknown>;
if (Array.isArray(nested.permissions)) {
return nested.permissions;
}
if (Array.isArray(nested.roles)) {
return nested.roles;
}
}
return [];
}
private normalizePermissions(payload: unknown): string[] {
const items = this.extractArrayFromResponse(payload);
return items
.map((item) => {
if (typeof item === 'string') {
return item;
}
if (item && typeof item === 'object' && typeof (item as Record<string, unknown>).name === 'string') {
return (item as Record<string, string>).name;
}
return null;
})
.filter((permission): permission is string => Boolean(permission));
}
private normalizeRoles(payload: unknown): Role[] {
const items = this.extractArrayFromResponse(payload);
return items.filter((item): item is Role => {
if (!item || typeof item !== 'object') {
return false;
}
const role = item as Partial<Role>;
return typeof role.name === 'string';
});
}
private resolveAuthError(error: any, fallback = 'Error en autenticación'): never {
if (error.response?.status === 422 && error.response?.data?.errors) {
const firstError = Object.values(error.response.data.errors)?.[0] as string[] | undefined;
throw new Error(firstError?.[0] || error.response.data.message || fallback);
}
if (error.response?.status === 429) {
throw new Error('Demasiados intentos de inicio de sesión. Intenta nuevamente en un minuto.');
}
throw new Error(error.response?.data?.message || fallback);
}
/**
* Iniciar sesión
*/
async login(credentials: LoginCredentials): Promise<{ user: User; token: string }> {
try {
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) {
this.resolveAuthError(error, 'Error al iniciar sesión');
}
}
/**
* Registrar nuevo usuario
*/
async register(data: RegisterData): Promise<LoginResponse> {
try {
const response = await api.post<LoginResponse>('/api/auth/register', data);
return response.data;
} catch (error: any) {
this.resolveAuthError(error, 'Error al registrar usuario');
}
}
/**
* Cerrar sesión
*/
async logout(): Promise<void> {
try {
await api.post('/api/auth/logout');
} 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
}
}
/**
* Obtener usuario actual
*/
async getCurrentUser(): Promise<User> {
try {
const response = await api.get<User>('/api/user');
return response.data;
} catch (error: any) {
this.resolveAuthError(error, 'Error al obtener usuario');
}
}
async getUserRoles(): Promise<Role[]> {
try {
const response = await api.get('/api/user/roles');
return this.normalizeRoles(response.data);
} catch (error: any) {
this.resolveAuthError(error, 'Error al obtener roles del usuario');
}
}
async getUserPermissions(): Promise<string[]> {
try {
const response = await api.get('/api/user/permissions');
console.log('User permissions response:', response);
return this.normalizePermissions(response.data);
} catch (error: any) {
this.resolveAuthError(error, 'Error al obtener permisos del usuario');
}
}
async refreshSession(): Promise<SessionRefreshData> {
try {
const [currentUser, roles, permissions] = await Promise.all([
this.getCurrentUser(),
this.getUserRoles(),
this.getUserPermissions(),
]);
return {
user: currentUser,
roles,
permissions,
};
} catch (error: any) {
this.resolveAuthError(error, 'Error al sincronizar sesión');
}
}
/**
* Actualizar perfil de usuario
*/
async updateProfile(data: Partial<User>): Promise<User> {
try {
const response = await api.put<User>('/api/auth/profile', data);
return response.data;
} catch (error: any) {
this.resolveAuthError(error, 'Error al actualizar perfil');
}
}
/**
* Cambiar contraseña
*/
async changePassword(currentPassword: string, newPassword: string): Promise<void> {
try {
await api.post('/api/auth/change-password', {
current_password: currentPassword,
new_password: newPassword
});
} catch (error: any) {
this.resolveAuthError(error, 'Error al cambiar contraseña');
}
}
/**
* Solicitar recuperación de contraseña
*/
async forgotPassword(email: string): Promise<void> {
try {
await api.post('/api/auth/forgot-password', { email });
} catch (error: any) {
this.resolveAuthError(error, 'Error al solicitar recuperación');
}
}
/**
* Resetear contraseña con token
*/
async resetPassword(token: string, password: string): Promise<void> {
try {
await api.post('/api/auth/reset-password', { token, password });
} catch (error: any) {
this.resolveAuthError(error, 'Error al resetear contraseña');
}
}
}
export const authService = new AuthService();
export default authService;