feat(bausteine): Abschnitte, die mehrere Vorlagen teilen
"Datensicherung pruefen" steht in vier der sieben mitgelieferten Vorlagen, wortgleich. Wer den Ablauf aendert, aendert ihn viermal -- oder dreimal und vergisst den vierten. Entschieden wie im Issue vorgeschlagen: ein Entwurf **verweist** auf eine veroeffentlichte Baustein-Fassung, und beim Veroeffentlichen der Vorlage setzt der Dienst deren Schritte als tiefe Kopie ein. Danach sieht ein Lauf weiterhin konkrete Schritte, und Schrittergebnis, Druckansicht und Archiv muessen nichts von Bausteinen wissen. Die Alternative -- Laeufe verweisen zur Laufzeit -- haette die Unveraenderlichkeit an einer zweiten Stelle durchsetzen muessen. Eine neue Fassung aendert damit keine veroeffentlichte Vorlagenversion und keinen laufenden Lauf. Sie wirkt in der naechsten veroeffentlichten Version; die Oberflaeche sagt das an zwei Stellen, weil man sonst Durchschlag erwartet. Vier Garantien liegen in der Datenbank, nicht nur im Dienst: hoechstens ein Entwurf je Baustein, eine veroeffentlichte Fassung ist unveraenderlich, der Weg zurueck zum Entwurf ist versperrt, und ein Abschnitt kann nur auf eine veroeffentlichte Fassung verweisen. Der Titel kommt aus dem Baustein, nicht aus dem Abschnitt der Vorlage: sonst muesste er in jeder Vorlage einzeln gepflegt werden, und zwei Vorlagen zeigten denselben Baustein unter verschiedenen Namen. Ein Export traegt die aufgeloesten Schritte, keinen Verweis -- eine Kennung aus dieser Installation bedeutet in einer anderen nichts. Das Durchklicken des gebauten Stands fand einen Fehler, den die Suite nicht gesehen haette: die Platzhalter-Schritte eines Baustein-Abschnitts haben keinen Titel, wurden aber mitgeschickt, und SchrittSchema wies den ganzen Entwurf ab. Behoben, mit Test und Gegenprobe. Der ganze Weg danach am gebauten Stand nachgemessen: Baustein anlegen, veroeffentlichen, einsetzen, Vorlage veroeffentlichen -- die Schritte stehen drin, mit Herkunft und Protokolleintrag. Closes #50
This commit is contained in:
@@ -13,7 +13,9 @@ import { Vorschau } from "@/components/vorlagen/vorschau";
|
||||
import {
|
||||
alsEditorAufbau,
|
||||
alsStruktur,
|
||||
bausteineAnreichern,
|
||||
neuerAbschnitt,
|
||||
neuerBausteinAbschnitt,
|
||||
neuerSchritt,
|
||||
neueVariable,
|
||||
umsortieren,
|
||||
@@ -67,20 +69,33 @@ type SpeicherZustand = {
|
||||
readonly gespeichertesJson: string;
|
||||
};
|
||||
|
||||
export type EinsetzbarerBaustein = {
|
||||
readonly bausteinVersionId: string;
|
||||
readonly name: string;
|
||||
readonly titel: string;
|
||||
readonly version: number;
|
||||
readonly anzahlSchritte: number;
|
||||
};
|
||||
|
||||
export function Editor({
|
||||
version,
|
||||
lesend,
|
||||
darfBearbeiten,
|
||||
beispielDatum,
|
||||
bausteine = [],
|
||||
}: {
|
||||
version: EditorVersion;
|
||||
lesend: boolean;
|
||||
darfBearbeiten: boolean;
|
||||
/** Heutiges Datum, auf dem Server formatiert — siehe `beispielwerte`. */
|
||||
beispielDatum: string;
|
||||
/** Veröffentlichte Bausteine, die sich einsetzen lassen (#030). */
|
||||
bausteine?: readonly EinsetzbarerBaustein[];
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const [aufbau, setzeAufbau] = useState<EditorAufbau>(() => alsEditorAufbau(version.struktur));
|
||||
const [aufbau, setzeAufbau] = useState<EditorAufbau>(() =>
|
||||
bausteineAnreichern(alsEditorAufbau(version.struktur), bausteine),
|
||||
);
|
||||
const aktuellesJson = useMemo(() => JSON.stringify(alsStruktur(aufbau)), [aufbau]);
|
||||
|
||||
/**
|
||||
@@ -92,8 +107,8 @@ export function Editor({
|
||||
* Speichern in die Datenbank.
|
||||
*/
|
||||
const anfangsJson = useMemo(
|
||||
() => JSON.stringify(alsStruktur(alsEditorAufbau(version.struktur))),
|
||||
[version.struktur],
|
||||
() => JSON.stringify(alsStruktur(bausteineAnreichern(alsEditorAufbau(version.struktur), bausteine))),
|
||||
[version.struktur, bausteine],
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -359,17 +374,52 @@ export function Editor({
|
||||
</SortierBereich>
|
||||
)}
|
||||
{lesend ? null : (
|
||||
<TextKnopf
|
||||
beschriftung={
|
||||
abschnitteVoll ? `Höchstens ${GRENZEN.abschnitte} Abschnitte` : "Abschnitt hinzufügen"
|
||||
}
|
||||
aus={abschnitteVoll}
|
||||
aufKlick={() => {
|
||||
const abschnitt = neuerAbschnitt();
|
||||
setzeZuletztAngelegt(abschnitt.kennung);
|
||||
abschnitteSetzen([...aufbau.abschnitte, abschnitt]);
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
<TextKnopf
|
||||
beschriftung={
|
||||
abschnitteVoll ? `Höchstens ${GRENZEN.abschnitte} Abschnitte` : "Abschnitt hinzufügen"
|
||||
}
|
||||
aus={abschnitteVoll}
|
||||
aufKlick={() => {
|
||||
const abschnitt = neuerAbschnitt();
|
||||
setzeZuletztAngelegt(abschnitt.kennung);
|
||||
abschnitteSetzen([...aufbau.abschnitte, abschnitt]);
|
||||
}}
|
||||
/>
|
||||
{/*
|
||||
Bausteine nur anbieten, wenn es welche gibt. Ein leeres
|
||||
Auswahlfeld mit „keine Bausteine“ wäre eine Frage ohne Antwort
|
||||
(#030).
|
||||
*/}
|
||||
{bausteine.length > 0 ? (
|
||||
<label className="flex min-w-0 items-center gap-2 text-sm">
|
||||
<span className="shrink-0 text-nebentext">Baustein einsetzen</span>
|
||||
<select
|
||||
aria-label="Baustein einsetzen"
|
||||
value=""
|
||||
disabled={abschnitteVoll}
|
||||
onChange={(ereignis) => {
|
||||
const gewaehlt = bausteine.find(
|
||||
(eintrag) => eintrag.bausteinVersionId === ereignis.target.value,
|
||||
);
|
||||
if (!gewaehlt) return;
|
||||
const abschnitt = neuerBausteinAbschnitt(gewaehlt);
|
||||
setzeZuletztAngelegt(abschnitt.kennung);
|
||||
abschnitteSetzen([...aufbau.abschnitte, abschnitt]);
|
||||
}}
|
||||
className="min-h-11 min-w-0 rounded-pille border border-linie bg-flaeche-hoch px-4 text-sm md:min-h-10"
|
||||
>
|
||||
<option value="">auswählen …</option>
|
||||
{bausteine.map((eintrag) => (
|
||||
<option key={eintrag.bausteinVersionId} value={eintrag.bausteinVersionId}>
|
||||
{eintrag.name} (Fassung {eintrag.version}, {eintrag.anzahlSchritte}{" "}
|
||||
{eintrag.anzahlSchritte === 1 ? "Schritt" : "Schritte"})
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user