File: //proc/thread-self/root/tmp/AppLayout.jsx
import React, { useEffect, useState } from 'react';
import { Link, Outlet, useLocation, useNavigate } from 'react-router-dom';
import { Matomo } from './Matomo.jsx';
import { Adsense } from './Adsense.jsx';
import 'preline';
import { apiFetch } from './api.js';
import { AuthModal } from '../auth/AuthModal.jsx';
import { ChatWidget } from '../chat/ChatWidget.jsx';
import { featuredSeoLinks, seoHub } from '../seo/catalog.js';
import { buildUploadPath } from '../../utils/tracking.js';
export const AppLayout = () => {
const location = useLocation();
const navigate = useNavigate();
const [me, setMe] = useState(null);
const [authOpen, setAuthOpen] = useState(false);
useEffect(() => {
// Auto-init Preline components after navigation
try { window.HSStaticMethods?.autoInit?.(); } catch {}
}, [location.pathname]);
useEffect(() => {
let cancelled = false;
const loadMe = async () => {
try {
const res = await apiFetch('/api/auth/me');
if (!cancelled && res.ok) {
const data = await res.json().catch(() => ({}));
setMe(data.user || null);
} else if (!cancelled) {
setMe(null);
}
} catch {
if (!cancelled) setMe(null);
}
};
loadMe();
const onAuthChanged = () => loadMe();
const onAuthOpen = () => setAuthOpen(true);
window.addEventListener('auth:changed', onAuthChanged);
window.addEventListener('auth:open', onAuthOpen);
return () => {
cancelled = true;
window.removeEventListener('auth:changed', onAuthChanged);
window.removeEventListener('auth:open', onAuthOpen);
};
}, [location.pathname]);
const onLogout = async () => {
try {
await apiFetch('/api/auth/logout', { method: 'POST' });
} finally {
try { window.dispatchEvent(new Event('auth:changed')); } catch {}
navigate('/');
}
};
return (
<div className="min-h-screen flex flex-col">
<Matomo />
<Adsense />
<header className="sticky top-0 z-40 bg-white/80 backdrop-blur border-b">
<div className="mx-auto max-w-6xl px-4 py-3 flex items-center justify-between">
<Link to="/" className="text-2xl font-black tracking-tight text-coral-500" aria-label="bewertemich.de Startseite">
bewertemich.de
</Link>
<nav className="flex items-center gap-4 text-sm">
<Link to={buildUploadPath('header_nav')} className="btn border-0 bg-coral-500 text-white hover:bg-coral-600">Upload</Link>
<Link to="/top10" className="btn btn-ghost hidden sm:inline-flex">Top 10</Link>
<a href={`/${seoHub.slug}/`} className="btn btn-ghost hidden md:inline-flex">Ratgeber</a>
{me && <Link to="/messages" className="btn btn-ghost">Nachrichten</Link>}
{me && <Link to="/wallet" className="btn btn-ghost">Credits</Link>}
{me?.is_admin ? (<Link to="/admin" className="btn btn-ghost">Admin</Link>) : null}
{!me ? (
<button className="btn btn-ghost hidden md:inline-flex" onClick={() => setAuthOpen(true)}>Login / Registrieren</button>
) : (
<div className="dropdown dropdown-end">
<div tabIndex={0} role="button" className="btn btn-ghost btn-circle avatar">
<div className="w-9 rounded-full">
{me?.avatar_photo_id ? (
<img alt="Avatar" src={`/api/photos/file/${me.avatar_photo_id}`} />
) : (
<div className="w-full h-full bg-gray-200 flex items-center justify-center text-xs font-semibold rounded-full">{(me.username||'?').slice(0,2).toUpperCase()}</div>
)}
</div>
</div>
<ul tabIndex={0} className="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li><Link to={`/u/${me.username}`}>Profil ansehen</Link></li>
<li><Link to="/settings">Einstellungen</Link></li>
<li><button onClick={onLogout}>Abmelden</button></li>
</ul>
</div>
)}
</nav>
</div>
</header>
<main className="flex-1">
<Outlet />
</main>
<footer className="border-t py-10 text-sm text-gray-600">
<div className="mx-auto max-w-6xl px-4 flex flex-wrap items-center justify-between gap-4">
<p>© {new Date().getFullYear()} bewertemich.de</p>
<nav className="flex items-center gap-4 flex-wrap">
<a href={`/${seoHub.slug}/`} className="link">#profilbild ratgeber</a>
{featuredSeoLinks.map((item) => (
<a key={item.slug} href={`/${item.slug}/`} className="link">#{item.slug.replace(/-/g, ' ')}</a>
))}
<a href="https://panomity.de/rechtliches/impressum/" target="_blank" rel="noreferrer">Impressum</a>
<a href="https://panomity.de/rechtliches/datenschutz/" target="_blank" rel="noreferrer">Datenschutz</a>
</nav>
</div>
</footer>
<AuthModal open={authOpen} onClose={() => setAuthOpen(false)} onSuccess={() => setAuthOpen(false)} />
<ChatWidget />
</div>
);
};