feat(schnellstart): Kacheln je Geraetetyp in der Oberflaeche pflegen (#44)
Prüfung / pruefung (push) Canceled after 0s
Prüfung / e2e (push) Canceled after 0s

This commit was merged in pull request #44.
This commit is contained in:
2026-10-04 15:41:47 +00:00
parent 0060a55387
commit f5936327a0
17 changed files with 910 additions and 24 deletions
+90
View File
@@ -0,0 +1,90 @@
import type { GeraeteTyp } from "@/generated/prisma/enums";
import { VersionStatus } from "@/generated/prisma/enums";
import { prisma } from "@/lib/db";
import { GERAETE_TYP_LISTE } from "@/lib/geraete-text";
/**
* Lesemodell fuer die Pflege des Schnellstarts (#022).
*
* `GeraetetypVorlage` ist global: die Zuordnung haengt am Geraetetyp, nicht am
* Kunden. Deshalb gibt es hier **keine** Mandantensicht — die Seite verlangt
* `verwaltung`, und wer die hat, sieht ohnehin die ganze Installation. Dass eine
* kundengebundene Vorlage trotzdem nur bei ihrem Kunden erscheint, entscheidet
* `vorlagenFuerGeraet` beim Lesen der Geraeteseite, nicht diese Pflege.
*/
export type SchnellstartEintrag = {
readonly vorlageId: string;
readonly name: string;
readonly kategorie: string | null;
/** Gesetzt, wenn die Vorlage nur fuer einen Kunden gilt. */
readonly kundeName: string | null;
};
export type SchnellstartTyp = {
readonly typ: GeraeteTyp;
readonly zugewiesen: readonly SchnellstartEintrag[];
};
export type SchnellstartPflege = {
readonly typen: readonly SchnellstartTyp[];
/** Alles, was sich zuweisen laesst – veroeffentlicht und nicht archiviert. */
readonly auswahl: readonly SchnellstartEintrag[];
};
export async function schnellstartPflege(): Promise<SchnellstartPflege> {
const [vorlagen, vorgaben] = await Promise.all([
prisma.vorlage.findMany({
where: {
archiviert: false,
// Ohne veroeffentlichte Version laesst sich kein Lauf starten; sie
// anzubieten waere eine Kachel, die nichts tut.
versionen: { some: { status: VersionStatus.VEROEFFENTLICHT } },
},
orderBy: [{ kategorie: "asc" }, { name: "asc" }],
select: {
id: true,
name: true,
kategorie: true,
kunde: { select: { name: true } },
},
}),
prisma.geraetetypVorlage.findMany({
// `vorlageId` als zweites Kriterium, damit die Reihenfolge **eindeutig**
// ist. Zwei Zeilen mit derselben `position` gibt es wirklich — der Seed
// zaehlt je Vorgabe von 0, und Altdaten tragen beides auf 0. Ohne diesen
// Tiebreak gab Postgres sie hier anders zurueck als im Dienst, und eine
// echte Umsortierung galt dort als "unveraendert" (#022, gefunden beim
// Durchklicken des gebauten Stands).
orderBy: [{ geraeteTyp: "asc" }, { position: "asc" }, { vorlageId: "asc" }],
select: { geraeteTyp: true, vorlageId: true },
}),
]);
const nachId = new Map<string, SchnellstartEintrag>(
vorlagen.map((vorlage) => [
vorlage.id,
{
vorlageId: vorlage.id,
name: vorlage.name,
kategorie: vorlage.kategorie,
kundeName: vorlage.kunde?.name ?? null,
},
]),
);
return {
typen: GERAETE_TYP_LISTE.map((typ) => ({
typ,
zugewiesen: vorgaben
.filter((vorgabe) => vorgabe.geraeteTyp === typ)
// Eine archivierte oder unveroeffentlichte Vorlage bleibt in der Tabelle
// stehen und verschwindet trotzdem aus der Liste — dieselbe Regel, die
// `vorlagenFuerGeraet` auf der Geraeteseite anwendet. Niemand muss
// aufraeumen, damit der Schnellstart stimmt.
.map((vorgabe) => nachId.get(vorgabe.vorlageId))
.filter((eintrag): eintrag is SchnellstartEintrag => eintrag !== undefined),
})),
auswahl: [...nachId.values()],
};
}