feat(vorlagen): Editor, Liste und Versionsgeschichte (#24)
This commit was merged in pull request #24.
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import type { Prisma } from "@/generated/prisma/client";
|
||||
import { VersionStatus } from "@/generated/prisma/enums";
|
||||
import { prisma } from "@/lib/db";
|
||||
import { platzhalterDerStruktur, type Struktur } from "@/lib/vorlagen-format";
|
||||
@@ -19,6 +20,10 @@ export type VorlagenZeile = {
|
||||
readonly schlagworte: readonly string[];
|
||||
readonly archiviert: boolean;
|
||||
readonly kundeId: string | null;
|
||||
/** Name des Kunden, wenn die Vorlage kundenspezifisch ist – fuer die Anzeige. */
|
||||
readonly kundeName: string | null;
|
||||
/** Kennung des vorhandenen Entwurfs, damit die Liste direkt dorthin verlinkt. */
|
||||
readonly entwurfVersionId: string | null;
|
||||
/** Nummer der zuletzt veroeffentlichten Version, falls es eine gibt. */
|
||||
readonly veroeffentlichteVersion: number | null;
|
||||
readonly veroeffentlichtAm: Date | null;
|
||||
@@ -51,6 +56,55 @@ async function kundenBedingung(benutzer: HandelnderBenutzer) {
|
||||
return { OR: [{ kundeId: null }, { kundeId: { in: [...sichtbar] } }] };
|
||||
}
|
||||
|
||||
/**
|
||||
* Auswahl und Abbildung stehen getrennt, damit Liste und Einzelabruf garantiert
|
||||
* dieselben Felder liefern – zwei Modelle fuer dieselbe Zeile laufen mit der
|
||||
* Zeit auseinander.
|
||||
*/
|
||||
const ZEILEN_AUSWAHL = {
|
||||
id: true,
|
||||
name: true,
|
||||
beschreibung: true,
|
||||
kategorie: true,
|
||||
schlagworte: true,
|
||||
archiviert: true,
|
||||
kundeId: true,
|
||||
kunde: { select: { name: true } },
|
||||
versionen: {
|
||||
select: {
|
||||
id: true,
|
||||
version: true,
|
||||
status: true,
|
||||
veroeffentlichtAm: true,
|
||||
_count: { select: { laeufe: true } },
|
||||
},
|
||||
},
|
||||
} satisfies Prisma.VorlageSelect;
|
||||
|
||||
type VorlagenRohzeile = Prisma.VorlageGetPayload<{ select: typeof ZEILEN_AUSWAHL }>;
|
||||
|
||||
function zeileBauen(zeile: VorlagenRohzeile): VorlagenZeile {
|
||||
const neueste = zeile.versionen
|
||||
.filter((version) => version.status === VersionStatus.VEROEFFENTLICHT)
|
||||
.sort((a, b) => b.version - a.version)[0];
|
||||
const entwurf = zeile.versionen.find((version) => version.status === VersionStatus.ENTWURF);
|
||||
return {
|
||||
id: zeile.id,
|
||||
name: zeile.name,
|
||||
beschreibung: zeile.beschreibung,
|
||||
kategorie: zeile.kategorie,
|
||||
schlagworte: zeile.schlagworte,
|
||||
archiviert: zeile.archiviert,
|
||||
kundeId: zeile.kundeId,
|
||||
kundeName: zeile.kunde?.name ?? null,
|
||||
entwurfVersionId: entwurf?.id ?? null,
|
||||
veroeffentlichteVersion: neueste?.version ?? null,
|
||||
veroeffentlichtAm: neueste?.veroeffentlichtAm ?? null,
|
||||
hatEntwurf: entwurf !== undefined,
|
||||
anzahlLaeufe: zeile.versionen.reduce((summe, version) => summe + version._count.laeufe, 0),
|
||||
};
|
||||
}
|
||||
|
||||
export async function vorlagenListe(
|
||||
benutzer: HandelnderBenutzer,
|
||||
filter: VorlagenFilter = {},
|
||||
@@ -75,44 +129,29 @@ export async function vorlagenListe(
|
||||
const zeilen = await prisma.vorlage.findMany({
|
||||
where: { AND: bedingungen },
|
||||
orderBy: [{ archiviert: "asc" }, { name: "asc" }],
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
beschreibung: true,
|
||||
kategorie: true,
|
||||
schlagworte: true,
|
||||
archiviert: true,
|
||||
kundeId: true,
|
||||
versionen: {
|
||||
select: {
|
||||
version: true,
|
||||
status: true,
|
||||
veroeffentlichtAm: true,
|
||||
_count: { select: { laeufe: true } },
|
||||
},
|
||||
},
|
||||
},
|
||||
select: ZEILEN_AUSWAHL,
|
||||
});
|
||||
return zeilen.map(zeileBauen);
|
||||
}
|
||||
|
||||
return zeilen.map((zeile) => {
|
||||
const veroeffentlichte = zeile.versionen
|
||||
.filter((v) => v.status === VersionStatus.VEROEFFENTLICHT)
|
||||
.sort((a, b) => b.version - a.version);
|
||||
const neueste = veroeffentlichte[0];
|
||||
return {
|
||||
id: zeile.id,
|
||||
name: zeile.name,
|
||||
beschreibung: zeile.beschreibung,
|
||||
kategorie: zeile.kategorie,
|
||||
schlagworte: zeile.schlagworte,
|
||||
archiviert: zeile.archiviert,
|
||||
kundeId: zeile.kundeId,
|
||||
veroeffentlichteVersion: neueste?.version ?? null,
|
||||
veroeffentlichtAm: neueste?.veroeffentlichtAm ?? null,
|
||||
hatEntwurf: zeile.versionen.some((v) => v.status === VersionStatus.ENTWURF),
|
||||
anzahlLaeufe: zeile.versionen.reduce((summe, v) => summe + v._count.laeufe, 0),
|
||||
};
|
||||
/**
|
||||
* Eine einzelne Vorlage mit ihren Kopfdaten.
|
||||
*
|
||||
* Bewusst dasselbe Lesemodell wie die Liste: die Vorlagenseite zeigt dieselben
|
||||
* Angaben, und zwei Modelle fuer dieselbe Zeile laufen mit der Zeit auseinander.
|
||||
* `undefined` heisst „gibt es nicht oder darfst du nicht sehen“ — die
|
||||
* Unterscheidung waere schon die Auskunft, die wir nicht geben wollen.
|
||||
*/
|
||||
export async function vorlageHolen(
|
||||
vorlageId: string,
|
||||
benutzer: HandelnderBenutzer,
|
||||
): Promise<VorlagenZeile | undefined> {
|
||||
const mandant = await kundenBedingung(benutzer);
|
||||
const zeile = await prisma.vorlage.findFirst({
|
||||
where: { id: vorlageId, ...(mandant ? mandant : {}) },
|
||||
select: ZEILEN_AUSWAHL,
|
||||
});
|
||||
return zeile ? zeileBauen(zeile) : undefined;
|
||||
}
|
||||
|
||||
/** Die Kategorien und Schlagworte, die tatsaechlich vorkommen – fuer die Filter. */
|
||||
@@ -188,6 +227,12 @@ export type VersionMitAufbau = {
|
||||
readonly vorlageName: string;
|
||||
readonly version: number;
|
||||
readonly status: VersionStatus;
|
||||
/**
|
||||
* Stand beim Laden. Der Editor schickt ihn beim Speichern zurueck; weicht er
|
||||
* ab, hat jemand anderes zwischenzeitlich gespeichert (#009).
|
||||
*/
|
||||
readonly stand: Date;
|
||||
readonly aenderungsnotiz: string | null;
|
||||
readonly struktur: Struktur;
|
||||
/** Platzhalter, die in Titeln, Hilfetexten und Befehlen vorkommen. */
|
||||
readonly platzhalter: readonly string[];
|
||||
@@ -238,6 +283,8 @@ export async function versionMitAufbau(
|
||||
vorlageName: version.vorlage.name,
|
||||
version: version.version,
|
||||
status: version.status,
|
||||
stand: version.geaendertAm,
|
||||
aenderungsnotiz: version.aenderungsnotiz,
|
||||
struktur,
|
||||
platzhalter: platzhalterDerStruktur(struktur),
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user