Merkkarten, Praxisaufgaben, Checklisten, Berichtsheft, Lernzielkontrollen und Feedbackgespraech lassen sich unter Verwaltung -> Module fuer die ganze Lern-Bar ab- und einschalten. Wochenplan, Handouts, Glossar, Auswertung und Verwaltung bleiben immer an. Gesperrt wird an drei Stellen, nicht im Layout (Next rendert Layouts beim Seitenwechsel darunter nicht neu): Seiten mit erfordereModul, Routen fuer PDF, Anhaenge und Lernstand mit modulAn, alle 37 Serveraktionen der Module mit pruefeModul. Die Anmeldepruefung kommt jeweils zuerst, damit ein Unangemeldeter nicht am 404 abliest, was abgeschaltet ist. Ein abgeschaltetes Modul traegt nirgends bei: Wochenplan, Fortschritt, Was-wartet, Mein Stand, Ergebnisverlauf, Rahmenplanabdeckung, Vorschlaege fuers Berichtsheft, Karten im Handout, Kurzbefehle im Manifest. Die Lernstand-Schnittstelle antwortet mit 410, das Geraet verwirft daraufhin seine Offline-Warteschlange. Abschalten loescht nichts. Gespeichert in modul_einstellung (Migration 0010); fehlt die Zeile, ist das Modul an, eine bestehende Datenbank verhaelt sich also wie vorher. 12 Tests fuer die Regeln, Abschnitt A8 im Abnahmelauf, Anleitung Kapitel 9 mit Bildschirmfoto, README, E43. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
237 lines
9.1 KiB
TypeScript
237 lines
9.1 KiB
TypeScript
import Link from 'next/link';
|
||
import { Fortschrittsbalken } from '@/komponenten/Fortschrittsbalken';
|
||
import type { Wochensicht } from '@/lib/planung/sicht';
|
||
import { alsDatumText, kalenderwocheText } from '@/lib/zeit';
|
||
|
||
const statusText: Record<string, { text: string; ton: string; zeichen: string }> = {
|
||
geplant: { text: 'geplant', ton: 'neutral', zeichen: '○' },
|
||
laufend: { text: 'läuft', ton: 'warnung', zeichen: '▶' },
|
||
abgeschlossen: { text: 'abgeschlossen', ton: 'gut', zeichen: '✓' },
|
||
};
|
||
|
||
/**
|
||
* Die Wochenansicht – für den Azubi die Startseite, für den Ausbilder dieselbe
|
||
* Woche in der Ansicht seines Azubis.
|
||
*/
|
||
export function WochenAnsicht({
|
||
sicht,
|
||
istAzubi,
|
||
wochenLink,
|
||
}: {
|
||
sicht: Wochensicht;
|
||
istAzubi: boolean;
|
||
/** Vorlage für den Verweis auf eine andere Woche, `{nr}` wird ersetzt. */
|
||
wochenLink: string;
|
||
}) {
|
||
const zurWoche = (nummer: number) => wochenLink.replace('{nr}', String(nummer));
|
||
const { woche, planung, fortschritt, tage } = sicht;
|
||
const status = statusText[planung.status] ?? statusText.geplant!;
|
||
|
||
return (
|
||
<div className="stapel">
|
||
<header className="stapel-eng">
|
||
<div className="reihe" style={{ gap: '0.6rem' }}>
|
||
<span className="wochen-nummer">Woche {woche.nummer}</span>
|
||
<span className="marke-status" data-ton={status.ton}>
|
||
{status.zeichen} {status.text}
|
||
</span>
|
||
<span className="leise">
|
||
{alsDatumText(planung.geplantVon)} – {alsDatumText(planung.geplantBis)} · {kalenderwocheText(planung.geplantVon)}
|
||
</span>
|
||
</div>
|
||
|
||
<h1 style={{ marginBottom: '0.2rem' }}>{woche.titel}</h1>
|
||
<p className="leitfrage">{woche.leitfrage}</p>
|
||
<p className="leise" style={{ marginBottom: 0 }}>{woche.ziel}</p>
|
||
</header>
|
||
|
||
{planung.tatsaechlichVon && planung.tatsaechlichVon !== planung.geplantVon ? (
|
||
<p className="hinweis hinweis-warnung" style={{ marginBottom: 0 }}>
|
||
Tatsächlich begonnen am {alsDatumText(planung.tatsaechlichVon)}, geplant war der{' '}
|
||
{alsDatumText(planung.geplantVon)}.
|
||
</p>
|
||
) : null}
|
||
|
||
{planung.notiz ? (
|
||
<p className="hinweis hinweis-warnung" style={{ marginBottom: 0 }}>
|
||
<strong>Notiz des Ausbilders:</strong> {planung.notiz}
|
||
</p>
|
||
) : null}
|
||
|
||
<section className="karte stapel">
|
||
<Fortschrittsbalken fortschritt={fortschritt} />
|
||
|
||
{fortschritt.offen.length > 0 ? (
|
||
<div>
|
||
<h2 style={{ fontSize: '1rem', marginBottom: '0.4rem' }}>
|
||
{istAzubi ? 'Das ist diese Woche noch offen' : 'Offen bei deinem Azubi'}
|
||
</h2>
|
||
<ul className="offen-liste">
|
||
{fortschritt.offen.map((bereich) => (
|
||
<li key={bereich.schluessel}>
|
||
<span>{bereich.titel}</span>
|
||
<span className="leise">
|
||
{bereich.zaehlwerk.erledigt} von {bereich.zaehlwerk.gesamt}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</div>
|
||
) : fortschritt.gesamt > 0 ? (
|
||
<p className="hinweis hinweis-gut" style={{ marginBottom: 0 }}>
|
||
✓ Diese Woche ist vollständig abgearbeitet.
|
||
</p>
|
||
) : null}
|
||
</section>
|
||
|
||
<section aria-labelledby="tage" className="stapel">
|
||
<h2 id="tage">Die Woche</h2>
|
||
<ol className="wochentage">
|
||
{tage.map((tag) => (
|
||
<li key={tag.tag} className={`wochentag${tag.istHeute ? ' wochentag-heute' : ''}`}>
|
||
<div className="wochentag-kopf">
|
||
<strong>{tag.tag}</strong>
|
||
<span className="leise">{alsDatumText(tag.datum)}</span>
|
||
{tag.istHeute ? <span className="marke-status" data-ton="warnung">● heute</span> : null}
|
||
</div>
|
||
|
||
<div className="wochentag-inhalt">
|
||
<div>
|
||
<span className="wochentag-marke">Theorie</span>
|
||
{tag.theorie.length === 0 ? (
|
||
<span className="leise">–</span>
|
||
) : (
|
||
<ul className="theorie-liste">
|
||
{tag.theorie.map((handout) => {
|
||
const gelesen = sicht.handouts.find((h) => h.handout.id === handout.id)?.gelesen ?? false;
|
||
return (
|
||
<li key={handout.id}>
|
||
<Link href={`/handouts/${handout.id}`}>
|
||
<span className="handout-kuerzel">{handout.id}</span> {handout.titel}
|
||
</Link>
|
||
{gelesen ? (
|
||
<span className="marke-status" data-ton="gut">
|
||
✓ gelesen
|
||
</span>
|
||
) : null}
|
||
</li>
|
||
);
|
||
})}
|
||
</ul>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<span className="wochentag-marke">Praxis</span>
|
||
<span>{tag.praxis || '–'}</span>
|
||
</div>
|
||
</div>
|
||
</li>
|
||
))}
|
||
</ol>
|
||
</section>
|
||
|
||
<div className="raster">
|
||
<section className="karte" aria-labelledby="woche-handouts">
|
||
<h2 id="woche-handouts" style={{ fontSize: '1rem' }}>
|
||
Handouts dieser Woche
|
||
</h2>
|
||
{sicht.handouts.length === 0 ? (
|
||
<p className="leise" style={{ marginBottom: 0 }}>Keine.</p>
|
||
) : (
|
||
<ul className="stapel-eng" style={{ listStyle: 'none', margin: 0, padding: 0 }}>
|
||
{sicht.handouts.map(({ handout, gelesen }) => (
|
||
<li key={handout.id} className="reihe" style={{ gap: '0.5rem', flexWrap: 'nowrap' }}>
|
||
<span className="handout-kuerzel">{handout.id}</span>
|
||
<Link href={`/handouts/${handout.id}`} style={{ flex: 1 }}>
|
||
{handout.titel}
|
||
</Link>
|
||
<span aria-label={gelesen ? 'gelesen' : 'noch nicht gelesen'} title={gelesen ? 'gelesen' : 'offen'}>
|
||
{gelesen ? '✓' : '○'}
|
||
</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
|
||
{sicht.module.aufgaben ? (
|
||
<section className="karte" aria-labelledby="woche-aufgaben">
|
||
<h2 id="woche-aufgaben" style={{ fontSize: '1rem' }}>
|
||
Praxisaufgaben
|
||
</h2>
|
||
{sicht.aufgaben.length === 0 ? (
|
||
<p className="leise" style={{ marginBottom: 0 }}>Keine.</p>
|
||
) : (
|
||
<ul className="stapel-eng" style={{ margin: 0, paddingLeft: '1.1rem' }}>
|
||
{sicht.aufgaben.map((aufgabe) => (
|
||
<li key={aufgabe.id}>
|
||
<strong>{aufgabe.id}</strong> {aufgabe.titel}
|
||
<br />
|
||
<span className="leise">{aufgabe.zeitrahmen}</span>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
) : null}
|
||
|
||
{sicht.module.checklisten ? (
|
||
<section className="karte" aria-labelledby="woche-checklisten">
|
||
<h2 id="woche-checklisten" style={{ fontSize: '1rem' }}>
|
||
Checklisten
|
||
</h2>
|
||
{sicht.checklisten.length === 0 ? (
|
||
<p className="leise" style={{ marginBottom: 0 }}>Keine.</p>
|
||
) : (
|
||
<ul className="stapel-eng" style={{ margin: 0, paddingLeft: '1.1rem' }}>
|
||
{sicht.checklisten.map((liste) => (
|
||
<li key={liste.id}>{liste.titel}</li>
|
||
))}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
) : null}
|
||
|
||
{sicht.module.kontrollen ? (
|
||
<section className="karte" aria-labelledby="woche-kontrolle">
|
||
<h2 id="woche-kontrolle" style={{ fontSize: '1rem' }}>
|
||
Lernzielkontrolle
|
||
</h2>
|
||
{sicht.kontrolle ? (
|
||
<p style={{ marginBottom: 0 }}>
|
||
{sicht.kontrolle.titel}
|
||
<br />
|
||
<span className="leise">
|
||
{sicht.kontrolle.fragen.length} Fragen ·{' '}
|
||
{sicht.kontrolle.fragen.reduce((summe, f) => summe + f.punkte, 0)} Punkte
|
||
{sicht.kontrolle.dauer_minuten ? ` · ${sicht.kontrolle.dauer_minuten} Minuten` : ''}
|
||
</span>
|
||
</p>
|
||
) : (
|
||
<p className="leise" style={{ marginBottom: 0 }}>Diese Woche keine.</p>
|
||
)}
|
||
</section>
|
||
) : null}
|
||
</div>
|
||
|
||
<nav className="reihe nicht-drucken" style={{ justifyContent: 'space-between' }} aria-label="Andere Wochen">
|
||
{sicht.vorherige ? (
|
||
<Link href={zurWoche(sicht.vorherige)} className="knopf knopf-still">
|
||
← Woche {sicht.vorherige}
|
||
</Link>
|
||
) : (
|
||
<span />
|
||
)}
|
||
{sicht.naechste ? (
|
||
<Link href={zurWoche(sicht.naechste)} className="knopf knopf-still">
|
||
Woche {sicht.naechste} →
|
||
</Link>
|
||
) : (
|
||
<span />
|
||
)}
|
||
</nav>
|
||
</div>
|
||
);
|
||
}
|