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.
158 lines
2.9 KiB
Svelte
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>
|