syncova-policies/components/legal-page.tsx
Weapie 265ca8ac38
All checks were successful
Container-Image bauen und veröffentlichen / build-and-push (push) Successful in 5m15s
Improve accessibility and access controls; verify production and database recovery
2026-09-14 11:44:41 +02:00

114 lines
4.0 KiB
TypeScript
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 { notFound } from "next/navigation"
import { PublicHeader } from "@/components/public-header"
import { SiteFooter } from "@/components/site-footer"
import { markdownToHtml } from "@/lib/markdown"
import {
SITE_PAGE_META,
SitePageKey,
hasSitePageContent,
isAllowedEmbedUrl,
isSafeMediaUrl,
} from "@/lib/site-pages"
import { readSitePages } from "@/lib/site-pages.server"
/**
* Rendert eine der rechtlichen Seiten aus dem im Admin gepflegten Markdown.
*
* Serverseitig gerendert: Der Text steht ohne JavaScript im Dokument und ist
* damit auch für Screenreader, Suchmaschinen und Archivierung erreichbar.
* Ist nichts gepflegt, gibt es die Seite nicht – ein leeres Impressum wäre
* irreführender als ein 404.
*/
export async function LegalPage({ page }: { page: SitePageKey }) {
const pages = await readSitePages()
const content = pages[page].trim()
const meta = SITE_PAGE_META[page]
// Auf der Gebärdensprach-Seite ist das Video der eigentliche Inhalt. Sie
// erscheint deshalb auch dann, wenn nur eine Videoadresse hinterlegt ist –
// ein zusätzlicher Text ist willkommen, aber keine Bedingung.
const video =
page === "signLanguage" && isSafeMediaUrl(pages.signLanguageVideo)
? pages.signLanguageVideo.trim()
: ""
if (!hasSitePageContent(pages, page)) notFound()
const html = content ? await markdownToHtml(content) : ""
const captions = isSafeMediaUrl(pages.signLanguageCaptions)
? pages.signLanguageCaptions.trim()
: ""
return (
<div className="flex min-h-screen flex-col bg-background">
<PublicHeader subtitle={meta.title} />
<main id="inhalt" tabIndex={-1} className="mx-auto w-full max-w-3xl flex-1 px-4 py-6 sm:px-6 sm:py-8">
<h1 className="mb-6 text-2xl font-semibold tracking-tight">
{meta.title}
</h1>
{/* Erläuterungen in Deutscher Gebärdensprache nach § 4 BITV 2.0.
Der native Player ist mit der Tastatur bedienbar; Untertitel
werden eingebunden, sobald eine WebVTT-Datei hinterlegt ist. */}
{page === "signLanguage" && video && (
<figure className="mb-8">
{isAllowedEmbedUrl(video) ? (
// Player eines zugelassenen Anbieters. Die Attribute setzt die
// Anwendung, nicht der Text im Admin – so lässt sich über das
// Adressfeld nichts anderes einschleusen.
<div className="aspect-video w-full overflow-hidden rounded-lg border bg-muted">
<iframe
src={video}
title="Gebärdensprach-Video des Amts Leezen"
referrerPolicy="no-referrer"
allow="autoplay; clipboard-write; encrypted-media; fullscreen; picture-in-picture"
allowFullScreen
className="size-full border-0"
/>
</div>
) : (
<video
controls
preload="metadata"
className="w-full rounded-lg border bg-muted"
>
<source src={video} />
{captions && (
<track
src={captions}
kind="captions"
srcLang="de"
label="Deutsch"
default
/>
)}
Ihr Browser kann dieses Video nicht abspielen.{" "}
<a href={video}>Video herunterladen</a>
</video>
)}
<figcaption className="mt-2 text-sm text-muted-foreground">
Video in Deutscher Gebärdensprache.{" "}
<a className="underline" href={video}>Video direkt öffnen</a>
</figcaption>
</figure>
)}
<div
className="markdown-content"
dangerouslySetInnerHTML={{ __html: html }}
/>
</main>
<SiteFooter />
</div>
)
}
/** Seitentitel für den Browser-Tab. */
export function legalPageMetadata(page: SitePageKey) {
return { title: SITE_PAGE_META[page].title }
}