"use client"; import { useState } from "react"; import { trpc } from "~/lib/trpc/client.js"; import type { WidgetProps } from "~/components/dashboard/widget-registry.js"; import { ProjectStatus } from "@planarchy/shared/types"; import { InfoTooltip } from "~/components/ui/InfoTooltip.js"; const STATUS_COLORS: Record = { DRAFT: "bg-gray-100 text-gray-700", ACTIVE: "bg-green-100 text-green-700", ON_HOLD: "bg-yellow-100 text-yellow-700", COMPLETED: "bg-blue-100 text-blue-700", CANCELLED: "bg-red-100 text-red-700", }; export function ProjectTableWidget({ config, onConfigChange }: WidgetProps) { const status = (config.status as ProjectStatus) || undefined; const search = (config.search as string) || ""; const { data: projects, isLoading } = trpc.project.listWithCosts.useQuery( { status, search: search || undefined }, { staleTime: 60_000 }, ); type SortKey = "code" | "name" | "status" | "cost" | "personDays"; const [sortKey, setSortKey] = useState("name"); const [sortDir, setSortDir] = useState<"asc" | "desc">("asc"); function toggleSort(key: SortKey) { if (sortKey === key) setSortDir((d) => (d === "asc" ? "desc" : "asc")); else { setSortKey(key); setSortDir("asc"); } } if (isLoading) { return (
{/* header row */}
{[40, 120, 80, 60, 60].map((w, i) => (
))}
{/* data rows */} {[...Array(6)].map((_, i) => (
))}
); } interface ProjectRow { id: string; shortCode: string; name: string; status: string; totalCostCents: number; totalPersonDays: number; } const list = ((projects as unknown as { projects: ProjectRow[] } | undefined)?.projects ?? []) as ProjectRow[]; const sorted = [...list].sort((a, b) => { const mult = sortDir === "asc" ? 1 : -1; switch (sortKey) { case "code": return mult * a.shortCode.localeCompare(b.shortCode); case "name": return mult * a.name.localeCompare(b.name); case "status": return mult * a.status.localeCompare(b.status); case "cost": return mult * (a.totalCostCents - b.totalCostCents); case "personDays": return mult * (a.totalPersonDays - b.totalPersonDays); default: return 0; } }); return (
{/* Filters */}
onConfigChange?.({ search: e.target.value })} className="flex-1 min-w-0 px-2 py-1 text-xs border border-gray-300 rounded-lg" />
{/* Table */}
{sorted.map((p) => ( ))}
{p.shortCode} {p.name} {p.status} {(p.totalCostCents / 100).toLocaleString("de-DE", { maximumFractionDigits: 0 })} € {p.totalPersonDays}d
{list.length === 0 && (
No projects found.
)}
); }