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>
113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import type { Metadata } from 'next'
|
|
import { Suspense } from 'react'
|
|
import { Filterleiste } from '@/app/(app)/aufgaben/filterleiste'
|
|
import { NeueAufgabe } from '@/app/(app)/aufgaben/neue-aufgabe'
|
|
import { AufgabenListe } from '@/components/aufgaben/aufgaben-liste'
|
|
import { Card, CardInhalt } from '@/components/ui/card'
|
|
import type { AufgabenStatus, AufgabenTyp } from '@/generated/prisma/enums'
|
|
import { aufwandSumme } from '@/lib/aufgabe'
|
|
import { alsDauer } from '@/lib/zeit'
|
|
import {
|
|
aufgabenLaden,
|
|
bereicheLaden,
|
|
projekteZurAuswahlLaden,
|
|
type AufgabenFilter,
|
|
} from '@/server/abfragen/aufgaben'
|
|
import { verlangeAnmeldung } from '@/server/berechtigung'
|
|
|
|
export const metadata: Metadata = { title: 'Aufgaben' }
|
|
|
|
const STATUS_WERTE: readonly string[] = [
|
|
'OFFEN',
|
|
'GEPLANT',
|
|
'WARTET',
|
|
'ERLEDIGT',
|
|
'VERWORFEN',
|
|
]
|
|
const TYP_WERTE: readonly string[] = [
|
|
'AUFGABE',
|
|
'IDEE',
|
|
'ARBEITSPAKET',
|
|
'MEILENSTEIN',
|
|
'SPIEGEL',
|
|
]
|
|
|
|
/** Übersetzt die Adresszeile in einen Filter; Unbekanntes wird ignoriert. */
|
|
function filterAus(
|
|
parameter: Record<string, string | string[] | undefined>,
|
|
): AufgabenFilter {
|
|
const einer = (name: string): string | undefined => {
|
|
const wert = parameter[name]
|
|
return typeof wert === 'string' && wert.length > 0 ? wert : undefined
|
|
}
|
|
|
|
const bereich = einer('bereich')
|
|
const status = einer('status')
|
|
const typ = einer('typ')
|
|
|
|
return {
|
|
...(bereich === 'inbox' ? { nurInbox: true } : bereich ? { bereichId: bereich } : {}),
|
|
...(einer('projekt') ? { projektId: einer('projekt') } : {}),
|
|
// Ohne Angabe zeigt die Liste nur, was noch Arbeit bedeutet.
|
|
...(status === 'alle'
|
|
? {}
|
|
: status && STATUS_WERTE.includes(status)
|
|
? { status: status as AufgabenStatus }
|
|
: { nurOffen: true }),
|
|
...(typ && TYP_WERTE.includes(typ) ? { typ: typ as AufgabenTyp } : {}),
|
|
...(einer('suche') ? { suche: einer('suche') } : {}),
|
|
// Ideen haben ihre eigene Seite; wer sie hier sehen will, filtert danach.
|
|
ohneIdeen: true,
|
|
}
|
|
}
|
|
|
|
export default async function AufgabenSeite({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<Record<string, string | string[] | undefined>>
|
|
}) {
|
|
await verlangeAnmeldung('/aufgaben')
|
|
const parameter = await searchParams
|
|
|
|
const [aufgaben, bereiche, projekte] = await Promise.all([
|
|
aufgabenLaden(filterAus(parameter)),
|
|
bereicheLaden(),
|
|
projekteZurAuswahlLaden(),
|
|
])
|
|
|
|
const geschaetzt = aufwandSumme(aufgaben)
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6">
|
|
<header className="flex flex-wrap items-start justify-between gap-4">
|
|
<div>
|
|
<h1 className="text-2xl font-semibold tracking-tight">Aufgaben</h1>
|
|
<p className="text-gedaempft-vordergrund mt-1 text-sm">
|
|
{aufgaben.length === 0
|
|
? 'Nichts gefunden.'
|
|
: `${aufgaben.length} Aufgabe(n)${geschaetzt > 0 ? `, zusammen ${alsDauer(geschaetzt)} geschätzt` : ''}.`}
|
|
</p>
|
|
</div>
|
|
<NeueAufgabe bereiche={bereiche} projekte={projekte} />
|
|
</header>
|
|
|
|
<Card>
|
|
<CardInhalt className="py-4">
|
|
<Suspense
|
|
fallback={<p className="text-gedaempft-vordergrund text-sm">Filter …</p>}
|
|
>
|
|
<Filterleiste bereiche={bereiche} projekte={projekte} />
|
|
</Suspense>
|
|
</CardInhalt>
|
|
</Card>
|
|
|
|
<AufgabenListe
|
|
aufgaben={aufgaben}
|
|
bereiche={bereiche}
|
|
projekte={projekte}
|
|
leerText="Keine Aufgabe passt zu diesen Filtern."
|
|
/>
|
|
</div>
|
|
)
|
|
}
|