"use client"; import Link from "next/link"; import { useState } from "react"; import { AlertTriangle, CheckSquare, GitBranch, GitCommitHorizontal, NotebookPen, Star, } from "lucide-react"; import { StatusSelect } from "@/components/StatusSelect"; import { PRIORITY_META, accentOf, statusOf } from "@/lib/status"; import { cn, formatRelative } from "@/lib/utils"; import type { ProjectListItem } from "@/types"; /** * Listenansicht des Boards. * * Warum zusaetzlich zum Raster: eine Karte braucht rund 260px Hoehe, auf dem * Telefon steht sie zwangslaeufig allein in der Zeile. Fuenf Projekte fuellen * damit zwei Bildschirme, bevor man ueberhaupt etwas vergleichen kann. Eine * Zeile kommt mit einem Viertel davon aus, und weil alle Zeilen gleich * aufgebaut sind, liegen Name, Fortschritt und Status untereinander – genau * das, was das Raster auf schmalen Anzeigen verliert. */ export function ProjectList({ projects, selection, onToggleSelect, }: { projects: ProjectListItem[]; selection: Set; onToggleSelect?: (id: string, selected: boolean) => void; }) { return ( ); } function ProjectRow({ project, selected, onToggleSelect, }: { project: ProjectListItem; selected: boolean; onToggleSelect?: (id: string, selected: boolean) => void; }) { const accent = accentOf(project.accent); const meta = statusOf(project.status); const priority = PRIORITY_META[project.priority] ?? PRIORITY_META[2]; const snapshot = project.repositories[0]?.snapshot ?? null; const syncError = project.repositories.find((repo) => repo.syncError)?.syncError ?? null; const [favorite, setFavorite] = useState(project.favorite); async function toggleFavorite() { const next = !favorite; setFavorite(next); try { const response = await fetch(`/api/projects/${project.id}`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ favorite: next }), }); if (!response.ok) throw new Error("failed"); } catch { setFavorite(!next); } } return (
  • {/* Die ganze Zeile ist der Link; was darueber bedienbar sein soll, bekommt weiter unten die Zeigerereignisse zurueck. */}
    {onToggleSelect ? ( onToggleSelect(project.id, event.target.checked)} aria-label={`${project.name} auswaehlen`} className={cn( "pointer-events-auto h-4 w-4 shrink-0 accent-violet-500 transition-opacity", !selected && "opacity-35 group-hover:opacity-100 focus-visible:opacity-100", )} /> ) : null} {/* Statusfarbe als Streifen: auf einen Blick erkennbar, ohne Platz fuer eine Beschriftung zu verbrauchen. */}
  • ); }