Première grosse version fonctionnel

This commit is contained in:
2026-04-22 23:30:31 +02:00
commit ccbe5ad801
329 changed files with 29518 additions and 0 deletions
+75
View File
@@ -0,0 +1,75 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Connexion | CESIZen - Ministère de la Santé</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
:root { --fr-blue: #000091; }
.fr-input-group { margin-bottom: 1.5rem; }
.fr-label { display: block; font-size: 0.875rem; font-weight: 700; margin-bottom: 0.5rem; color: #161616; }
.fr-input { width: 100%; padding: 0.75rem; border: 2px solid #3a3a3a; border-radius: 0; background-color: #eee; transition: border-color 0.2s; }
.fr-input:focus { border-color: var(--fr-blue); outline: 3px solid var(--fr-blue); outline-offset: 2px; }
</style>
</head>
<body class="bg-[#F6F6F6] min-h-screen flex flex-col font-sans">
@include('layouts.header')
<main id="main-content" class="flex-1 flex items-center justify-center p-8">
<div class="w-full max-w-lg bg-white border border-slate-200 shadow-xl p-12">
<div class="mb-10 text-left border-l-4 border-[#000091] pl-6">
<h1 class="text-3xl font-black text-slate-900 mb-2">Se connecter</h1>
<p class="text-slate-600 font-medium">Espace sécurisé pour les étudiants du CESI</p>
</div>
@if ($errors->any())
<div class="bg-[#FAEFEF] border-l-4 border-[#E1000F] p-4 mb-8" role="alert">
<p class="text-[#E1000F] text-sm font-bold">Erreur : {{ $errors->first() }}</p>
</div>
@endif
<form action="{{ route('login') }}" method="POST" class="space-y-8">
@csrf
<div class="fr-input-group">
<label for="email" class="fr-label">Adresse électronique (email)</label>
<input type="email" id="email" name="email" required
class="fr-input"
value="{{ old('email') }}"
aria-required="true"
autocomplete="email">
</div>
<div class="fr-input-group">
<label for="password" class="fr-label">Mot de passe</label>
<input type="password" id="password" name="password" required
class="fr-input"
aria-required="true"
autocomplete="current-password">
</div>
<div class="flex items-center justify-between">
<label class="flex items-center gap-3 cursor-pointer group">
<input type="checkbox" name="remember" class="w-5 h-5 border-2 border-slate-400 text-[#000091] focus:ring-[#000091]">
<span class="text-sm font-bold text-slate-700 group-hover:text-[#000091]">Rester connecté</span>
</label>
<a href="#" class="text-sm font-bold text-[#000091] hover:underline">Mot de passe oublié ?</a>
</div>
<button type="submit" class="w-full py-4 bg-[#000091] text-white font-bold text-lg hover:bg-blue-800 shadow-md transition-all uppercase tracking-widest">
Valider la connexion
</button>
</form>
<div class="mt-12 pt-8 border-t border-slate-100">
<h2 class="text-lg font-bold text-slate-900 mb-4">Pas encore de compte ?</h2>
<a href="{{ route('register') }}" class="inline-flex items-center gap-2 px-6 py-3 border-2 border-[#000091] text-[#000091] font-bold hover:bg-blue-50 transition-all">
Créer mon espace personnel
</a>
</div>
</div>
</main>
@include('layouts.footer')
</body>
</html>
@@ -0,0 +1,94 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Créer un compte | CESIZen - Ministère de la Santé</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
:root { --fr-blue: #000091; }
.fr-input-group { margin-bottom: 1.5rem; }
.fr-label { display: block; font-size: 0.875rem; font-weight: 700; margin-bottom: 0.5rem; color: #161616; }
.fr-input { width: 100%; padding: 0.75rem; border: 2px solid #3a3a3a; border-radius: 0; background-color: #eee; transition: border-color 0.2s; }
.fr-input:focus { border-color: var(--fr-blue); outline: 3px solid var(--fr-blue); outline-offset: 2px; }
</style>
</head>
<body class="bg-[#F6F6F6] min-h-screen flex flex-col font-sans">
@include('layouts.header')
<main id="main-content" class="flex-1 flex items-center justify-center p-8">
<div class="w-full max-w-lg bg-white border border-slate-200 shadow-xl p-12">
<div class="mb-10 text-left border-l-4 border-[#000091] pl-6">
<h1 class="text-3xl font-black text-slate-900 mb-2">Créer mon espace</h1>
<p class="text-slate-600 font-medium">Rejoignez la plateforme de santé étudiante du CESI</p>
</div>
@if ($errors->any())
<div class="bg-[#FAEFEF] border-l-4 border-[#E1000F] p-4 mb-8" role="alert">
<ul class="list-disc list-inside text-[#E1000F] text-sm font-bold">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('register') }}" method="POST" class="space-y-6">
@csrf
<div class="fr-input-group">
<label for="name" class="fr-label">Nom complet</label>
<input type="text" id="name" name="name" required
class="fr-input"
value="{{ old('name') }}"
aria-required="true"
autocomplete="name">
</div>
<div class="fr-input-group">
<label for="email" class="fr-label">Adresse électronique (email académique)</label>
<input type="email" id="email" name="email" required
class="fr-input"
value="{{ old('email') }}"
aria-required="true"
autocomplete="email">
</div>
<div class="fr-input-group">
<label for="password" class="fr-label">Mot de passe</label>
<input type="password" id="password" name="password" required
class="fr-input"
aria-required="true"
autocomplete="new-password">
<p class="text-[10px] text-slate-500 mt-1 uppercase font-bold tracking-tight">8 caractères minimum, incluant chiffres et symboles</p>
</div>
<div class="fr-input-group">
<label for="password_confirmation" class="fr-label">Confirmer le mot de passe</label>
<input type="password" id="password_confirmation" name="password_confirmation" required
class="fr-input"
aria-required="true"
autocomplete="new-password">
</div>
<div class="bg-slate-50 p-4 border border-slate-200 text-[11px] text-slate-600 mb-6">
En cliquant sur "Créer mon compte", vous acceptez nos <a href="#" class="underline hover:text-[#000091]">Conditions Générales d'Utilisation</a> et confirmez avoir lu notre <a href="#" class="underline hover:text-[#000091]">Politique de Confidentialité</a> (RGPD).
</div>
<button type="submit" class="w-full py-4 bg-[#000091] text-white font-bold text-lg hover:bg-blue-800 shadow-md transition-all uppercase tracking-widest">
Créer mon compte
</button>
</form>
<div class="mt-12 pt-8 border-t border-slate-100 text-center">
<p class="text-slate-600 mb-4 font-medium">Vous avez déjà un compte ?</p>
<a href="{{ route('login') }}" class="inline-flex items-center gap-2 px-8 py-3 border-2 border-[#000091] text-[#000091] font-bold hover:bg-blue-50 transition-all uppercase tracking-widest text-sm">
Se connecter
</a>
</div>
</div>
</main>
@include('layouts.footer')
</body>
</html>
@@ -0,0 +1,54 @@
@extends('layouts.app')
@section('title', 'Bilan de Stress')
@section('header_title', 'Évaluation de Stress (Holmes & Rahe)')
@section('content')
<div class="max-w-4xl mx-auto space-y-10">
@if(session('score'))
<div class="bg-white p-8 border-l-8 border-[--fr-blue] shadow-lg">
<h2 class="text-2xl font-black mb-4">Votre Résultat : <span class="text-[--fr-blue]">{{ session('score') }} points</span></h2>
<div class="p-6 bg-slate-50 rounded-lg">
@if(session('score') >= 300)
<p class="font-bold text-red-700 uppercase mb-2">Risque élevé de maladie</p>
<p class="text-slate-600">Votre score indique un niveau de stress très important. Nous vous conseillons vivement de consulter un professionnel de santé.</p>
@elseif(session('score') >= 150)
<p class="font-bold text-orange-700 uppercase mb-2">Risque modéré</p>
<p class="text-slate-600">Vous avez traversé beaucoup de changements récemment. Prenez du temps pour vous reposer.</p>
@else
<p class="font-bold text-teal-700 uppercase mb-2">Risque faible</p>
<p class="text-slate-600">Votre niveau de stress lié aux changements de vie semble stable.</p>
@endif
</div>
<a href="{{ route('diagnostics.index') }}" class="mt-6 inline-block text-[--fr-blue] font-bold hover:underline">Recommencer le test</a>
</div>
@else
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<div class="mb-8">
<h2 class="text-xl font-bold mb-2">Quels événements avez-vous vécus ces 12 derniers mois ?</h2>
<p class="text-sm text-slate-500 italic">Cochez toutes les cases qui correspondent à votre situation actuelle.</p>
</div>
<form action="{{ route('diagnostics.store') }}" method="POST" class="space-y-4">
@csrf
<div class="grid grid-cols-1 gap-3 max-h-[500px] overflow-y-auto pr-4 border-b border-t border-slate-100 py-6">
@foreach($events as $event)
<label class="flex items-center gap-4 p-4 hover:bg-slate-50 rounded-lg cursor-pointer transition-colors border border-transparent hover:border-slate-200 group">
<input type="checkbox" name="events[]" value="{{ $event->id }}" class="w-5 h-5 text-[--fr-blue] border-slate-300 rounded focus:ring-[--fr-blue]">
<span class="flex-1 text-sm font-medium text-slate-700 group-hover:text-slate-900">{{ $event->event_name }}</span>
<span class="text-xs font-bold text-slate-400">{{ $event->points }} pts</span>
</label>
@endforeach
</div>
<div class="pt-6">
<button type="submit" class="w-full py-4 bg-[#000091] text-white font-bold rounded-sm hover:bg-blue-800 shadow-md transition-all uppercase tracking-widest">
Calculer mon score de stress
</button>
</div>
</form>
</div>
@endif
</div>
@endsection
@@ -0,0 +1,84 @@
@extends('layouts.app')
@section('title', 'Journal Émotionnel')
@section('content')
<div class="max-w-4xl mx-auto space-y-10">
<!-- Nouveau Record -->
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<h2 class="text-xl font-bold mb-6">Comment vous sentez-vous aujourd'hui ?</h2>
<form action="{{ route('emotions.store') }}" method="POST" class="space-y-6">
@csrf
<div>
<label class="block text-sm font-bold text-slate-700 mb-3">Votre émotion dominante</label>
<div class="grid grid-cols-3 md:grid-cols-5 gap-4">
@foreach(['Très bien' => '😊', 'Bien' => '🙂', 'Neutre' => '😐', 'Pas top' => '🙁', 'Stressé' => '😫'] as $label => $emoji)
<label class="cursor-pointer group">
<input type="radio" name="emotion" value="{{ $label }}" class="peer hidden" required>
<div class="p-4 border-2 border-slate-100 rounded-xl text-center peer-checked:border-[--fr-blue] peer-checked:bg-blue-50 transition-all">
<span class="block text-2xl mb-1">{{ $emoji }}</span>
<span class="text-[10px] font-bold uppercase tracking-widest text-slate-500 group-hover:text-slate-900">{{ $label }}</span>
</div>
</label>
@endforeach
</div>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-3">Intensité (1 à 5)</label>
<input type="range" name="intensity" min="1" max="5" step="1" class="w-full h-2 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-[--fr-blue]">
<div class="flex justify-between text-[10px] font-bold text-slate-400 mt-2 px-1">
<span>FAIBLE</span>
<span>MODÉRÉE</span>
<span>TRÈS FORTE</span>
</div>
</div>
<div>
<label class="block text-sm font-bold text-slate-700 mb-3">Notes (Optionnel)</label>
<textarea name="note" rows="3" class="w-full border-slate-200 rounded-lg focus:ring-[--fr-blue] focus:border-[--fr-blue]" placeholder="Décrivez votre état..."></textarea>
</div>
<button type="submit" class="w-full py-4 bg-[--fr-blue] text-white font-bold rounded-sm hover:bg-blue-800 shadow-md transition-all">
Enregistrer dans mon journal
</button>
</form>
</div>
<!-- Historique -->
<div class="space-y-4">
<h2 class="text-xl font-bold">Historique récent</h2>
@forelse($records as $record)
<div class="bg-white p-6 border border-slate-100 shadow-sm flex items-center justify-between">
<div class="flex items-center gap-6">
<div class="text-3xl">
@switch($record->emotion)
@case('Très bien') 😊 @break
@case('Bien') 🙂 @break
@case('Neutre') 😐 @break
@case('Pas top') 🙁 @break
@case('Stressé') 😫 @break
@default 😐
@endswitch
</div>
<div>
<p class="font-bold text-slate-900 capitalize">{{ $record->emotion }}</p>
<p class="text-xs text-slate-500">{{ $record->created_at->diffForHumans() }}</p>
@if($record->note)
<p class="text-sm text-slate-600 mt-1 italic">"{{ $record->note }}"</p>
@endif
</div>
</div>
<div class="flex gap-1">
@for($i=1; $i<=5; $i++)
<div class="w-2 h-6 rounded-full {{ $i <= $record->intensity ? 'bg-[--cz-jade-bg]' : 'bg-slate-100' }}"></div>
@endfor
</div>
</div>
@empty
<p class="text-center text-slate-400 py-10">Aucun enregistrement pour le moment.</p>
@endforelse
</div>
</div>
@endsection
+37
View File
@@ -0,0 +1,37 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>@yield('title', 'CESIZen') | Ministère de la Santé</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
:root {
--fr-blue: #000091;
--cz-navy: #000074;
--cz-mustard: #715200;
--cz-mustard-bg: #F8B803;
--cz-jade: #00635B;
}
</style>
</head>
@include('layouts.header')
<body class="bg-[#F6F6F6] text-[#161616] min-h-screen font-sans">
<div class="flex min-h-screen">
<!-- Sidebar Simplifiée pour les sous-pages -->
@include('layouts.sidebar')
<main class="flex-1">
<!-- <header class="bg-white border-b border-slate-200 px-8 py-4 flex items-center justify-between">
<h1 class="text-xl font-bold">@yield('header_title')</h1>
@auth
<span class="text-sm font-bold text-slate-600">{{ Auth::user()->name }}</span>
@endauth
</header> -->
<div class="p-8">
@yield('content')
</div>
</main>
</div>
</body>
</html>
@@ -0,0 +1,21 @@
<footer class="mt-auto bg-white border-t border-slate-200 pt-12 pb-8" role="contentinfo">
<div class="container mx-auto px-8">
<div class="flex flex-col lg:flex-row justify-between gap-10 mb-10 border-b border-slate-100 pb-10">
<div class="flex items-center gap-6">
<img src="img/ministere.png" alt="Logo du Ministère de la Santé et de la Prévention" class="w-24 h-auto">
</div>
<div class="flex flex-wrap gap-6 text-xs font-bold text-slate-600">
<a href="#" class="hover:text-[--fr-blue] underline">legifrance.gouv.fr</a>
<a href="#" class="hover:text-[--fr-blue] underline">service-public.fr</a>
<a href="#" class="hover:text-[--fr-blue] underline">data.gouv.fr</a>
</div>
</div>
<div class="flex flex-wrap gap-6 text-[11px] font-bold text-slate-500 uppercase tracking-wider">
<a href="#" class="hover:underline">Mentions légales</a>
<a href="#" class="hover:underline">Accessibilité : partiellement conforme</a>
<a href="#" class="hover:underline">Données personnelles</a>
<a href="#" class="hover:underline">Cookies</a>
<span class="ml-auto text-slate-400 font-normal">&copy; {{ date('Y') }} Ministère de la Santé - CESIZen</span>
</div>
</div>
</footer>
@@ -0,0 +1,41 @@
<header role="banner" class="bg-white border-b-2 border-[--fr-blue] sticky top-0 z-50 shadow-sm">
<div class="container mx-auto px-4 lg:px-8 py-3 flex items-center justify-between">
<div class="flex items-start justify-start gap-8">
<div class="flex flex-col border-r-2 border-slate-200 pr-8" aria-hidden="true">
<img src="img/ministere.png" alt="Logo du Ministère de la Santé et de la Prévention" class="w-24 h-auto">
</div>
<div class="flex flex-col">
<h1 class="text-2xl font-black text-[--fr-blue] tracking-tight">
<a href="{{ route('home') }}"><img src="img/CesiZen.png" alt="Logo CesiZen" class="w-32 h-auto"></a>
</h1>
</div>
</div>
<nav class="flex items-center gap-6" role="navigation" aria-label="Menu utilisateur">
@auth
<a href="{{ route('profile.index') }}" class="flex items-center gap-3 px-4 py-1.5 bg-slate-50 rounded-lg border border-slate-200 shadow-sm hover:border-[#000091] transition-all group">
<div class="text-right">
<p class="text-sm font-bold leading-none text-slate-900 group-hover:text-[#000091]">{{ Auth::user()->name }}</p>
<p class="text-[10px] text-slate-500 font-bold uppercase tracking-tighter">Mon Profil</p>
</div>
<img src="https://ui-avatars.com/api/?name={{ urlencode(Auth::user()->name) }}&background=000091&color=fff" class="w-9 h-9 rounded-lg border border-slate-200" alt="Avatar de {{ Auth::user()->name }}">
</a>
<form action="{{ route('logout') }}" method="POST" class="inline">
@csrf
<button type="submit" class="text-xs font-bold text-slate-500 hover:text-[#E1000F] uppercase tracking-tighter flex items-center gap-1">
<x-heroicon-o-arrow-left-on-rectangle class="h-4 w-4" />
Quitter
</button>
</form>
@else
<div class="flex items-center gap-4">
<a href="/login" class="text-sm font-bold text-[#000091] hover:underline px-2 py-2">Se connecter</a>
<a href="/register" class="px-6 py-2.5 bg-[#000091] text-white text-sm font-bold rounded-sm shadow-md hover:bg-blue-800 transition-all">
Créer un compte
</a>
</div>
@endauth
</nav>
</div>
</header>
@@ -0,0 +1,67 @@
<aside class="hidden lg:flex w-80 flex-col bg-white border-r border-slate-200 overflow-y-auto" role="navigation" aria-label="Menu latéral">
<nav class="flex-1 px-6 py-10 space-y-1">
<p class="text-[10px] font-bold text-slate-500 uppercase tracking-[0.2em] mb-4 px-4">Navigation</p>
<!-- Tableau de bord -->
<a href="{{ route('home') }}"
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all {{ request()->routeIs('home') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium' }}"
{!! request()->routeIs('home') ? 'aria-current="page"' : '' !!}>
<x-heroicon-o-home class="h-5 w-5" aria-hidden="true" />
Tableau de bord
</a>
<!-- Mon Journal Émotionnel -->
<a href="{{ route('emotions.index') }}"
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all group {{ request()->routeIs('emotions.*') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium border-b border-slate-100/50' }}"
{!! request()->routeIs('emotions.*') ? 'aria-current="page"' : '' !!}>
<div class="w-8 h-8 rounded-sm flex items-center justify-center transition-colors {{ request()->routeIs('emotions.*') ? 'bg-white/20 text-white' : 'bg-teal-50 text-[--cz-jade]' }}" aria-hidden="true">
<x-heroicon-o-clock class="h-4 w-4" />
</div>
Mon Journal Émotionnel
</a>
<!-- Diagnostics Stress -->
<a href="{{ route('diagnostics.index') }}"
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all group {{ request()->routeIs('diagnostics.*') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium border-b border-slate-100/50' }}"
{!! request()->routeIs('diagnostics.*') ? 'aria-current="page"' : '' !!}>
<div class="w-8 h-8 rounded-sm flex items-center justify-center transition-colors {{ request()->routeIs('diagnostics.*') ? 'bg-white/20 text-white' : 'bg-orange-50 text-[--cz-mustard]' }}" aria-hidden="true">
<x-heroicon-o-chart-bar class="h-4 w-4" />
</div>
Diagnostics Stress
</a>
<!-- Exercices de Respiration -->
<a href="{{ route('relaxation.index') }}"
class="flex items-center gap-4 px-4 py-3 rounded-sm transition-all group {{ request()->routeIs('relaxation.*') ? 'text-white bg-[#000091] font-bold shadow-md' : 'text-slate-700 hover:bg-slate-50 font-medium border-b border-slate-100/50' }}"
{!! request()->routeIs('relaxation.*') ? 'aria-current="page"' : '' !!}>
<div class="w-8 h-8 rounded-sm flex items-center justify-center transition-colors {{ request()->routeIs('relaxation.*') ? 'bg-white/20 text-white' : 'bg-indigo-50 text-indigo-700' }}" aria-hidden="true">
<x-heroicon-o-heart class="h-4 w-4" />
</div>
Exercices de Respiration
</a>
<div class="pt-10 space-y-1">
<p class="text-[10px] font-bold text-slate-500 uppercase tracking-[0.2em] mb-4 px-4">Services de l'État</p>
<a href="#" class="flex items-center justify-between px-4 py-2 text-sm text-slate-600 hover:text-[--fr-blue] font-medium transition-colors">
<span>Guide Santé Étudiante</span>
<x-heroicon-o-document-text class="h-4 w-4" aria-hidden="true" />
</a>
<a href="#" class="flex items-center justify-between px-4 py-2 text-sm text-slate-600 hover:text-[--fr-blue] font-medium transition-colors">
<span>Ligne d'Urgence</span>
<x-heroicon-o-phone class="h-4 w-4" aria-hidden="true" />
</a>
</div>
</nav>
<!-- Alert Block -->
<div class="p-6">
<div class="bg-slate-900 rounded-sm p-6 text-white relative overflow-hidden">
<p class="text-[10px] font-bold uppercase tracking-widest text-slate-400 mb-2">Besoin d'aide ?</p>
<p class="text-xs font-medium leading-relaxed mb-4">Une ligne d'écoute gratuite est disponible pour tous les étudiants.</p>
<a href="tel:0800130000" class="flex items-center justify-center gap-2 w-full py-2.5 bg-white text-slate-900 text-sm font-bold rounded-sm hover:bg-slate-100 transition-colors">
<x-heroicon-o-phone class="h-4 w-4" aria-hidden="true" />
0 800 130 000
</a>
</div>
</div>
</aside>
+121
View File
@@ -0,0 +1,121 @@
@extends('layouts.app')
@section('title', 'Mon Profil')
@section('header_title', 'Gestion du compte')
@section('content')
<div class="max-w-4xl mx-auto space-y-8">
<!-- Informations Personnelles -->
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<div class="flex items-center gap-4 mb-8 border-l-4 border-[#000091] pl-6">
<h2 class="text-2xl font-black text-slate-900">Informations personnelles</h2>
</div>
@if (session('status') === 'profile-updated')
<div class="bg-teal-50 border-l-4 border-[#00635B] p-4 mb-8" role="alert">
<p class="text-[#00635B] text-sm font-bold">Vos informations ont été mises à jour avec succès.</p>
</div>
@endif
<form method="post" action="{{ route('profile.update') }}" class="space-y-6">
@csrf
@method('patch')
<div class="space-y-2">
<label for="name" class="block text-sm font-bold text-slate-700">Nom complet</label>
<input id="name" name="name" type="text" class="w-full border-2 border-slate-300 p-3 focus:border-[#000091] focus:ring-0 bg-slate-50" value="{{ old('name', $user->name) }}" required autocomplete="name">
@error('name') <p class="text-red-600 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div class="space-y-2">
<label for="email" class="block text-sm font-bold text-slate-700">Adresse électronique</label>
<input id="email" name="email" type="email" class="w-full border-2 border-slate-300 p-3 focus:border-[#000091] focus:ring-0 bg-slate-50" value="{{ old('email', $user->email) }}" required autocomplete="username">
@error('email') <p class="text-red-600 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div class="pt-4">
<button type="submit" class="px-8 py-3 bg-[#000091] text-white font-bold rounded-sm hover:bg-blue-800 transition-all shadow-md uppercase tracking-wider text-sm">
Enregistrer les modifications
</button>
</div>
</form>
</div>
<!-- Changement de Mot de Passe -->
<div class="bg-white p-8 border border-slate-200 shadow-sm">
<div class="flex items-center gap-4 mb-8 border-l-4 border-[#000091] pl-6">
<h2 class="text-2xl font-black text-slate-900">Sécurité du compte</h2>
</div>
@if (session('status') === 'password-updated')
<div class="bg-teal-50 border-l-4 border-[#00635B] p-4 mb-8" role="alert">
<p class="text-[#00635B] text-sm font-bold">Votre mot de passe a été modifié.</p>
</div>
@endif
<form method="post" action="{{ route('profile.password') }}" class="space-y-6">
@csrf
@method('put')
<div class="space-y-2">
<label for="current_password" class="block text-sm font-bold text-slate-700">Mot de passe actuel</label>
<input id="current_password" name="current_password" type="password" class="w-full border-2 border-slate-300 p-3 focus:border-[#000091] focus:ring-0 bg-slate-50" autocomplete="current-password">
@error('current_password', 'updatePassword') <p class="text-red-600 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div class="space-y-2">
<label for="password" class="block text-sm font-bold text-slate-700">Nouveau mot de passe</label>
<input id="password" name="password" type="password" class="w-full border-2 border-slate-300 p-3 focus:border-[#000091] focus:ring-0 bg-slate-50" autocomplete="new-password">
@error('password', 'updatePassword') <p class="text-red-600 text-xs mt-1">{{ $message }}</p> @enderror
</div>
<div class="space-y-2">
<label for="password_confirmation" class="block text-sm font-bold text-slate-700">Confirmer le mot de passe</label>
<input id="password_confirmation" name="password_confirmation" type="password" class="w-full border-2 border-slate-300 p-3 focus:border-[#000091] focus:ring-0 bg-slate-50" autocomplete="new-password">
</div>
<div class="pt-4">
<button type="submit" class="px-8 py-3 bg-[#000091] text-white font-bold rounded-sm hover:bg-blue-800 transition-all shadow-md uppercase tracking-wider text-sm">
Mettre à jour le mot de passe
</button>
</div>
</form>
</div>
<!-- Zone de Danger -->
<div class="bg-[#FAEFEF] p-8 border border-red-200 shadow-sm">
<div class="flex items-center gap-4 mb-6 border-l-4 border-[#E1000F] pl-6">
<h2 class="text-2xl font-black text-[#E1000F]">Suppression du compte</h2>
</div>
<p class="text-sm text-slate-700 mb-8 leading-relaxed">
Une fois votre compte supprimé, toutes vos données de santé, votre journal émotionnel et vos favoris seront définitivement effacés de nos serveurs sécurisés. Cette action est irréversible.
</p>
<button onclick="document.getElementById('delete-modal').classList.remove('hidden')" class="px-8 py-3 bg-[#E1000F] text-white font-bold rounded-sm hover:bg-red-800 transition-all shadow-md uppercase tracking-wider text-sm">
Supprimer mon compte définitivement
</button>
</div>
</div>
<!-- Modal de suppression (Simplifié pour l'exemple) -->
<div id="delete-modal" class="hidden fixed inset-0 bg-slate-900/50 backdrop-blur-sm z-[100] flex items-center justify-center p-4">
<div class="bg-white max-w-md w-full p-8 shadow-2xl border-t-8 border-[#E1000F]">
<h3 class="text-xl font-black mb-4">Êtes-vous absolument sûr ?</h3>
<p class="text-sm text-slate-600 mb-8">Veuillez saisir votre mot de passe pour confirmer la suppression définitive de votre compte.</p>
<form method="post" action="{{ route('profile.destroy') }}" class="space-y-6">
@csrf
@method('delete')
<input type="password" name="password" placeholder="Mot de passe" class="w-full border-2 border-slate-300 p-3 focus:border-[#E1000F] focus:ring-0 bg-slate-50" required>
<div class="flex gap-4">
<button type="button" onclick="document.getElementById('delete-modal').classList.add('hidden')" class="flex-1 py-3 bg-slate-100 text-slate-700 font-bold hover:bg-slate-200 transition-all">Annuler</button>
<button type="submit" class="flex-1 py-3 bg-[#E1000F] text-white font-bold hover:bg-red-800 transition-all shadow-md">Confirmer</button>
</div>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,214 @@
@extends('layouts.app')
@section('title', 'Détente & Respiration')
@section('content')
<div class="max-w-6xl mx-auto space-y-12">
<!-- Module de Respiration Guidée -->
<div x-data="breathingApp()" class="bg-white p-8 border border-slate-300 shadow-sm relative overflow-hidden">
<div class="flex flex-col lg:flex-row gap-12 items-center">
<!-- Contrôles et Choix du mode -->
<div class="flex-1 space-y-6 w-full">
<div>
<h2 class="text-2xl font-bold text-slate-900 mb-2 uppercase tracking-tight">Respiration Guidée</h2>
<p class="text-slate-500 text-sm">Sélectionnez un programme pour stabiliser votre rythme cardiaque.</p>
</div>
<div class="grid grid-cols-1 gap-4" x-show="!isStarted">
<template x-for="mode in modes" :key="mode.name">
<button
@click="selectedMode = mode"
:class="selectedMode.name === mode.name ? 'border-[--fr-blue] bg-blue-50 ring-2 ring-[--fr-blue] ring-opacity-20' : 'border-slate-300 bg-white'"
class="flex items-center gap-4 p-5 border-2 rounded-xl transition-all text-left group shadow-sm hover:border-slate-400"
>
<div :class="mode.colorClass" class="w-12 h-12 rounded-full flex items-center justify-center text-white shadow-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="w-6 h-6">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 3v17.25m0 0c-1.472 0-2.882.265-4.185.75M12 20.25c1.472 0 2.882.265 4.185.75M18.75 4.905c-.921-.154-1.862-.23-2.812-.23H9.25c-.95 0-1.891.076-2.812.23m12.324 3.248c.334.334.613.725.828 1.154M6.75 8.153a6.001 6.001 0 0 0-4.02 5.492m18.77 0c.115.624.175 1.266.175 1.921v2.684m-19.17 0v-2.684c0-.655.06-1.297.175-1.921M18 21V9a6 6 0 0 0-6-6h0a6 6 0 0 0-6 6v12" />
</svg>
</div>
<div class="flex-1">
<p class="font-bold text-slate-900 text-base" x-text="mode.name"></p>
<p class="text-xs text-slate-500 leading-tight mt-1" x-text="mode.desc"></p>
</div>
</button>
</template>
<button @click="startExercise()" class="w-full py-5 bg-[--fr-blue] text-white font-bold rounded-sm shadow-lg hover:bg-blue-800 transition-all mt-4 uppercase tracking-[0.2em] text-sm">
Lancer la session (5 min)
</button>
</div>
<!-- Affichage pendant l'exercice -->
<div class="space-y-8 py-10 text-center lg:text-left" x-show="isStarted" x-cloak>
<div>
<p class="text-6xl font-light tracking-tighter text-slate-400" x-text="formatTime(totalSeconds)"></p>
<p class="text-xs font-bold text-[--fr-blue] uppercase tracking-[0.3em] mt-3" x-text="selectedMode.name"></p>
</div>
<button @click="stopExercise()" class="px-10 py-4 border-2 border-red-200 text-red-600 font-bold rounded-sm hover:bg-red-50 transition-all uppercase text-xs tracking-widest">
Interrompre
</button>
</div>
</div>
<!-- Animation du Cercle -->
<div class="flex-1 flex flex-col items-center justify-center min-h-[450px] w-full bg-slate-50 border border-slate-200 rounded-sm relative">
<div
class="rounded-full flex items-center justify-center transition-all duration-1000 ease-in-out border-8 relative shadow-2xl"
:class="selectedMode.borderClass"
:style="'width: ' + circleSize + 'px; height: ' + circleSize + 'px; background-color: ' + selectedMode.bgOpacity"
>
<img src="{{ asset('img/CesiZen logo.png') }}" class="absolute w-2/3 opacity-10 pointer-events-none" alt="">
<div class="text-center z-10">
<p class="text-2xl font-black tracking-widest uppercase mb-1" :class="selectedMode.textClass" x-text="phaseText"></p>
<p class="text-5xl font-light" :class="selectedMode.textClass" x-show="isStarted" x-text="phaseTimer"></p>
</div>
</div>
</div>
</div>
</div>
<!-- Section Activités -->
<div class="space-y-8">
<div class="flex items-center gap-4">
<h2 class="text-xl font-bold text-slate-900 uppercase tracking-widest whitespace-nowrap">Bibliothèque Méditation</h2>
<div class="h-1 flex-1 bg-slate-100"></div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
@forelse($activities as $activity)
<div class="bg-white rounded-sm border-2 border-slate-200 overflow-hidden flex flex-col shadow-sm group hover:border-[--fr-blue] transition-all">
<div class="h-48 bg-slate-100 flex items-center justify-center relative">
@if($activity->type == 'audio')
<x-heroicon-o-musical-note class="w-16 h-16 text-slate-300 group-hover:text-blue-200 transition-colors" />
@elseif($activity->type == 'video')
<x-heroicon-o-play-circle class="w-16 h-16 text-slate-300 group-hover:text-blue-200 transition-colors" />
@else
<x-heroicon-o-document-text class="w-16 h-16 text-slate-300 group-hover:text-blue-200 transition-colors" />
@endif
<button
onclick="toggleFavorite({{ $activity->id }}, this)"
class="absolute top-4 right-4 p-2.5 rounded-full bg-white shadow-md border border-slate-200 transition-all hover:scale-110 {{ in_array($activity->id, $favorites) ? 'text-red-500 border-red-100' : 'text-slate-400' }}"
>
<x-heroicon-s-heart class="w-6 h-6" />
</button>
</div>
<div class="p-8 flex-1 flex flex-col">
<div class="flex items-center gap-2 mb-4">
<span class="px-3 py-1 bg-blue-50 text-[--fr-blue] text-[10px] font-bold uppercase rounded-sm">{{ $activity->type }}</span>
</div>
<h3 class="text-xl font-bold text-slate-900 mb-3 group-hover:text-[--fr-blue] transition-colors leading-tight">{{ $activity->title }}</h3>
<p class="text-sm text-slate-500 mb-8 flex-1 leading-relaxed">{{ $activity->description }}</p>
<a href="{{ $activity->url ?? '#' }}" target="_blank" class="w-full py-4 bg-[--fr-blue] text-white text-center text-xs font-bold rounded-sm shadow-md hover:bg-blue-800 transition-all uppercase tracking-widest">
DÉCOUVRIR L'ACTIVITÉ
</a>
</div>
</div>
@empty
<p class="text-slate-400 italic">Aucune activité disponible.</p>
@endforelse
</div>
</div>
</div>
<script>
async function toggleFavorite(activityId, btn) {
try {
const response = await fetch(`/relaxation/${activityId}/favorite`, {
method: 'POST',
headers: {
'X-CSRF-TOKEN': '{{ csrf_token() }}',
'Content-Type': 'application/json'
}
});
const data = await response.json();
if (data.status === 'added') {
btn.classList.remove('text-slate-400');
btn.classList.add('text-red-500', 'border-red-100');
} else {
btn.classList.remove('text-red-500', 'border-red-100');
btn.classList.add('text-slate-400');
}
} catch (error) {
console.error('Erreur favoris');
}
}
function breathingApp() {
return {
isStarted: false,
totalSeconds: 300,
circleSize: 220,
phaseText: 'Prêt ?',
phaseTimer: 0,
interval: null,
modes: [
{ name: '7-4-8', desc: 'Favorise l\'endormissement et la détente musculaire profonde.', inhale: 7, hold: 4, exhale: 8, colorClass: 'bg-indigo-600', borderClass: 'border-indigo-200', textClass: 'text-indigo-600', bgClass: 'bg-indigo-600', bgOpacity: 'rgba(79, 70, 229, 0.05)' },
{ name: '5-5', desc: 'Cohérence cardiaque pour équilibrer le système nerveux.', inhale: 5, hold: 0, exhale: 5, colorClass: 'bg-teal-600', borderClass: 'border-teal-200', textClass: 'text-teal-600', bgClass: 'bg-teal-600', bgOpacity: 'rgba(13, 148, 136, 0.05)' },
{ name: '4-6', desc: 'Réduction rapide de l\'anxiété par l\'expiration prolongée.', inhale: 4, hold: 0, exhale: 6, colorClass: 'bg-orange-600', borderClass: 'border-orange-200', textClass: 'text-orange-600', bgClass: 'bg-orange-600', bgOpacity: 'rgba(234, 88, 12, 0.05)' }
],
selectedMode: null,
init() {
this.selectedMode = this.modes[1];
},
async startExercise() {
this.isStarted = true;
this.totalSeconds = 300;
this.interval = setInterval(() => {
if (this.totalSeconds > 0) this.totalSeconds--;
else this.stopExercise();
}, 1000);
while (this.isStarted) {
await this.runPhase('Inspirez', this.selectedMode.inhale, 350);
if (!this.isStarted) break;
if (this.selectedMode.hold > 0) {
await this.runPhase('Bloquez', this.selectedMode.hold, 350);
if (!this.isStarted) break;
}
await this.runPhase('Expirez', this.selectedMode.exhale, 220);
}
},
async runPhase(text, seconds, targetSize) {
this.phaseText = text;
this.phaseTimer = seconds;
this.circleSize = targetSize;
for (let i = 0; i < seconds; i++) {
if (!this.isStarted) return;
await new Promise(resolve => setTimeout(resolve, 1000));
this.phaseTimer--;
}
},
stopExercise() {
this.isStarted = false;
this.phaseText = 'Prêt ?';
this.circleSize = 220;
clearInterval(this.interval);
},
formatTime(sec) {
let m = Math.floor(sec / 60);
let s = sec % 60;
return `${m}:${s < 10 ? '0' : ''}${s}`;
}
}
}
</script>
<style>
[x-cloak] { display: none !important; }
</style>
@endsection
+169
View File
@@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>CESIZen | Ministère de la Santé et de la Prévention</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600,700" rel="stylesheet" />
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
<style>
:root {
/* Official French Government Colors (DSFR) */
--fr-blue: #000091;
--fr-blue-hover: #1212ff;
--fr-red: #E1000F;
--fr-text: #161616;
--fr-background: #F6F6F6;
/* CESIZen Adapted for RGAA (High Contrast) */
--cz-navy: #000074; /* Darker than 000080 for better contrast */
--cz-mustard: #715200; /* Darkened mustard for text on white (Contrast > 7:1) */
--cz-mustard-bg: #F8B803; /* Original mustard for backgrounds only */
--cz-jade: #00635B; /* Darkened jade for text (Contrast > 4.5:1) */
--cz-jade-bg: #26A69A; /* Original jade for backgrounds */
--cz-indigo: #303F9F;
--cz-coral: #C62828;
}
/* Focus state for RGAA */
:focus-visible {
outline: 3px solid var(--fr-blue);
outline-offset: 2px;
}
.skip-link {
position: absolute;
top: -40px;
left: 0;
background: var(--fr-blue);
color: white;
padding: 8px;
z-index: 100;
transition: top 0.2s;
}
.skip-link:focus {
top: 0;
}
</style>
</head>
<body class="bg-[#F6F6F6] text-[#161616] min-h-screen font-sans antialiased overflow-x-hidden">
<a href="#main-content" class="skip-link">Aller au contenu principal</a>
<!-- Government Official Header -->
@include('layouts.header')
<div class="flex min-h-[calc(100vh-80px)]">
<!-- Institutional Sidebar -->
@include('layouts.sidebar')
<!-- Main Workspace -->
<main id="main-content" class="flex-1 flex flex-col bg-[#F6F6F6] overflow-y-auto">
<div class="p-6 lg:p-10 max-w-7xl mx-auto w-full space-y-10">
<!-- Hero Section: Accessible and Structured -->
<section class="bg-white border border-slate-200 p-8 lg:p-12 shadow-sm flex flex-col md:flex-row items-center gap-10 relative overflow-hidden">
<div class="absolute top-0 left-0 w-2 h-full bg-[--cz-mustard-bg]"></div>
<div class="flex-1 space-y-6">
<h2 class="text-4xl font-black text-slate-900 leading-tight">
Bonjour <span class="text-[--fr-blue]">{{ Auth::user()->name ?? 'Cher étudiant' }}</span>
</h2>
<p class="text-slate-700 text-lg leading-relaxed max-w-2xl">
Le <span class="font-bold">Ministère de la Santé</span> vous propose cet espace sécurisé pour suivre votre bien-être au quotidien. Retrouvez vos outils de prévention et vos diagnostics.
</p>
<div class="flex flex-wrap gap-4 pt-2">
<a href="{{ route('diagnostics.index') }}" class="px-8 py-3.5 bg-[#000091] text-white font-bold rounded-sm hover:bg-blue-800 transition-all shadow-md">
Faire mon bilan de santé
</a>
<a href="{{ route('relaxation.index') }}" class="px-8 py-3.5 bg-white border-2 border-[#000091] text-[#000091] font-bold rounded-sm hover:bg-slate-50 transition-all">
Voir mes favoris
</a>
</div>
</div>
</section>
<!-- Grid of Modules -->
<section aria-labelledby="modules-title">
<h3 id="modules-title" class="text-xl font-bold text-slate-900 mb-6 flex items-center gap-2">
Vos outils de suivi
<span class="w-8 h-1 bg-[--cz-mustard-bg] rounded-full"></span>
</h3>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<!-- Tracker (Jade) -->
<div class="bg-white p-8 border border-slate-200 shadow-sm transition-colors relative group">
<div class="w-12 h-12 rounded-sm bg-teal-50 flex items-center justify-center mb-6 text-[--cz-jade]" aria-hidden="true">
<x-heroicon-o-clock class="h-7 w-7" />
</div>
<h4 class="text-xl font-bold text-slate-900 mb-2">Journal Émotionnel</h4>
<p class="text-slate-600 text-sm leading-relaxed mb-6">Suivez l'évolution de votre état émotionnel avec l'outil d'auto-évaluation.</p>
<a href="{{ route('emotions.index') }}" class="inline-flex items-center gap-2 text-sm font-bold text-[--cz-jade] hover:underline underline-offset-4 decoration-2">
Accéder au journal
<x-heroicon-o-arrow-right class="h-4 w-4" aria-hidden="true" />
</a>
</div>
<!-- Diagnostics (Mustard) -->
<div class="bg-white p-8 border border-slate-200 shadow-sm transition-colors relative group">
<div class="w-12 h-12 rounded-sm bg-orange-50 flex items-center justify-center mb-6 text-[--cz-mustard]" aria-hidden="true">
<x-heroicon-o-chart-bar class="h-7 w-7" />
</div>
<h4 class="text-xl font-bold text-slate-900 mb-2">Tests de Stress</h4>
<p class="text-slate-600 text-sm leading-relaxed mb-6">Réalisez des diagnostics validés scientifiquement par nos experts.</p>
<a href="{{ route('diagnostics.index') }}" class="inline-flex items-center gap-2 text-sm font-bold text-[--cz-mustard] hover:underline underline-offset-4 decoration-2">
Commencer un test
<x-heroicon-o-arrow-right class="h-4 w-4" aria-hidden="true" />
</a>
</div>
<!-- Info -->
<div class="bg-white p-8 border border-slate-200 shadow-sm transition-colors relative group">
<div class="w-12 h-12 rounded-sm bg-blue-50 flex items-center justify-center mb-6 text-[--fr-blue]" aria-hidden="true">
<x-heroicon-o-document-text class="h-7 w-7" />
</div>
<h4 class="text-xl font-bold text-slate-900 mb-2">Guides Pratiques</h4>
<p class="text-slate-600 text-sm leading-relaxed mb-6">Consultez les fiches de prévention sur le sommeil et la nutrition.</p>
<a href="#" class="inline-flex items-center gap-2 text-sm font-bold text-[--fr-blue] hover:underline underline-offset-4 decoration-2">
Lire les guides
<x-heroicon-o-arrow-right class="h-4 w-4" aria-hidden="true" />
</a>
</div>
</div>
</section>
<!-- RGPD / Information Footer -->
<section class="bg-slate-100 p-8 border border-slate-200 rounded-sm">
<div class="flex flex-col md:flex-row gap-8 items-start">
<div class="w-12 h-12 bg-white rounded-sm flex items-center justify-center text-[--fr-blue] border border-slate-200 shadow-sm flex-shrink-0" aria-hidden="true">
<x-heroicon-o-shield-check class="w-6 h-6" />
</div>
<div>
<h4 class="text-lg font-bold text-slate-900 mb-2">Vos données sont protégées</h4>
<p class="text-slate-600 text-sm leading-relaxed">
Conformément au RGPD et aux recommandations de la CNIL, vos données de santé sont strictement confidentielles.
Elles sont hébergées sur des infrastructures sécurisées de l'État et ne sont jamais partagées sans votre accord.
</p>
</div>
</div>
</section>
</div>
<!-- Global Footer Gov -->
</main>
</div>
@include('layouts.footer')
</body>
</html>