Baustein 2: Inhalte einlesen und pruefen, Handouts, Suche, Glossar
Die fachlichen Inhalte liegen als Dateien unter content/ und werden beim Start eingelesen, geprueft und in den Speicher geladen. - Inhaltslader mit Zod-Schemata und Querverweispruefung - Fehlermeldungen nennen Datei und Zeile, gesammelt statt beim ersten Fehler geworfen; die Anwendung startet bei Fehlern nicht - npm run inhalte:pruefen prueft ohne Start - Handout-Ansicht mit festem Aufbau, Uebersicht nach Themenbloecken - Volltextsuche mit Gewichtung und Trefferausschnitt - Gelesen-Markierung je Handout - Glossar mit Suche und automatischer Verlinkung in den Handouts - Beispielinhalte: 4 Wochen, 12 Handouts, 24 Merkkarten, 4 Aufgaben, 4 Checklisten, 2 Lernzielkontrollen, 24 Glossareintraege - rahmenplan.yaml aus FIAusbV 2020 und KMK-Rahmenlehrplan, mit Fundstellen - gray-matter entfernt, YAML-Kopf wird selbst herausgeloest - npm run screenshots nimmt die Bilder fuer die Anleitung auf - 39 neue Tests, insgesamt 68 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e856349cd4
commit
1af5613e9b
@@ -0,0 +1,72 @@
|
||||
import type { Metadata } from 'next';
|
||||
import Link from 'next/link';
|
||||
import { erfordereBenutzer } from '@/lib/auth/rechte';
|
||||
import { inhalte } from '@/lib/inhalte';
|
||||
import { glossarAnker } from '@/lib/inhalte/glossarverweise';
|
||||
import { normalisiere } from '@/lib/inhalte/laden';
|
||||
import { GlossarSuche } from './GlossarSuche';
|
||||
|
||||
export const metadata: Metadata = { title: 'Glossar' };
|
||||
export const dynamic = 'force-dynamic';
|
||||
|
||||
export default async function GlossarSeite({ searchParams }: { searchParams: Promise<{ suche?: string }> }) {
|
||||
await erfordereBenutzer();
|
||||
const parameter = await searchParams;
|
||||
const alle = inhalte();
|
||||
|
||||
const suchbegriff = (parameter.suche ?? '').trim();
|
||||
const gesucht = normalisiere(suchbegriff);
|
||||
|
||||
const eintraege = [...alle.glossar].sort((a, b) => a.begriff.localeCompare(b.begriff, 'de-DE'));
|
||||
const gefiltert = gesucht
|
||||
? eintraege.filter(
|
||||
(e) =>
|
||||
normalisiere(e.begriff).includes(gesucht) ||
|
||||
normalisiere(e.steht_fuer ?? '').includes(gesucht) ||
|
||||
normalisiere(e.erklaerung).includes(gesucht) ||
|
||||
e.formen.some((f) => normalisiere(f).includes(gesucht)),
|
||||
)
|
||||
: eintraege;
|
||||
|
||||
return (
|
||||
<div className="stapel">
|
||||
<div>
|
||||
<h1>Glossar</h1>
|
||||
<p className="leise">
|
||||
{alle.glossar.length} Begriffe. In den Handouts sind sie verlinkt – ein Klick führt hierher.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<GlossarSuche anfangswert={suchbegriff} />
|
||||
|
||||
{gefiltert.length === 0 ? (
|
||||
<p className="karte">Kein Begriff passt zu „{suchbegriff}“.</p>
|
||||
) : (
|
||||
<dl className="glossar">
|
||||
{gefiltert.map((eintrag) => (
|
||||
<div key={eintrag.begriff} id={glossarAnker(eintrag.begriff)} className="glossar-eintrag karte">
|
||||
<dt>
|
||||
{eintrag.begriff}
|
||||
{eintrag.steht_fuer ? <span className="glossar-langform">{eintrag.steht_fuer}</span> : null}
|
||||
</dt>
|
||||
<dd>
|
||||
<p style={{ marginBottom: eintrag.siehe_auch.length > 0 ? '0.5rem' : 0 }}>{eintrag.erklaerung}</p>
|
||||
{eintrag.siehe_auch.length > 0 ? (
|
||||
<p className="leise" style={{ margin: 0 }}>
|
||||
Siehe auch:{' '}
|
||||
{eintrag.siehe_auch.map((verweis, index) => (
|
||||
<span key={verweis}>
|
||||
{index > 0 ? ', ' : ''}
|
||||
<Link href={`#${glossarAnker(verweis)}`}>{verweis}</Link>
|
||||
</span>
|
||||
))}
|
||||
</p>
|
||||
) : null}
|
||||
</dd>
|
||||
</div>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user