Let the truncated Anstehend list open
"… und 5 weitere Ereignisse in diesem Zeitraum" was a sentence to read. It is now a button: click it and the rest unfolds in place, click again and the card goes back to eight rows. Until now the only way to see the remaining entries was to narrow the period or the kinds — which changes the question rather than the answer. This happens in the browser, not through the URL, unlike the period and kind filters. Those go through the address because a longer period brings rows into play that were never loaded; here every entry in the period is already on the page and the eight was purely presentational. A round trip would mean waiting for data that is already there, plus a history entry for something nobody wants to go back to. The list moved into its own component so the state has somewhere to live. KIND_LABEL and the item type moved with it, since they only describe this list. The button only appears when there is something to unfold, and once open it offers the way back — otherwise the card stays long for the rest of the session because somebody looked once. Verified: 10 tests, five mutation-checked (ignoring the state, dropping the way back, showing the button with nothing to unfold, losing the singular, dropping aria-expanded each turn them red). 487 tests, typecheck and build clean. Not seen in a browser — login goes through the company account and the database is unreachable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,12 +2,13 @@ import { ChevronRight } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Suspense } from "react";
|
||||
import { AnstehendFilter } from "@/components/dashboard/AnstehendFilter";
|
||||
import { AnstehendListe, type AnstehendEintrag } from "@/components/dashboard/AnstehendListe";
|
||||
import { DraftsCard } from "@/components/dashboard/DraftsCard";
|
||||
import { Card, CARD_CLASS, CardTitle } from "@/components/ui/Card";
|
||||
import { actionBadgeStyle } from "@/lib/colors";
|
||||
import { istEingeschraenkt, parseArten, parseZeitraum } from "@/lib/dashboard-filter";
|
||||
import { loadDashboardData } from "@/lib/dashboard-data";
|
||||
import { addDaysIso, fmtDate, fmtName, todayIso } from "@/lib/format";
|
||||
import { addDaysIso, fmtName, todayIso } from "@/lib/format";
|
||||
import { divisionOf } from "@/lib/org";
|
||||
import { deriveStatusAsOf } from "@/lib/reports";
|
||||
import { currentUserId } from "@/lib/auth/session";
|
||||
@@ -38,13 +39,6 @@ const DOT_STYLES: Record<string, string> = {
|
||||
Gehaltsanpassung: "bg-warning-text",
|
||||
};
|
||||
|
||||
const KIND_LABEL = {
|
||||
hire: "Eintritt",
|
||||
exit: "Austritt",
|
||||
return: "Rückkehr aus Abwesenheit",
|
||||
note: "Wiedervorlage",
|
||||
} as const;
|
||||
|
||||
export default async function DashboardPage({
|
||||
searchParams,
|
||||
}: {
|
||||
@@ -121,17 +115,7 @@ export default async function DashboardPage({
|
||||
.sort((a, b) => b.count - a.count);
|
||||
const maxDivisionCount = Math.max(1, ...divisionBars.map((d) => d.count));
|
||||
|
||||
type UpcomingItem = {
|
||||
id: string;
|
||||
/** Ziel des Klicks — bei einer Wiedervorlage die Akte, nicht die Notiz. */
|
||||
employeeId: string;
|
||||
label: string;
|
||||
/** Zweite Zeile: bei einer Wiedervorlage der Notiztext statt der Art. */
|
||||
hinweis?: string;
|
||||
date: string;
|
||||
kind: keyof typeof KIND_LABEL;
|
||||
};
|
||||
const upcomingAlle: UpcomingItem[] = [
|
||||
const upcomingAlle: AnstehendEintrag[] = [
|
||||
...(upcomingHires).map((e) => ({
|
||||
id: e.id,
|
||||
employeeId: e.id,
|
||||
@@ -163,12 +147,6 @@ export default async function DashboardPage({
|
||||
})),
|
||||
].sort((a, b) => a.date.localeCompare(b.date));
|
||||
|
||||
// Die Karte bleibt eine Übersicht, keine Liste: acht Zeilen, und darunter
|
||||
// steht, wie viele es insgesamt sind. Wer alle sehen will, filtert enger
|
||||
// oder geht in die Mitarbeiterliste.
|
||||
const upcoming = upcomingAlle.slice(0, 8);
|
||||
const weitere = upcomingAlle.length - upcoming.length;
|
||||
|
||||
// Each tile links to the view that shows what it counts, with the filters
|
||||
// pre-applied.
|
||||
//
|
||||
@@ -258,49 +236,15 @@ export default async function DashboardPage({
|
||||
<Suspense fallback={<div className="mb-2 h-9" />}>
|
||||
<AnstehendFilter zeitraum={zeitraum} arten={arten} />
|
||||
</Suspense>
|
||||
<ul className="flex flex-col divide-y divide-border-subtle">
|
||||
{upcoming.map((item) => {
|
||||
// Überfällig gibt es nur bei Wiedervorlagen: die anderen Arten
|
||||
// haben eine untere Grenze, eine offene Aufgabe nicht.
|
||||
const ueberfaellig = item.date < today;
|
||||
return (
|
||||
<li key={`${item.kind}-${item.id}`}>
|
||||
<Link
|
||||
href={`/employees/${item.employeeId}`}
|
||||
className="-mx-2 flex items-center justify-between gap-2 rounded px-2 py-2.5 text-sm hover:bg-surface"
|
||||
>
|
||||
<span className="min-w-0">
|
||||
<span className="block truncate font-semibold text-ink">{item.label}</span>
|
||||
<span className="block truncate text-xs text-ink-muted">
|
||||
{KIND_LABEL[item.kind]}
|
||||
{item.hinweis ? ` — ${item.hinweis}` : ""}
|
||||
</span>
|
||||
</span>
|
||||
<span
|
||||
className={`shrink-0 text-xs font-semibold tabular-nums ${
|
||||
ueberfaellig ? "text-danger-text" : "text-ink-muted"
|
||||
}`}
|
||||
>
|
||||
{ueberfaellig ? "überfällig " : ""}
|
||||
{fmtDate(item.date)}
|
||||
</span>
|
||||
</Link>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
{upcoming.length === 0 && (
|
||||
<p className="py-2 text-sm text-ink-muted">
|
||||
{istEingeschraenkt(zeitraum, arten)
|
||||
? "Zu dieser Auswahl steht nichts an."
|
||||
: "Keine anstehenden Ereignisse."}
|
||||
</p>
|
||||
)}
|
||||
</ul>
|
||||
{weitere > 0 && (
|
||||
<p className="mt-2 text-xs text-ink-muted">
|
||||
… und {weitere} {weitere === 1 ? "weiteres Ereignis" : "weitere Ereignisse"} in diesem Zeitraum.
|
||||
</p>
|
||||
)}
|
||||
<AnstehendListe
|
||||
eintraege={upcomingAlle}
|
||||
today={today}
|
||||
leerText={
|
||||
istEingeschraenkt(zeitraum, arten)
|
||||
? "Zu dieser Auswahl steht nichts an."
|
||||
: "Keine anstehenden Ereignisse."
|
||||
}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user