feat: Dashboard placeholder avec preview Score Sérénité

Permet aux parents de visualiser une démo du Score Sérénité dès leur
première connexion, avant même que les données réelles soient disponibles.
Les autres rôles (enseignant, élève, admin) ont également leur dashboard
adapté avec des sections placeholder.

La landing page redirige automatiquement vers /dashboard si l'utilisateur
est déjà authentifié, offrant un accès direct au tableau de bord.
This commit is contained in:
2026-02-04 18:34:08 +01:00
parent d3c6773be5
commit b45ef735db
26 changed files with 3096 additions and 76 deletions

View File

@@ -0,0 +1,208 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { logout } from '$lib/auth/auth.svelte';
let { children } = $props();
let isLoggingOut = $state(false);
// Note: Authentication is handled by authenticatedFetch in the page component.
// If not authenticated, authenticatedFetch will attempt refresh and redirect to /login if needed.
async function handleLogout() {
isLoggingOut = true;
try {
await logout();
} finally {
isLoggingOut = false;
}
}
function goHome() {
goto('/dashboard');
}
function goSettings() {
goto('/settings');
}
</script>
<div class="dashboard-layout">
<header class="dashboard-header">
<div class="header-content">
<button class="logo-button" onclick={goHome}>
<span class="logo-text">Classeo</span>
</button>
<nav class="header-nav">
<a href="/dashboard" class="nav-link active">Tableau de bord</a>
<button class="nav-button" onclick={goSettings}>Parametres</button>
<button class="logout-button" onclick={handleLogout} disabled={isLoggingOut}>
{#if isLoggingOut}
<span class="spinner"></span>
Deconnexion...
{:else}
Deconnexion
{/if}
</button>
</nav>
</div>
</header>
<main class="dashboard-main">
<div class="main-content">
{@render children()}
</div>
</main>
</div>
<style>
.dashboard-layout {
min-height: 100vh;
display: flex;
flex-direction: column;
background: var(--surface-primary, #f8fafc);
}
.dashboard-header {
background: var(--surface-elevated, #fff);
border-bottom: 1px solid var(--border-subtle, #e2e8f0);
padding: 0 1.5rem;
position: sticky;
top: 0;
z-index: 100;
}
.header-content {
max-width: 1200px;
margin: 0 auto;
display: flex;
justify-content: space-between;
align-items: center;
height: 64px;
}
.logo-button {
background: none;
border: none;
cursor: pointer;
padding: 0.5rem 0;
}
.logo-text {
font-size: 1.25rem;
font-weight: 700;
color: var(--accent-primary, #0ea5e9);
}
.header-nav {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-link {
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--text-secondary, #64748b);
text-decoration: none;
border-radius: 0.5rem;
transition: all 0.2s;
}
.nav-link:hover {
color: var(--text-primary, #1f2937);
background: var(--surface-primary, #f8fafc);
}
.nav-link.active {
color: var(--accent-primary, #0ea5e9);
background: var(--accent-primary-light, #e0f2fe);
}
.nav-button {
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--text-secondary, #64748b);
background: transparent;
border: none;
border-radius: 0.5rem;
cursor: pointer;
transition: all 0.2s;
}
.nav-button:hover {
color: var(--text-primary, #1f2937);
background: var(--surface-primary, #f8fafc);
}
.logout-button {
display: inline-flex;
align-items: center;
gap: 0.5rem;
padding: 0.5rem 1rem;
font-size: 0.875rem;
font-weight: 500;
color: var(--text-secondary, #64748b);
background: transparent;
border: 1px solid var(--border-subtle, #e2e8f0);
border-radius: 0.5rem;
cursor: pointer;
transition: all 0.2s;
}
.logout-button:hover:not(:disabled) {
color: var(--color-alert, #ef4444);
border-color: var(--color-alert, #ef4444);
}
.logout-button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.spinner {
width: 14px;
height: 14px;
border: 2px solid var(--border-subtle, #e2e8f0);
border-top-color: var(--text-secondary, #64748b);
border-radius: 50%;
animation: spin 0.8s linear infinite;
}
.dashboard-main {
flex: 1;
padding: 1.5rem;
}
.main-content {
max-width: 1200px;
margin: 0 auto;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@media (max-width: 768px) {
.header-content {
flex-wrap: wrap;
height: auto;
padding: 0.75rem 0;
gap: 0.75rem;
}
.header-nav {
width: 100%;
justify-content: flex-end;
flex-wrap: wrap;
gap: 0.5rem;
}
.dashboard-main {
padding: 1rem;
}
}
</style>