Files
mesreleves-php/resources/views/layouts/navigation.blade.php
T
yann64 f530f55577 Mode sombre, option désactivation mises à jour, user-picker avec recherche
- Dark mode complet : darkMode:'class' Tailwind, sélecteur clair/sombre/auto
  dans la navigation (mémorisé dans localStorage, sans flash au chargement) ;
  53 vues et 8 composants Breeze mis à jour avec classes dark:
- Composant user-picker : fenêtre modale avec recherche temps réel (nom/email)
  remplace les <select> d'ajout de membres dans sections et sources
- Paramètres : option "Désactiver la vérification automatique des mises à jour"
  (case à cochage auto-soumise, route POST parametres/updates)
- Panneau "Paramètres généraux" remonté en tête de la page de paramètres
- README recentré sur l'installation manuelle hébergement PHP+MySQL
- VERSION 1.0.1

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-04 19:46:22 +02:00

290 lines
19 KiB
PHP

<nav x-data="{ open: false }" class="bg-white dark:bg-gray-800 border-b border-gray-100 dark:border-gray-700">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="{{ route('dashboard') }}" class="flex items-center">
@if($siteLogoUrl)
<img src="{{ $siteLogoUrl }}" alt="{{ config('app.name') }}"
class="block w-auto object-contain"
style="max-height: 40px; max-width: 200px;">
@else
<span class="font-semibold text-gray-800 dark:text-gray-200 text-lg">{{ config('app.name') }}</span>
@endif
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
Tableau de bord
</x-nav-link>
<x-nav-link :href="route('sources.index')" :active="request()->routeIs('sources.*') && !request()->routeIs('sources.releves.*')">
Sources
</x-nav-link>
<x-nav-link :href="route('lieux.index')" :active="request()->routeIs('lieux.*')">
Lieux
</x-nav-link>
<x-nav-link :href="route('recherche')" :active="request()->routeIs('recherche')">
Recherche
</x-nav-link>
@if(auth()->user()->isSectionManager())
<div class="hidden sm:flex sm:items-center">
<x-dropdown align="left" width="w-56">
<x-slot name="trigger">
<button class="inline-flex items-center px-1 pt-1 border-b-2 text-sm font-medium leading-5 transition duration-150 ease-in-out
{{ request()->routeIs('admin.*') ? 'border-indigo-400 text-gray-900 dark:text-white' : 'border-transparent text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 hover:border-gray-300 dark:hover:border-gray-600' }}">
Administration
<svg class="ms-1 h-4 w-4 fill-current" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
</button>
</x-slot>
<x-slot name="content">
@if(auth()->user()->isAdmin())
<x-dropdown-link :href="route('admin.dashboard')"
class="{{ request()->routeIs('admin.dashboard') ? 'font-semibold text-indigo-700 bg-indigo-50' : '' }}">
Tableau de bord admin
</x-dropdown-link>
<x-dropdown-link :href="route('admin.utilisateurs.index')">
Utilisateurs
</x-dropdown-link>
@endif
<x-dropdown-link :href="route('admin.sections.index')">
Sections
</x-dropdown-link>
@if(auth()->user()->isAdmin())
<x-dropdown-link :href="route('admin.depots.index')">
Dépôts d'archives
</x-dropdown-link>
<x-dropdown-link :href="route('admin.source-types.index')">
Types de sources
</x-dropdown-link>
<x-dropdown-link :href="route('admin.lieu-types.index')">
Types de lieux
</x-dropdown-link>
<div class="border-t border-gray-100 dark:border-gray-700 my-1"></div>
<x-dropdown-link :href="route('admin.parametres')">
Paramètres du site
</x-dropdown-link>
@endif
</x-slot>
</x-dropdown>
</div>
@endif
</div>
</div>
<!-- Right side: theme toggle + notifications + user menu -->
<div class="hidden sm:flex sm:items-center sm:gap-1">
<!-- Sélecteur de thème -->
<div x-data="{
theme: localStorage.getItem('colorTheme') || 'auto',
apply(t) {
this.theme = t;
localStorage.setItem('colorTheme', t);
var dark = t === 'dark' || (t === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.classList.toggle('dark', dark);
}
}" class="flex items-center">
<div class="flex items-center bg-gray-100 dark:bg-gray-700 rounded-full p-0.5 gap-0.5">
<!-- Clair -->
<button @click="apply('light')"
:class="theme === 'light' ? 'bg-white dark:bg-gray-600 shadow text-gray-800 dark:text-gray-100' : 'text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300'"
class="p-1.5 rounded-full transition-all" title="Mode clair">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707M17.657 17.657l-.707-.707M6.343 6.343l-.707-.707M12 7a5 5 0 100 10A5 5 0 0012 7z"/>
</svg>
</button>
<!-- Automatique -->
<button @click="apply('auto')"
:class="theme === 'auto' ? 'bg-white dark:bg-gray-600 shadow text-gray-800 dark:text-gray-100' : 'text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300'"
class="p-1.5 rounded-full transition-all" title="Automatique (système)">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
</button>
<!-- Sombre -->
<button @click="apply('dark')"
:class="theme === 'dark' ? 'bg-white dark:bg-gray-600 shadow text-gray-800 dark:text-gray-100' : 'text-gray-400 dark:text-gray-500 hover:text-gray-600 dark:hover:text-gray-300'"
class="p-1.5 rounded-full transition-all" title="Mode sombre">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
</svg>
</button>
</div>
</div>
<!-- Notifications -->
@php $unreadCount = auth()->user()->unreadNotifications->count(); @endphp
<a href="{{ route('notifications.index') }}"
class="relative p-2 text-gray-500 dark:text-gray-400 hover:text-indigo-600 dark:hover:text-indigo-400 transition-colors"
title="Notifications">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/>
</svg>
@if($unreadCount > 0)
<span class="absolute top-1 right-1 inline-flex items-center justify-center w-4 h-4 text-xs font-bold text-white bg-red-500 rounded-full">
{{ $unreadCount > 9 ? '9+' : $unreadCount }}
</span>
@endif
</a>
<!-- Menu utilisateur -->
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 dark:text-gray-400 bg-white dark:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-200 focus:outline-none transition ease-in-out duration-150">
<div>{{ Auth::user()->name }}</div>
<div class="ms-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
<div class="px-4 py-2 text-xs text-gray-400 dark:text-gray-500 border-b border-gray-100 dark:border-gray-700">
{{ Auth::user()->role->label() }}
</div>
<x-dropdown-link :href="route('profile.edit')">
Mon profil
</x-dropdown-link>
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-dropdown-link :href="route('logout')"
onclick="event.preventDefault(); this.closest('form').submit();">
Se déconnecter
</x-dropdown-link>
</form>
</x-slot>
</x-dropdown>
</div>
<!-- Hamburger (mobile) -->
<div class="-me-2 flex items-center sm:hidden">
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 dark:text-gray-500 hover:text-gray-500 dark:hover:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 focus:outline-none transition duration-150 ease-in-out">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
<!-- Menu responsive (mobile) -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
<div class="pt-2 pb-3 space-y-1">
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
Tableau de bord
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('sources.index')" :active="request()->routeIs('sources.*')">
Sources
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('lieux.index')" :active="request()->routeIs('lieux.*')">
Lieux
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('recherche')" :active="request()->routeIs('recherche')">
Recherche
</x-responsive-nav-link>
@if(auth()->user()->isSectionManager())
@if(auth()->user()->isAdmin())
<x-responsive-nav-link :href="route('admin.dashboard')" :active="request()->routeIs('admin.dashboard')">
Tableau de bord admin
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('admin.utilisateurs.index')" :active="request()->routeIs('admin.utilisateurs.*')">
Utilisateurs
</x-responsive-nav-link>
@endif
<x-responsive-nav-link :href="route('admin.sections.index')" :active="request()->routeIs('admin.sections.*')">
Sections
</x-responsive-nav-link>
@if(auth()->user()->isAdmin())
<x-responsive-nav-link :href="route('admin.depots.index')" :active="request()->routeIs('admin.depots.*')">
Dépôts d'archives
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('admin.source-types.index')" :active="request()->routeIs('admin.source-types.*')">
Types de sources
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('admin.lieu-types.index')" :active="request()->routeIs('admin.lieu-types.*')">
Types de lieux
</x-responsive-nav-link>
<x-responsive-nav-link :href="route('admin.parametres')">
Paramètres du site
</x-responsive-nav-link>
@endif
@endif
</div>
<!-- Options utilisateur (mobile) -->
<div class="pt-4 pb-1 border-t border-gray-200 dark:border-gray-700">
<div class="px-4">
<div class="font-medium text-base text-gray-800 dark:text-gray-200">{{ Auth::user()->name }}</div>
<div class="font-medium text-sm text-gray-500 dark:text-gray-400">{{ Auth::user()->email }}</div>
<div class="text-xs text-gray-400 dark:text-gray-500">{{ Auth::user()->role->label() }}</div>
</div>
<!-- Sélecteur de thème mobile -->
<div x-data="{
theme: localStorage.getItem('colorTheme') || 'auto',
apply(t) {
this.theme = t;
localStorage.setItem('colorTheme', t);
var dark = t === 'dark' || (t === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.classList.toggle('dark', dark);
}
}" class="px-4 pt-3 pb-1">
<p class="text-xs text-gray-400 dark:text-gray-500 mb-2">Apparence</p>
<div class="flex gap-2">
<button @click="apply('light')"
:class="theme === 'light' ? 'bg-indigo-50 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 border-indigo-300 dark:border-indigo-600' : 'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400'"
class="flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs border rounded-md transition-colors">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364-6.364l-.707.707M6.343 17.657l-.707.707M17.657 17.657l-.707-.707M6.343 6.343l-.707-.707M12 7a5 5 0 100 10A5 5 0 0012 7z"/>
</svg>
Clair
</button>
<button @click="apply('auto')"
:class="theme === 'auto' ? 'bg-indigo-50 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 border-indigo-300 dark:border-indigo-600' : 'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400'"
class="flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs border rounded-md transition-colors">
<svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.75 17L9 20l-1 1h8l-1-1-.75-3M3 13h18M5 17h14a2 2 0 002-2V5a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
</svg>
Auto
</button>
<button @click="apply('dark')"
:class="theme === 'dark' ? 'bg-indigo-50 dark:bg-indigo-900/50 text-indigo-700 dark:text-indigo-300 border-indigo-300 dark:border-indigo-600' : 'border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-400'"
class="flex-1 flex items-center justify-center gap-1.5 py-1.5 text-xs border rounded-md transition-colors">
<svg class="w-3.5 h-3.5" fill="currentColor" viewBox="0 0 24 24">
<path d="M21 12.79A9 9 0 1111.21 3 7 7 0 0021 12.79z"/>
</svg>
Sombre
</button>
</div>
</div>
<div class="mt-3 space-y-1">
<x-responsive-nav-link :href="route('profile.edit')">Mon profil</x-responsive-nav-link>
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-responsive-nav-link :href="route('logout')"
onclick="event.preventDefault(); this.closest('form').submit();">
Se déconnecter
</x-responsive-nav-link>
</form>
</div>
</div>
</div>
</nav>