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>
52 lines
1.5 KiB
TypeScript
52 lines
1.5 KiB
TypeScript
'use client';
|
||
|
||
import { useRouter } from 'next/navigation';
|
||
import { useEffect, useRef, useState } from 'react';
|
||
|
||
/**
|
||
* Suchfeld für die Handouts. Sucht beim Tippen mit kurzer Verzögerung, damit
|
||
* nicht bei jedem Buchstaben eine Anfrage losgeht, und funktioniert auch ohne
|
||
* JavaScript – dann eben erst beim Absenden.
|
||
*/
|
||
export function Suchfeld({ anfangswert }: { anfangswert: string }) {
|
||
const [wert, setzeWert] = useState(anfangswert);
|
||
const router = useRouter();
|
||
const erstesMal = useRef(true);
|
||
|
||
useEffect(() => {
|
||
if (erstesMal.current) {
|
||
erstesMal.current = false;
|
||
return;
|
||
}
|
||
|
||
const zeitgeber = setTimeout(() => {
|
||
const ziel = wert.trim() ? `/handouts?suche=${encodeURIComponent(wert.trim())}` : '/handouts';
|
||
router.replace(ziel, { scroll: false });
|
||
}, 250);
|
||
|
||
return () => clearTimeout(zeitgeber);
|
||
}, [wert, router]);
|
||
|
||
return (
|
||
<form action="/handouts" method="get" role="search" className="nicht-drucken">
|
||
<label htmlFor="suche" className="nur-lesehilfe">
|
||
Handouts durchsuchen
|
||
</label>
|
||
<div className="reihe" style={{ gap: '0.5rem', flexWrap: 'nowrap' }}>
|
||
<input
|
||
id="suche"
|
||
name="suche"
|
||
type="search"
|
||
placeholder="Suchen – z. B. Subnetting, SMART, Vererbung"
|
||
value={wert}
|
||
onChange={(e) => setzeWert(e.target.value)}
|
||
autoComplete="off"
|
||
/>
|
||
<button type="submit" className="knopf knopf-zweit" style={{ flexShrink: 0 }}>
|
||
Suchen
|
||
</button>
|
||
</div>
|
||
</form>
|
||
);
|
||
}
|