feat(ui): Dashboard mit Board, Filtern und direktem Statuswechsel
- Fuenf Kennzahlen ueber dem Board: Projektzahl mit Durchschnittsfortschritt, Ideen, aktive Entwicklungen, fertige Projekte, verknuepfte Repos - Volltextsuche ueber Name, Beschreibung, Tags und Repo-Pfad - Statusfilter als Chips mit Trefferzaehler, mehrfach kombinierbar - Sortierung nach Aenderung, Anlage, Name, Fortschritt oder Prioritaet; Favoriten stehen immer oben - Zwei Ansichten: freies Raster oder nach Status gruppiert - Archiv standardmaessig ausgeblendet, per Schalter sichtbar Die Projektkarte zeigt Fortschritt, Provider, Default-Branch, letzten Tag, Alter des letzten Commits, Notizzahl und Sync-Fehler. Status und Favorit sind direkt auf der Karte aenderbar - beides optimistisch, mit Rollback wenn der Server ablehnt. Die Karte ist ein "stretched link", damit die Schaltflaechen darin nicht in einem Anker verschachtelt sind. Der Anlege-Dialog verlangt nur einen Namen; neue Projekte starten als Idee. Ist eine Git-Verbindung gewaehlt, laesst sich das Repository aus einer durchsuchbaren Liste uebernehmen. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
import { CircleDot, GitBranch, Lightbulb, Rocket, Trophy } from "lucide-react";
|
||||
import { ProjectBoard } from "@/components/ProjectBoard";
|
||||
import { StatCard } from "@/components/StatCard";
|
||||
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 [projects, connections] = await Promise.all([
|
||||
prisma.project.findMany({
|
||||
include: projectListInclude,
|
||||
orderBy: [{ favorite: "desc" }, { updatedAt: "desc" }],
|
||||
}),
|
||||
prisma.gitConnection.findMany({
|
||||
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) => Boolean(project.repoOwner && project.repoName)).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>
|
||||
</header>
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-5">
|
||||
<StatCard
|
||||
label="Projekte"
|
||||
value={active.length}
|
||||
hint={`Ø Fortschritt ${averageProgress}%`}
|
||||
icon={CircleDot}
|
||||
color="#a78bfa"
|
||||
/>
|
||||
<StatCard
|
||||
label="Ideen"
|
||||
value={ideas}
|
||||
hint={STATUS_META.IDEA.description}
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user