diff --git a/frontend/src/routes/dashboard/+layout.svelte b/frontend/src/routes/dashboard/+layout.svelte index 24aa41fd..f1a7706d 100644 --- a/frontend/src/routes/dashboard/+layout.svelte +++ b/frontend/src/routes/dashboard/+layout.svelte @@ -61,6 +61,48 @@ // Actualizar el contexto reactive al cambiar data.user (post invalidateAll) $effect(() => { setContext('user', data.user); }); + // ── Sembrar el authStore desde data.user ─────────────────────────────── + // Se ejecuta SÍNCRONAMENTE en el script (SSR + hidratación), no en onMount, + // para que `$currentUser` ya esté poblado en el primer render de las páginas + // hijas. Evita el flash de "Acceso restringido" al refrescar cuando las páginas + // derivan `canView` a partir de `$currentUser`. + function syncAuthStoreFromData() { + if (!data.user) return; + if (data.user.token) { + authStore.setToken(data.user.token); + authStore.setAuthenticated(true); + } + const resolvedAvatarUrl = + data.user.workspaceAvatarUrl || + data.user.workspace_avatar_url || + data.user.avatarUrl || + data.user.avatar_url || + data.user.legacyAvatarUrl || + data.user.legacy_avatar_url || + null; + authStore.setUser({ + id: data.user.sub ?? data.user.id ?? '', + username: data.user.preferred_username ?? data.user.username ?? '', + email: data.user.email, + name: data.user.name, + avatarUrl: resolvedAvatarUrl, + workspaceAvatarUrl: data.user.workspaceAvatarUrl ?? data.user.workspace_avatar_url ?? null, + legacyAvatarUrl: data.user.legacyAvatarUrl ?? data.user.legacy_avatar_url ?? data.user.avatar_url ?? null, + tenantId: data.user.tenant_id, + roles: data.user.roles ?? [], + permissions: data.user.permissions ?? [] + }); + } + + syncAuthStoreFromData(); + + // Re-sincronizar si data.user cambia (por ejemplo, tras invalidateAll o cambio de compañía) + $effect(() => { + // Dependencia explícita para que el effect reaccione al swap de data.user + data.user; + syncAuthStoreFromData(); + }); + // ── Manejar expiración de sesión ──────────────────────────────────────── function handleSessionExpired(e: Event) { const { reason } = (e as CustomEvent).detail; @@ -69,37 +111,6 @@ } onMount(() => { - // ── Inicializar el token en el authStore desde los datos del servidor ── - // El servidor valida la cookie y pasa el access_token a través de data.user.token. - // Lo almacenamos en memoria (authStore) sin tocar localStorage. - if (data.user?.token) { - authStore.setToken(data.user.token); - authStore.setAuthenticated(true); - } - - if (data.user) { - const resolvedAvatarUrl = - data.user.workspaceAvatarUrl || - data.user.workspace_avatar_url || - data.user.avatarUrl || - data.user.avatar_url || - data.user.legacyAvatarUrl || - data.user.legacy_avatar_url || - null; - authStore.setUser({ - id: data.user.sub ?? data.user.id ?? '', - username: data.user.preferred_username ?? data.user.username ?? '', - email: data.user.email, - name: data.user.name, - avatarUrl: resolvedAvatarUrl, - workspaceAvatarUrl: data.user.workspaceAvatarUrl ?? data.user.workspace_avatar_url ?? null, - legacyAvatarUrl: data.user.legacyAvatarUrl ?? data.user.legacy_avatar_url ?? data.user.avatar_url ?? null, - tenantId: data.user.tenant_id, - roles: data.user.roles ?? [], - permissions: data.user.permissions ?? [] - }); - } - // ── Inicializar el SessionManager ───────────────────────────────────── if (data.user?.token) { const mgr = createSessionManager({