HifiPlanet-Website/hifi-src/src/components/PublicLayout.jsx
Maaxxs e282a981a6 Add German/English language toggle for the storefront
Auto-detects the visitor's browser language on first visit, with a
manual dropdown switcher (flag icons) in the header and mobile menu.
Legal pages, the admin panel, and DB-driven content stay German-only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-06 21:51:32 +02:00

78 lines
2.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useEffect, useState } from 'react';
import { Outlet, useLocation } from 'react-router-dom';
import Navbar from './Navbar.jsx';
import Footer from './Footer.jsx';
import ScrollProgress from './ScrollProgress.jsx';
import MaintenanceNotice from './MaintenanceNotice.jsx';
import MaintenanceBypassBanner from './MaintenanceBypassBanner.jsx';
import CookieConsentBanner from './CookieConsentBanner.jsx';
import { MaintenanceProvider } from '../context/MaintenanceContext.jsx';
import { SiteSettingsProvider } from '../context/SiteSettingsContext.jsx';
import { LanguageProvider } from '../context/LanguageContext.jsx';
import { useAuth } from '../context/AuthContext.jsx';
import { api } from '../api/client.js';
const FALLBACK_STATUS = {
global: { enabled: false, message: null },
services: { enabled: false, message: null },
vehicles: { enabled: false, message: null },
};
const DEFAULT_SITE_SETTINGS = {
phone: '09373 20 62 390',
whatsapp: null,
contact_email: 'info@hifi-planet-amorbach.de',
hero_image_path: null,
};
// Das Impressum (und die anderen rechtlichen Pflichtseiten) müssen laut § 5 DDG
// "leicht erkennbar und unmittelbar erreichbar" bleiben auch während der
// generellen Wartung darf diese Erreichbarkeit nicht durch die Wartungsseite
// verdeckt werden.
const LEGAL_PATHS = ['/impressum', '/datenschutz', '/agb'];
export default function PublicLayout() {
const [status, setStatus] = useState(null);
const [siteSettings, setSiteSettings] = useState(DEFAULT_SITE_SETTINGS);
const { loading: authLoading, hasPermission } = useAuth();
const location = useLocation();
useEffect(() => {
api.get('/maintenance').then(setStatus).catch(() => setStatus(FALLBACK_STATUS));
api.get('/site-settings').then(setSiteSettings).catch(() => {});
}, []);
if (status === null || authLoading) {
return <div className="min-h-screen bg-white dark:bg-neutral-950" />;
}
const bypass = hasPermission('maintenance.bypass');
const isLegalPage = LEGAL_PATHS.includes(location.pathname);
if (status.global.enabled && !bypass && !isLegalPage) {
return (
<LanguageProvider>
<MaintenanceNotice message={status.global.message} fullScreen />
</LanguageProvider>
);
}
return (
<LanguageProvider>
<MaintenanceProvider value={{ ...status, bypass }}>
<SiteSettingsProvider value={siteSettings}>
<div className="page-texture flex min-h-screen flex-col text-neutral-900 dark:text-neutral-100">
{bypass && status.global.enabled && <MaintenanceBypassBanner />}
<ScrollProgress />
<Navbar />
<main className="flex-1">
<Outlet />
</main>
<Footer />
<CookieConsentBanner />
</div>
</SiteSettingsProvider>
</MaintenanceProvider>
</LanguageProvider>
);
}