feat(auth): synchronize access token from cookies to localStorage if not present

refactor(dashboard): create dynamic columns for CodePedimentoRegimen with onSuccess callback

feat(dashboard): implement create, edit, and delete dialogs for CodePedimentoRegimen

feat(dialogs): add reusable dialog components for confirmation and details display

style(alert-dialog): improve styling and structure for alert dialog components

style(dialog): enhance styling and structure for dialog components
This commit is contained in:
2025-11-02 14:20:30 -06:00
parent 19472b840c
commit 206e81ef05
28 changed files with 969 additions and 77 deletions

View File

@@ -36,11 +36,31 @@ function onTokenRefreshed(token: string) {
async function refreshToken(): Promise<string | null> {
if (!browser) return null;
const refreshTokenValue = localStorage.getItem('refresh_token');
let refreshTokenValue = localStorage.getItem('refresh_token');
// Si no está en localStorage, intentar obtenerlo de las cookies
if (!refreshTokenValue) {
const getCookie = (name: string): string | null => {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop()?.split(';').shift() || null;
return null;
};
refreshTokenValue = getCookie('refresh_token');
if (refreshTokenValue) {
console.log('📝 [API] Refresh token encontrado en cookies, sincronizando a localStorage');
localStorage.setItem('refresh_token', refreshTokenValue);
}
}
if (!refreshTokenValue) {
console.error('❌ [API] No hay refresh token disponible');
return null;
}
console.log('🔄 [API] Intentando refrescar token...');
try {
const response = await fetch(`${API_BASE_URL}/v1/auth/refresh`, {
method: 'POST',
@@ -52,7 +72,7 @@ async function refreshToken(): Promise<string | null> {
});
if (!response.ok) {
console.error('❌ [API] Refresh token expirado o inválido');
console.error('❌ [API] Refresh token expirado o inválido, status:', response.status);
// Si el refresh token también está expirado, limpiar todo
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
@@ -69,6 +89,7 @@ async function refreshToken(): Promise<string | null> {
}
const data = await response.json();
console.log('✅ [API] Token refrescado correctamente');
// Guardar los nuevos tokens
if (data.access_token) {
@@ -116,6 +137,7 @@ async function fetchApi<T = any>(
): Promise<ApiResponse<T>> {
// Si ya estamos refrescando el token, esperar
if (isRefreshing && retryCount === 0) {
console.log('⏳ [API] Esperando refresh del token...');
return new Promise((resolve) => {
subscribeTokenRefresh((newToken) => {
resolve(fetchApi<T>(endpoint, options, 1));
@@ -124,6 +146,10 @@ async function fetchApi<T = any>(
}
const token = getToken();
if (!token && !endpoint.includes('/auth/login')) {
console.warn('⚠️ [API] No hay token disponible para', endpoint);
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
@@ -143,6 +169,7 @@ async function fetchApi<T = any>(
// Si recibimos 401 o 403 y no es el endpoint de refresh, intentar refrescar el token
if ((response.status === 401 || response.status === 403) && !endpoint.includes('/auth/refresh') && retryCount === 0) {
console.log('🔄 [API] Recibido 401/403, intentando refrescar token...');
isRefreshing = true;
try {
@@ -150,11 +177,13 @@ async function fetchApi<T = any>(
if (newToken) {
// Token refrescado exitosamente
console.log('✅ [API] Token refrescado exitosamente');
onTokenRefreshed(newToken);
isRefreshing = false;
// Reintentar la petición original con el nuevo token
return await fetchApi<T>(endpoint, options, 1);
} else {
console.error('❌ [API] No se pudo refrescar el token');
isRefreshing = false;
// Retornar error 401 para que la capa superior lo maneje
return {
@@ -163,6 +192,7 @@ async function fetchApi<T = any>(
};
}
} catch (refreshError) {
console.error('❌ [API] Error al refrescar:', refreshError);
isRefreshing = false;
return {
error: 'Error al refrescar la sesión',