Next.js 15 App Router mit öffentlicher Übersicht und geschütztem Admin-Bereich zur Pflege von Datenschutzerklärungen nach Art. 12ff. DSGVO. - Oberfläche auf Basis von shadcn/ui, Inter lokal eingebunden - Prisma/SQLite, Auth.js mit Credentials-Provider und Rollen - Massenimport der Merkblätter des Amtes Leezen aus PDF - Docker-Image (standalone) und Gitea-Workflow zur Veröffentlichung Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
55 lines
2.1 KiB
TypeScript
55 lines
2.1 KiB
TypeScript
import type { Metadata } from "next"
|
||
import localFont from "next/font/local"
|
||
|
||
import { AuthProvider } from "@/components/providers/session-provider"
|
||
import { Toaster } from "@/components/ui/sonner"
|
||
import "./globals.css"
|
||
|
||
// Lokal ausgeliefert aus /font/Inter – keine Anfrage an Google Fonts.
|
||
const inter = localFont({
|
||
src: [
|
||
{ path: "../font/Inter/Inter_18pt-Regular.ttf", weight: "400", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-Italic.ttf", weight: "400", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-Medium.ttf", weight: "500", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-MediumItalic.ttf", weight: "500", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-SemiBold.ttf", weight: "600", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-SemiBoldItalic.ttf", weight: "600", style: "italic" },
|
||
{ path: "../font/Inter/Inter_18pt-Bold.ttf", weight: "700", style: "normal" },
|
||
{ path: "../font/Inter/Inter_18pt-BoldItalic.ttf", weight: "700", style: "italic" },
|
||
],
|
||
variable: "--font-inter",
|
||
display: "swap",
|
||
fallback: ["system-ui", "Segoe UI", "Arial", "sans-serif"],
|
||
})
|
||
|
||
export const metadata: Metadata = {
|
||
title: {
|
||
default: "Syncova Policies",
|
||
template: "%s · Syncova Policies",
|
||
},
|
||
description: "Datenschutzerklärungen nach DSGVO – transparent und zentral verwaltet",
|
||
}
|
||
|
||
export default function RootLayout({
|
||
children,
|
||
}: {
|
||
children: React.ReactNode
|
||
}) {
|
||
return (
|
||
<html lang="de" className={inter.variable} suppressHydrationWarning>
|
||
<head>
|
||
<script
|
||
// Verhindert ein Aufblitzen des hellen Themes vor der Hydration.
|
||
dangerouslySetInnerHTML={{
|
||
__html: `(function(){try{var t=localStorage.getItem('theme')||'system';var d=t==='dark'||(t==='system'&&window.matchMedia('(prefers-color-scheme: dark)').matches);document.documentElement.classList.add(d?'dark':'light');document.documentElement.style.colorScheme=d?'dark':'light';}catch(e){}})();`,
|
||
}}
|
||
/>
|
||
</head>
|
||
<body className={inter.className}>
|
||
<AuthProvider>{children}</AuthProvider>
|
||
<Toaster />
|
||
</body>
|
||
</html>
|
||
)
|
||
}
|