Files
Classeo/frontend/src/routes/super-admin/+layout.svelte
Mathias STRASSER 0951322d71 feat: Permettre au super admin de se connecter et accéder à son dashboard
Le super admin (table super_admins, master DB) ne pouvait pas se connecter
via /api/login car ce firewall n'utilisait que le provider tenant. De même,
le JWT n'était pas enrichi pour les super admins, l'endpoint /api/me/roles
les rejetait, et le frontend redirigeait systématiquement vers /dashboard.

Un chain provider (super_admin + tenant) résout l'authentification,
le JwtPayloadEnricher et MyRolesProvider gèrent désormais les deux types
d'utilisateurs, et le frontend redirige selon le rôle après login.
2026-02-18 10:15:47 +01:00

158 lines
2.9 KiB
Svelte

<script lang="ts">
import { untrack } from 'svelte';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { isAuthenticated, refreshToken } from '$lib/auth/auth.svelte';
import { fetchRoles, getActiveRole } from '$lib/features/roles/roleContext.svelte';
let { children } = $props();
let hasAccess = $derived(isAuthenticated() && getActiveRole() === 'ROLE_SUPER_ADMIN');
$effect(() => {
untrack(async () => {
if (!isAuthenticated()) {
const refreshed = await refreshToken();
if (!refreshed) {
goto('/login');
return;
}
}
await fetchRoles();
});
});
$effect(() => {
const role = getActiveRole();
if (role !== null && role !== 'ROLE_SUPER_ADMIN') {
goto('/dashboard');
}
});
const isEstablishmentsActive = $derived($page.url.pathname.startsWith('/super-admin/establishments'));
const isDashboardActive = $derived(
$page.url.pathname === '/super-admin' || $page.url.pathname === '/super-admin/dashboard'
);
</script>
{#if hasAccess}
<div class="super-admin-layout">
<header class="sa-header">
<div class="sa-header-inner">
<div class="sa-brand">
<span class="sa-logo">SA</span>
<h1>Classeo Super Admin</h1>
</div>
<nav class="sa-nav">
<a
href="/super-admin/dashboard"
class="sa-nav-link"
class:active={isDashboardActive}
>
Dashboard
</a>
<a
href="/super-admin/establishments"
class="sa-nav-link"
class:active={isEstablishmentsActive}
>
Établissements
</a>
</nav>
</div>
</header>
<main class="sa-main">
{@render children()}
</main>
</div>
{:else}
<div class="sa-loading">
<p>Vérification des accès...</p>
</div>
{/if}
<style>
.super-admin-layout {
min-height: 100vh;
background: #f8f9fa;
}
.sa-header {
background: #1a1a2e;
color: white;
padding: 0 1.5rem;
position: sticky;
top: 0;
z-index: 100;
}
.sa-header-inner {
max-width: 1400px;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: space-between;
height: 56px;
}
.sa-brand {
display: flex;
align-items: center;
gap: 0.75rem;
}
.sa-logo {
background: #e94560;
color: white;
font-weight: 700;
font-size: 0.75rem;
padding: 0.25rem 0.5rem;
border-radius: 4px;
}
.sa-brand h1 {
font-size: 1.125rem;
font-weight: 600;
margin: 0;
}
.sa-nav {
display: flex;
gap: 0.25rem;
}
.sa-nav-link {
color: rgba(255, 255, 255, 0.7);
text-decoration: none;
padding: 0.5rem 1rem;
border-radius: 6px;
font-size: 0.875rem;
transition: all 0.15s;
}
.sa-nav-link:hover {
color: white;
background: rgba(255, 255, 255, 0.1);
}
.sa-nav-link.active {
color: white;
background: rgba(255, 255, 255, 0.15);
}
.sa-main {
max-width: 1400px;
margin: 0 auto;
padding: 2rem 1.5rem;
}
.sa-loading {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
color: #666;
}
</style>