feat: Gespiegelte Belegung in der Wochenansicht zeigen

Sie zaehlt gegen die Tageskapazitaet wie jeder fremde Termin, traegt
aber keinen Betreff und ist gestreift gezeichnet — damit niemand darin
einen Termin vermutet, dem der Titel fehlt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Florian Wilde
2026-09-11 23:04:35 +02:00
co-authored by Claude Opus 5
parent 1281c3ea96
commit 0d0f8503cc
3 changed files with 53 additions and 13 deletions
+1
View File
@@ -187,6 +187,7 @@ export default async function WocheSeite({
ganztaegig: t.ganztaegig,
frei: t.zeigtAls === 'FREI',
kalenderName: t.kalenderName,
gespiegelt: t.gespiegelt,
}))}
aufgaben={aufgaben.map((a) => ({
id: a.id,
+15 -2
View File
@@ -64,6 +64,8 @@ export type RasterTermin = {
ganztaegig: boolean
frei: boolean
kalenderName: string
/** Aus der anderen Instanz gespiegelt: belegt, aber ohne Betreff. */
gespiegelt?: boolean
}
export type RasterSpalte = {
@@ -321,6 +323,11 @@ export function Wochenraster({
eintrag.frei
? 'bg-gedaempft/60 text-gedaempft-vordergrund'
: 'bg-gedaempft text-gedaempft-vordergrund',
// Gespiegeltes gehört sichtbar in eine eigene
// Klasse: gestreift, damit niemand darin einen
// Termin mit fehlendem Betreff vermutet.
eintrag.gespiegelt &&
'border-rand border border-dashed bg-[repeating-linear-gradient(45deg,transparent,transparent_4px,color-mix(in_oklch,currentColor_12%,transparent)_4px,color-mix(in_oklch,currentColor_12%,transparent)_8px)]',
)}
style={{
top: lage.oben,
@@ -328,10 +335,16 @@ export function Wochenraster({
left: `${(spur / spuren) * 100}%`,
width: `${100 / spuren}%`,
}}
title={`${eintrag.betreff ?? 'Termin'} · ${eintrag.kalenderName}`}
title={
eintrag.gespiegelt
? 'Dienstlich belegt – gespiegelt aus der anderen Instanz'
: `${eintrag.betreff ?? 'Termin'} · ${eintrag.kalenderName}`
}
>
<span className="line-clamp-2">
{eintrag.betreff ?? 'Termin'}
{eintrag.gespiegelt
? 'Dienstlich belegt'
: (eintrag.betreff ?? 'Termin')}
</span>
</div>
)
+37 -11
View File
@@ -33,6 +33,13 @@ export type WochenTermin = {
ganztaegig: boolean
zeigtAls: ZeigtAls
kalenderName: string
/**
* Kommt aus der anderen Instanz und trägt darum keinen Betreff.
*
* Für die Ansicht ist so eine Belegung ein fremder Termin wie jeder andere:
* Sie blockiert Zeit und lässt sich hier nicht verschieben.
*/
gespiegelt: boolean
}
export type WocheDaten = {
@@ -55,7 +62,7 @@ export async function wocheLaden(
): Promise<WocheDaten> {
const fenster = wochenFenster(zeitpunkt)
const [zeitbloecke, termine] = await Promise.all([
const [zeitbloecke, termine, belegungen] = await Promise.all([
db.zeitblock.findMany({
where: {
kalenderkonto: { benutzerId },
@@ -97,6 +104,11 @@ export async function wocheLaden(
kalenderkonto: { select: { anzeigename: true } },
},
}),
db.spiegelbelegung.findMany({
where: { beginn: { lt: fenster.bis }, ende: { gt: fenster.von } },
orderBy: { beginn: 'asc' },
select: { id: true, beginn: true, ende: true },
}),
])
// Ein Zeitblock steht auch im Kalender und damit im Spiegel. Er würde sonst
@@ -118,17 +130,31 @@ export async function wocheLaden(
letzterFehler: z.letzterFehler,
offen: z.extId === null,
})),
termine: termine
.filter((t) => !eigeneExtIds.has(t.extId))
.map((t) => ({
id: t.id,
beginn: t.beginn,
ende: t.ende,
betreff: t.betreff,
ganztaegig: t.ganztaegig,
zeigtAls: t.zeigtAls,
kalenderName: t.kalenderkonto.anzeigename,
termine: [
...termine
.filter((t) => !eigeneExtIds.has(t.extId))
.map((t) => ({
id: t.id,
beginn: t.beginn,
ende: t.ende,
betreff: t.betreff,
ganztaegig: t.ganztaegig,
zeigtAls: t.zeigtAls,
kalenderName: t.kalenderkonto.anzeigename,
gespiegelt: false,
})),
...belegungen.map((b) => ({
id: b.id,
beginn: b.beginn,
ende: b.ende,
// Kein Betreff: Die andere Instanz schickt bewusst keinen.
betreff: null,
ganztaegig: false,
zeigtAls: 'GEBUCHT' as const,
kalenderName: 'Dienstlich',
gespiegelt: true,
})),
].sort((a, b) => a.beginn.getTime() - b.beginn.getTime()),
}
}