<header class="bg-white border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<nav class="max-w-screen-xl flex flex-wrap items-center justify-around mx-auto p-4">
<!-- Logo -->
<a href="{{ path('app_home') }}" class="flex items-center space-x-3">
<span class="self-center text-2xl italic font-semibold whitespace-nowrap dark:text-white">🛠️ MatchMeuble</span>
</a>
<!-- Navigation Desktop -->
<div class="hidden md:flex md:items-center md:space-x-8">
<a href="{{ path('app_home') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Accueil
</a>
<a href="{{ path('app_annonces') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Annonces <sup class="text-gradient italic text-xs">New</sup>
</a>
{% if app.user %}
<a href="{{ path('app_favorites') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Mes favoris
</a>
{% endif %}
<a href="/#pricing"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Tarifs
</a>
<a href="/#faq"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Faq
</a>
<a href="/#blog"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Blog
</a>
</div>
<!-- Boutons CTA Desktop -->
<div class="hidden md:flex items-center space-x-3">
{% if app.user %}
<div class="relative">
<button id="notifDropdownButton" data-dropdown-toggle="notifDropdown" class="relative px-3 py-2 rounded-lg border border-gray-200 dark:border-gray-700">
🔔
<span id="notifBadge" class="hidden absolute -top-1 -right-1 text-xs font-semibold text-white bg-red-600 rounded-full px-1.5 py-0.5">0</span>
</button>
<div id="notifDropdown" class="z-20 hidden bg-white divide-y divide-gray-100 rounded-lg shadow w-80 dark:bg-gray-700">
<div class="flex items-center justify-between p-2">
<span class="text-sm font-medium text-gray-700 dark:text-gray-200">Notifications</span>
<button id="notifMarkAll" class="text-xs text-primary-700 hover:underline dark:text-primary-400">Tout marquer comme lu</button>
</div>
<div class="max-h-80 overflow-auto" id="notifList">
<div class="p-3 text-sm text-gray-500 dark:text-gray-400">Chargement...</div>
</div>
</div>
</div>
<a href="#"
class="text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-4 py-2 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700">
Mon compte
</a>
<a href="{{ path('app_annonces') }}"
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-blue-600 dark:hover:bg-blue-700">
Voir les annonces
</a>
{% else %}
<a href="{{ path('app_login') }}"
class="text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-4 py-2 dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700">
Connexion
</a>
<a href="#"
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 dark:bg-blue-600 dark:hover:bg-blue-700">
Essayer gratuitement
</a>
{% endif %}
</div>
<!-- Bouton Mobile -->
<button type="button"
class="md:hidden inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600"
id="mobile-menu-button">
<span class="sr-only">Ouvrir le menu</span>
<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="M4 6h16M4 12h16M4 18h16">
</path>
</svg>
</button>
<!-- Menu Mobile -->
<div class="hidden w-full md:hidden" id="mobile-menu">
<ul class="flex flex-col font-medium mt-4 rounded-lg bg-gray-50 dark:bg-gray-800 dark:border-gray-700">
{% if is_granted('ROLE_ADMIN') %}
<a href="{{ path('admin') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Administration
</a>
{% endif %}
<a href="{{ path('myAccount') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Mon compte
</a>
{% if app.user %}
<a href="{{ path('app_favorites') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Mes favoris
</a>
{% endif %}
<a href="{{ path('abonnement') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Abonnements <sup class="text-gradient italic text-xs">New</sup>
</a>
<a href="{{ path('abonnement') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Abonnements <sup class="text-gradient italic text-xs">New</sup>
</a>
<a href="{{ path('app_logout') }}"
class="text-gray-900 hover:text-blue-700 px-3 py-2 text-sm font-medium dark:text-white dark:hover:text-blue-500">
Déconnexion
</a>
{% if app.user %}
<li>
<a href="{{ path('quote_index') }}"
class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">
Mes Devis
</a>
</li>
{% endif %}
<li>
<a href="#"
class="block py-2 px-3 text-gray-900 rounded hover:bg-gray-100 dark:text-white dark:hover:bg-gray-700">
Blog
</a>
</li>
</ul>
<!-- Boutons CTA Mobile -->
<div class="flex flex-col space-y-2 mt-4 px-3 pb-3">
{% if app.user %}
<a href="{{ path('app_account') }}"
class="text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700">
Tableau de bord
</a>
<a href="{{ path('app_annonces') }}"
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700">
Annonces
</a>
{% else %}
<a href="{{ path('app_login') }}"
class="text-gray-900 bg-white border border-gray-300 hover:bg-gray-100 focus:ring-4 focus:ring-gray-100 font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-gray-800 dark:text-white dark:border-gray-600 dark:hover:bg-gray-700">
Connexion
</a>
<a href="{{ path('app_annonces') }}"
class="text-white bg-blue-700 hover:bg-blue-800 focus:ring-4 focus:ring-blue-300 font-medium rounded-lg text-sm px-4 py-2 text-center dark:bg-blue-600 dark:hover:bg-blue-700">
Annonces
</a>
{% endif %}
</div>
</div>
</nav>
</header>
<script>
// Script pour le menu mobile
document.addEventListener('DOMContentLoaded', function () {
const mobileMenuButton = document.getElementById('mobile-menu-button');
const mobileMenu = document.getElementById('mobile-menu');
if (mobileMenuButton && mobileMenu) {
mobileMenuButton.addEventListener('click', function () {
mobileMenu.classList.toggle('hidden');
});
}
});
</script>
<script>
// Lightweight fetch of notifications when opening the dropdown
document.addEventListener('DOMContentLoaded', () => {
const btn = document.getElementById('notifDropdownButton');
const list = document.getElementById('notifList');
const badge = document.getElementById('notifBadge');
const markAll = document.getElementById('notifMarkAll');
const csrf = '{{ csrf_token('notif') }}';
if (!btn || !list || !badge) return;
async function refreshCount() {
try {
const res = await fetch('{{ path('app_notifications_count') }}', { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
const data = await res.json();
const c = (data && typeof data.count === 'number') ? data.count : 0;
if (c > 0) {
badge.textContent = c;
badge.classList.remove('hidden');
} else {
badge.classList.add('hidden');
}
} catch {}
}
async function loadList() {
try {
const res = await fetch('{{ path('app_notifications') }}', { headers: { 'X-Requested-With': 'XMLHttpRequest' } });
const data = await res.json();
list.innerHTML = '';
if (!data || data.length === 0) {
list.innerHTML = '<div class="p-3 text-sm text-gray-500 dark:text-gray-400">Aucune notification</div>';
} else {
data.forEach(n => {
const el = document.createElement('div');
el.className = 'p-3 text-sm text-gray-800 dark:text-gray-200 border-b border-gray-100 dark:border-gray-600';
el.dataset.id = n.id;
el.innerHTML = `
<div class="flex items-start justify-between gap-2">
<div class="flex items-start gap-2">
${n.lu ? '' : '<span class="mt-1 inline-block w-2 h-2 rounded-full bg-red-500"></span>'}
<span>${n.message}</span>
</div>
<div class="flex items-center gap-2">
${n.lu ? '' : '<button class="notif-mark text-xs text-primary-700 hover:underline dark:text-primary-400">Marquer comme lu</button>'}
<button class="notif-del text-xs text-red-700 hover:underline dark:text-red-400" title="Supprimer" aria-label="Supprimer">
🗑️
</button>
</div>
</div>
`;
list.appendChild(el);
});
}
} catch (e) {
list.innerHTML = '<div class="p-3 text-sm text-red-600">Erreur de chargement</div>';
}
}
btn.addEventListener('click', async () => {
await loadList();
});
if (markAll) {
markAll.addEventListener('click', async (e) => {
e.preventDefault();
try {
const res = await fetch('{{ path('app_notifications_read') }}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': csrf },
body: JSON.stringify({ all: true })
});
if (res.ok) {
await loadList();
await refreshCount();
}
} catch {}
});
}
// Event delegation for per-notification "mark as read"
list.addEventListener('click', async (e) => {
const btn = e.target.closest('.notif-mark');
if (!btn) return;
const container = btn.closest('[data-id]');
const id = container?.dataset?.id;
if (!id) return;
try {
const res = await fetch('{{ path('app_notifications_read') }}', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': csrf },
body: JSON.stringify({ ids: [parseInt(id, 10)] })
});
if (res.ok) {
await loadList();
await refreshCount();
}
} catch {}
});
list.addEventListener('click', async (e) => {
const btn = e.target.closest('.notif-del');
if (!btn) return;
const container = btn.closest('[data-id]');
const id = container?.dataset?.id;
if (!id) return;
try {
const res = await fetch('{{ path('app_notifications_delete', { id: 0 }) }}'.replace('/0', '/' + id), {
method: 'DELETE',
headers: { 'X-Requested-With': 'XMLHttpRequest', 'X-CSRF-TOKEN': csrf },
});
if (res.ok) {
await loadList();
await refreshCount();
}
} catch {}
});
refreshCount();
setInterval(refreshCount, 30000);
});
</script>