Files
nebula/src/app/page.tsx
T
florian.wilde 52f2d0ce8e
CI / pruefen (pull_request) Canceled after 0s
Wochenrueckblick ueber alle Projekte
Eine Seite, die aus dem vorhandenen Aktivitaetsverlauf zusammenfasst, was in
einer Woche ueber alle Projekte hinweg passiert ist: Kennzahlen oben, darunter
die Vorgaenge nach Kalendertagen gebuendelt, neuester Tag zuerst. Blaettern in
frueher Wochen ueber ?woche=-n.

Der Verlauf kennt keinen Besitzer - die Trennung laeuft ueber das Projekt,
"project: { ownerId }" steht als erste Bedingung.

Zwei Dinge, die sonst still falsch werden:

- **Wochengrenzen** stehen als pruefbare Funktion in lib/review, nicht in der
  Seite. Gerechnet wird ueber Berliner Kalendertage, nicht ueber "sieben mal
  24 Stunden": liegt eine Zeitumstellung in der Woche, hat sie 167 oder 169
  Stunden. Und im Container laeuft die Anwendung nach UTC, ein Eintrag von
  Sonntagabend fiele sonst in die falsche Woche.
- **"Aufgaben erledigt"** wird am meta-Feld abgelesen (to: "DONE") und nicht
  am Anzeigetext. Der Text ist zum Lesen da und aendert sich mit der
  Formulierung; eine Zaehlung daran wuerde beim naechsten umformulierten
  Einzeiler unbemerkt kippen. Eintraege ohne meta zaehlen nicht mit - lieber
  untertreiben als raten.

Damit completeTaskFromIssue dieselbe Grundlage liefert, schreibt es jetzt
ebenfalls from/to ins meta, wie es das Verschieben von Hand schon tut.

Die Zuordnung Symbol/Farbe je Eintragsart lag bisher in ActivityFeed und wird
jetzt von zwei Stellen gebraucht - herausgezogen nach components/activityKinds,
sonst laufen die Kopien beim naechsten neuen Eintragstyp auseinander.

Erreichbar ueber die Schnellsuche und einen Verweis vom Dashboard. Bewusst
kein vierter Punkt in der Kopfleiste - die ist auf dem Telefon schon eng.

Geprueft: 20 neue Unit-Tests (Wochengrenzen inklusive beider Zeitumstellungen,
Jahreswechsel, Zaehlung), ein E2E-Test ueber Aufruf und Blaettern, axe-core auf
375px und 1280px ueber drei Zustaende - auch die leere Woche - ohne
Beanstandung, kein seitlicher Ueberlauf.

Refs #2
2026-08-18 00:55:02 +02:00

116 lines
4.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Link from "next/link";
import { CalendarRange, CircleDot, GitBranch, Lightbulb, Rocket, Trophy } from "lucide-react";
import { ProjectBoard } from "@/components/ProjectBoard";
import { StatCard } from "@/components/StatCard";
import { requireUserPage } from "@/lib/auth/guard";
import { prisma } from "@/lib/db";
import { connectionSafeSelect, projectListInclude } from "@/lib/projects";
import { STATUS_META } from "@/lib/status";
import type { ConnectionSafe, ProjectListItem } from "@/types";
export const dynamic = "force-dynamic";
export default async function DashboardPage() {
const user = await requireUserPage();
const [projects, connections] = await Promise.all([
prisma.project.findMany({
where: { ownerId: user.id },
include: projectListInclude,
orderBy: [{ favorite: "desc" }, { updatedAt: "desc" }],
}),
prisma.gitConnection.findMany({
where: { ownerId: user.id },
select: connectionSafeSelect,
orderBy: { name: "asc" },
}),
]);
const active = projects.filter((project) => project.status !== "ARCHIVED");
const ideas = active.filter((project) => project.status === "IDEA").length;
const inDev = active.filter((project) => project.status === "IN_DEVELOPMENT").length;
const done = active.filter((project) => project.status === "DONE").length;
const linked = active.filter((project) => project.repositories.length > 0).length;
const averageProgress =
active.length > 0
? Math.round(active.reduce((sum, project) => sum + project.progress, 0) / active.length)
: 0;
return (
<div className="space-y-6">
<header className="fade-up pt-4">
<p className="text-[11px] font-semibold uppercase tracking-[0.3em] text-violet-300/70">
Vibe Coding Kontrollzentrum
</p>
<h1 className="mt-2 text-3xl font-semibold tracking-tight text-white sm:text-4xl">
Deine Projekte auf{" "}
<span className="text-gradient">einen Blick</span>
</h1>
<p className="mt-2 max-w-2xl text-sm text-slate-400">
Ideen festhalten, Status pflegen, Notizen sammeln – und live sehen, was in den
verknuepften Repositories bei GitHub, GitLab und Gitea passiert.
</p>
{/* Kein weiterer Punkt in der Kopfleiste – die ist auf dem Telefon
schon eng. Der Rueckblick haengt dort, wo man ohnehin hinsieht. */}
<Link
href="/rueckblick"
className="mt-3 inline-flex items-center gap-1.5 text-sm text-violet-300 underline-offset-2 hover:underline"
>
<CalendarRange className="h-4 w-4" />
Wochenrückblick ansehen
</Link>
</header>
{/* Zwei Spalten schon auf dem Telefon: einspaltig fuellten die fuenf
Kacheln dort den ganzen ersten Bildschirm, und die Projekte – der
eigentliche Inhalt – begannen erst nach dem zweiten Wischen. */}
<div className="grid grid-cols-2 gap-3 sm:gap-4 lg:grid-cols-5">
<StatCard
label="Projekte"
value={active.length}
hint={`Ø Fortschritt ${averageProgress}%`}
icon={CircleDot}
color="#a78bfa"
className="col-span-2 lg:col-span-1"
/>
<StatCard
label="Ideen"
value={ideas}
// Kurzer Hinweis statt der langen Statusbeschreibung – die wurde in
// der schmalen Kachel mitten im Wort abgeschnitten.
hint="Noch nichts gebaut"
icon={Lightbulb}
color={STATUS_META.IDEA.color}
/>
<StatCard
label="In Entwicklung"
value={inDev}
hint="Aktiv in Arbeit"
icon={Rocket}
color={STATUS_META.IN_DEVELOPMENT.color}
/>
<StatCard
label="Fertig"
value={done}
hint="Ausgeliefert"
icon={Trophy}
color={STATUS_META.DONE.color}
/>
<StatCard
label="Repos verknuepft"
value={linked}
hint={`${connections.length} Git-Verbindung${connections.length === 1 ? "" : "en"}`}
icon={GitBranch}
color="#38bdf8"
/>
</div>
<ProjectBoard
projects={projects as unknown as ProjectListItem[]}
connections={connections as unknown as ConnectionSafe[]}
/>
</div>
);
}