Der Fehler fiel erst in den aufgenommenen Bildern auf: Ideen standen mitten in der Aufgabenliste, obwohl der ganze Sinn der getrennten Ideenseite ist, dass sie das nicht tun. Ohne Termin hätten sie dort entweder unsichtbar unten gestanden oder mit erfundenem Termin oben. Der Filter kennt jetzt ohneIdeen; ein ausdrücklich gewählter Typ gewinnt, wer nach Ideen filtert, sieht sie also weiterhin. Die Startseite filterte Ideen bisher von Hand nach dem Laden — das erledigt jetzt dieselbe Abfrage wie überall sonst. Ein E2E-Test hält den Fall fest. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
155 lines
5.1 KiB
TypeScript
155 lines
5.1 KiB
TypeScript
import Link from 'next/link'
|
|
import { AufgabenListe } from '@/components/aufgaben/aufgaben-liste'
|
|
import { Schnellerfassung } from '@/components/aufgaben/schnellerfassung'
|
|
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 { 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] = await Promise.all([
|
|
aufgabenLaden({ nurOffen: true, ohneIdeen: true }, 200),
|
|
bereicheLaden(),
|
|
projekteZurAuswahlLaden(),
|
|
zaehlstaende(),
|
|
db.bereich.findMany({
|
|
where: { aktiv: true },
|
|
orderBy: [{ reihenfolge: 'asc' }, { name: 'asc' }],
|
|
}),
|
|
])
|
|
|
|
// 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)
|
|
return (
|
|
<li key={bereich.id}>
|
|
<Card>
|
|
<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>
|
|
<p className="text-gedaempft-vordergrund text-sm">
|
|
{offeneHier.length === 0
|
|
? 'Nichts offen'
|
|
: `${offeneHier.length} offen`}{' '}
|
|
· {alsDauer(wochenKapazitaet(bereich))} Kapazität je Woche
|
|
</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>
|
|
)
|
|
}
|