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,233 @@
<script lang="ts">
import DashboardSection from '$lib/components/molecules/DashboardSection.svelte';
import SkeletonList from '$lib/components/atoms/Skeleton/SkeletonList.svelte';
let {
isLoading = false,
hasRealData = false,
establishmentName = ''
}: {
isLoading?: boolean;
hasRealData?: boolean;
establishmentName?: string;
} = $props();
</script>
<div class="dashboard-admin">
<header class="dashboard-header">
<h1>Administration</h1>
{#if establishmentName}
<p class="dashboard-subtitle">{establishmentName}</p>
{:else}
<p class="dashboard-subtitle">Bienvenue dans votre espace d'administration</p>
{/if}
</header>
<div class="quick-actions">
<h2 class="sr-only">Actions de configuration</h2>
<div class="action-cards">
<div class="action-card disabled" aria-disabled="true">
<span class="action-icon">👥</span>
<span class="action-label">Gérer les utilisateurs</span>
<span class="action-hint">Bientôt disponible</span>
</div>
<div class="action-card disabled" aria-disabled="true">
<span class="action-icon">🏫</span>
<span class="action-label">Configurer les classes</span>
<span class="action-hint">Bientôt disponible</span>
</div>
<div class="action-card disabled" aria-disabled="true">
<span class="action-icon">📅</span>
<span class="action-label">Calendrier scolaire</span>
<span class="action-hint">Bientôt disponible</span>
</div>
<div class="action-card disabled" aria-disabled="true">
<span class="action-icon">📤</span>
<span class="action-label">Importer des données</span>
<span class="action-hint">Bientôt disponible</span>
</div>
</div>
</div>
<div class="dashboard-grid">
<DashboardSection
title="Utilisateurs"
isPlaceholder={!hasRealData}
placeholderMessage="Les statistiques utilisateurs s'afficheront une fois les comptes créés"
>
{#if hasRealData}
{#if isLoading}
<SkeletonList items={3} message="Chargement des statistiques..." />
{:else}
<div class="stats-grid">
<div class="stat-card">
<span class="stat-value">--</span>
<span class="stat-label">Élèves</span>
</div>
<div class="stat-card">
<span class="stat-value">--</span>
<span class="stat-label">Enseignants</span>
</div>
<div class="stat-card">
<span class="stat-value">--</span>
<span class="stat-label">Parents</span>
</div>
</div>
{/if}
{/if}
</DashboardSection>
<DashboardSection
title="Configuration"
isPlaceholder={!hasRealData}
placeholderMessage="L'état de la configuration sera affiché ici"
>
{#if hasRealData && isLoading}
<SkeletonList items={4} message="Vérification de la configuration..." />
{/if}
</DashboardSection>
<DashboardSection
title="Activité récente"
isPlaceholder={!hasRealData}
placeholderMessage="L'historique des actions s'affichera ici"
>
{#if hasRealData && isLoading}
<SkeletonList items={5} message="Chargement de l'activité..." />
{/if}
</DashboardSection>
</div>
</div>
<style>
.dashboard-admin {
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.dashboard-header {
margin-bottom: 0.5rem;
}
.dashboard-header h1 {
margin: 0;
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
}
.dashboard-subtitle {
margin: 0.25rem 0 0;
color: #6b7280;
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
.quick-actions {
margin-bottom: 0.5rem;
}
.action-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 1rem;
}
.action-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.5rem;
padding: 1.25rem 1rem;
background: white;
border: 2px solid #e5e7eb;
border-radius: 0.75rem;
text-decoration: none;
color: inherit;
transition: all 0.2s;
}
.action-card:not(.disabled):hover {
border-color: #3b82f6;
background: #eff6ff;
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
.action-card.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.action-icon {
font-size: 2rem;
}
.action-label {
font-weight: 600;
color: #374151;
text-align: center;
}
.action-hint {
font-size: 0.75rem;
color: #6b7280;
text-align: center;
}
.dashboard-grid {
display: grid;
gap: 1.5rem;
grid-template-columns: 1fr;
}
@media (min-width: 768px) {
.dashboard-grid {
grid-template-columns: repeat(2, 1fr);
}
}
@media (min-width: 1024px) {
.dashboard-grid {
grid-template-columns: repeat(3, 1fr);
}
}
.stats-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1rem;
}
.stat-card {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 1rem;
background: #f9fafb;
border-radius: 0.5rem;
}
.stat-value {
font-size: 1.5rem;
font-weight: 700;
color: #1f2937;
}
.stat-label {
font-size: 0.75rem;
color: #6b7280;
}
</style>