Jeder Bereich zeigt, wie voll der heutige Tag schon ist: Termine aus allen verbundenen Kalendern gegen die Tageskapazität dieses Bereichs. Alle Kalender zählen gegen jeden Bereich, weil ein Termin die Zeit ohnehin blockiert, gleich zu welchem Lebensbereich er gehört. Wer um 14 Uhr beim Zahnarzt sitzt, kann zur selben Zeit nichts für die Firma tun. Ohne verbundenen Kalender bleibt der Balken weg. Er wäre sonst immer bei null und sähe nach "alles frei" aus — eine Zahl, die lügt, ist schlechter als keine. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
181 lines
6.6 KiB
TypeScript
181 lines
6.6 KiB
TypeScript
import Link from 'next/link'
|
||
import { AufgabenListe } from '@/components/aufgaben/aufgaben-liste'
|
||
import { Schnellerfassung } from '@/components/aufgaben/schnellerfassung'
|
||
import { Fortschrittsbalken } from '@/components/fortschrittsbalken'
|
||
import {
|
||
Card,
|
||
CardBeschreibung,
|
||
CardInhalt,
|
||
CardKopf,
|
||
CardTitel,
|
||
} from '@/components/ui/card'
|
||
import { wochenKapazitaet } from '@/lib/bereich'
|
||
import { alsDatum, alsDauer } from '@/lib/zeit'
|
||
import {
|
||
aufgabenLaden,
|
||
bereicheLaden,
|
||
projekteZurAuswahlLaden,
|
||
zaehlstaende,
|
||
} from '@/server/abfragen/aufgaben'
|
||
import { belegungHeute, hatKalenderkonto } from '@/server/abfragen/kalender'
|
||
import { verlangeAnmeldung } from '@/server/berechtigung'
|
||
import { db } from '@/server/db'
|
||
import { jetzt } from '@/server/zeitgeber'
|
||
|
||
export default async function StartSeite() {
|
||
const benutzer = await verlangeAnmeldung('/')
|
||
const heute = jetzt()
|
||
|
||
const [alleOffenen, bereiche, projekte, zahlen, bereichsListe, belegung, mitKalender] =
|
||
await Promise.all([
|
||
aufgabenLaden({ nurOffen: true, ohneIdeen: true }, 200),
|
||
bereicheLaden(),
|
||
projekteZurAuswahlLaden(),
|
||
zaehlstaende(),
|
||
db.bereich.findMany({
|
||
where: { aktiv: true },
|
||
orderBy: [{ reihenfolge: 'asc' }, { name: 'asc' }],
|
||
}),
|
||
belegungHeute(benutzer.id, heute),
|
||
hatKalenderkonto(benutzer.id),
|
||
])
|
||
|
||
// Was jetzt zählt: überfällig, heute oder morgen fällig.
|
||
const dran = alleOffenen.filter(
|
||
(a) => a.ueberfaellig || a.faelligkeit === 'heute' || a.faelligkeit === 'morgen',
|
||
)
|
||
|
||
const anrede = benutzer.name?.split(' ')[0] ?? benutzer.email
|
||
|
||
return (
|
||
<div className="flex flex-col gap-8">
|
||
<header>
|
||
<h1 className="text-2xl font-semibold tracking-tight">Guten Tag, {anrede}</h1>
|
||
<p className="text-gedaempft-vordergrund mt-1 text-sm">{alsDatum(heute)}</p>
|
||
</header>
|
||
|
||
<Card>
|
||
<CardKopf>
|
||
<CardTitel className="text-base">Schnell erfassen</CardTitel>
|
||
<CardBeschreibung>
|
||
Landet in der{' '}
|
||
<Link href="/inbox" className="text-betont underline underline-offset-4">
|
||
Inbox
|
||
</Link>{' '}
|
||
und wird später einsortiert.
|
||
</CardBeschreibung>
|
||
</CardKopf>
|
||
<CardInhalt>
|
||
<Schnellerfassung />
|
||
</CardInhalt>
|
||
</Card>
|
||
|
||
<section className="flex flex-col gap-3">
|
||
<div className="flex flex-wrap items-baseline justify-between gap-2">
|
||
<h2 className="text-lg font-medium">Jetzt dran</h2>
|
||
<Link
|
||
href="/aufgaben"
|
||
className="text-gedaempft-vordergrund hover:text-vordergrund text-sm"
|
||
>
|
||
Alle {zahlen.offen} offenen Aufgaben
|
||
</Link>
|
||
</div>
|
||
|
||
<AufgabenListe
|
||
aufgaben={dran}
|
||
bereiche={bereiche}
|
||
projekte={projekte}
|
||
leerText="Nichts überfällig, nichts heute fällig. Ein guter Tag."
|
||
/>
|
||
</section>
|
||
|
||
<section className="flex flex-col gap-3">
|
||
<div className="flex flex-wrap items-baseline justify-between gap-2">
|
||
<h2 className="text-lg font-medium">Deine Bereiche</h2>
|
||
<Link
|
||
href="/bereiche"
|
||
className="text-gedaempft-vordergrund hover:text-vordergrund text-sm"
|
||
>
|
||
Bereiche verwalten
|
||
</Link>
|
||
</div>
|
||
|
||
{bereichsListe.length === 0 ? (
|
||
<Card>
|
||
<CardInhalt className="text-gedaempft-vordergrund py-8 text-center text-sm">
|
||
Noch keine Bereiche angelegt.{' '}
|
||
<Link href="/bereiche" className="text-betont underline underline-offset-4">
|
||
Jetzt einrichten
|
||
</Link>
|
||
</CardInhalt>
|
||
</Card>
|
||
) : (
|
||
<ul className="grid gap-4 sm:grid-cols-2">
|
||
{bereichsListe.map((bereich) => {
|
||
const offeneHier = alleOffenen.filter((a) => a.bereich?.id === bereich.id)
|
||
const heuteHier = belegung.find((b) => b.id === bereich.id)?.belegung
|
||
return (
|
||
<li key={bereich.id}>
|
||
<Card className="h-full">
|
||
<CardKopf className="flex-row items-center gap-3">
|
||
<span
|
||
aria-hidden
|
||
className="size-3 shrink-0 rounded-full ring-1 ring-black/10"
|
||
style={{ backgroundColor: bereich.farbe }}
|
||
/>
|
||
<CardTitel className="text-base">{bereich.name}</CardTitel>
|
||
</CardKopf>
|
||
<CardInhalt className="flex flex-col gap-2">
|
||
<p className="text-gedaempft-vordergrund text-sm">
|
||
{offeneHier.length === 0
|
||
? 'Nichts offen'
|
||
: `${offeneHier.length} offen`}{' '}
|
||
· {alsDauer(wochenKapazitaet(bereich))} Kapazität je Woche
|
||
</p>
|
||
|
||
{/*
|
||
Ohne verbundenen Kalender wäre die Belegung immer null –
|
||
das sähe nach "alles frei" aus und wäre irreführend.
|
||
*/}
|
||
{mitKalender && heuteHier && (
|
||
<>
|
||
<Fortschrittsbalken
|
||
prozent={heuteHier.prozent}
|
||
beschriftung={`Belegung heute in ${bereich.name}`}
|
||
/>
|
||
<p className="text-gedaempft-vordergrund text-xs">
|
||
{heuteHier.kapazitaet === 0
|
||
? 'Heute nicht eingeplant'
|
||
: heuteHier.ueberbucht
|
||
? `Heute überbucht: ${alsDauer(heuteHier.belegt)} Termine bei ${alsDauer(heuteHier.kapazitaet)} Kapazität`
|
||
: `Heute noch ${alsDauer(heuteHier.frei)} frei von ${alsDauer(heuteHier.kapazitaet)}`}
|
||
</p>
|
||
</>
|
||
)}
|
||
</CardInhalt>
|
||
</Card>
|
||
</li>
|
||
)
|
||
})}
|
||
</ul>
|
||
)}
|
||
</section>
|
||
|
||
{zahlen.ideen > 0 && (
|
||
<Card>
|
||
<CardKopf>
|
||
<CardTitel className="text-base">
|
||
<Link href="/ideen" className="hover:underline hover:underline-offset-4">
|
||
{zahlen.ideen} Idee(n) warten
|
||
</Link>
|
||
</CardTitel>
|
||
<CardBeschreibung>
|
||
Ideen drängen sich nicht in die Aufgabenliste — bis du eine übernimmst.
|
||
</CardBeschreibung>
|
||
</CardKopf>
|
||
</Card>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|