import type { Metadata } from "next"; import { Geist } from "next/font/google"; import "./globals.css"; import { getSettings } from "@/lib/db/settings"; import { COLOR_VARS, DEFAULT_COLORS, faviconUrl, SITE_TITLE } from "@/lib/types"; import { getDictionary, getLocale } from "@/lib/i18n"; import { listLanguages } from "@/lib/db/languages"; import { LocaleProvider } from "@/components/i18n/LocaleProvider"; import packageJson from "@/package.json"; // Farben/Logo/Favicon sollen Änderungen sofort widerspiegeln. export const dynamic = "force-dynamic"; const geistSans = Geist({ variable: "--font-geist-sans", subsets: ["latin"], }); export async function generateMetadata(): Promise { const settings = await getSettings(); return { title: SITE_TITLE, description: "Speisekarte der Betriebskantine", // Immer explizit setzen: app/favicon.ico würde sonst zusätzlich zu // einem eigenen Favicon eingebunden (Next.js kombiniert Datei- // Konvention und Metadata statt sie zu ersetzen). icons: { icon: faviconUrl(settings?.favicon_path) ?? "/favicon.ico" }, // Autodiscovery für Feedreader (Browser zeigen dafür kein eigenes // Icon mehr an, daher zusätzlich ein sichtbares RSS-Icon im Header // der öffentlichen Speisekarte, s. app/page.tsx). alternates: { types: { "application/rss+xml": [{ url: "/feed.xml", title: `${SITE_TITLE} – Speiseplan` }], }, }, }; } export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { const [settings, locale, allLanguages] = await Promise.all([ getSettings(), getLocale(), listLanguages(), ]); const dict = await getDictionary(locale); const languages = allLanguages .filter((l) => l.active) .map((l) => ({ code: l.code, label: l.label })); const colors: Record = { ...DEFAULT_COLORS, ...(settings?.colors ?? {}), }; const style = Object.fromEntries( Object.entries(COLOR_VARS).map(([key, cssVar]) => [cssVar, colors[key]]) ) as React.CSSProperties; return ( {children} ); }