"use client"; import * as React from "react"; import { Archive, MoreHorizontal, Pencil, } from "lucide-react"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Tooltip, TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; import type { ViewObject } from "@/lib/hooks/use-view-data"; import { usePanelStore } from "@/lib/stores/panel-store"; import { cn } from "@/lib/utils"; /** Shared grid for list header and rows — dense ClickUp-style columns */ export const LIST_ROW_GRID = "grid grid-cols-[28px_32px_minmax(0,1fr)_minmax(5rem,7.5rem)_5.5rem_6rem_5rem] gap-2 items-center px-3"; const STATUS_CYCLE = ["open", "in_progress", "done", "closed"] as const; function statusColorClass(status: string | null): string { switch (status) { case "in_progress": return "bg-blue-500 shadow-[0_0_0_1px_hsl(var(--background))]"; case "done": return "bg-green-500 shadow-[0_0_0_1px_hsl(var(--background))]"; case "closed": return "bg-slate-500 shadow-[0_0_0_1px_hsl(var(--background))]"; case "open": default: return "bg-muted-foreground/60 shadow-[0_0_0_1px_hsl(var(--background))]"; } } export function getPriorityValue(object: ViewObject): string { const v = object.propertyValues?.find( (p) => p.propertyDefinition.name === "Priority", )?.value; return typeof v === "string" ? v : "—"; } export function getDueDateValue(object: ViewObject): string { const v = object.propertyValues?.find( (p) => p.propertyDefinition.name === "Due Date", )?.value; return typeof v === "string" ? v : "—"; } function formatDueDisplay(iso: string): string { if (iso === "—") return "—"; try { const d = new Date(iso); if (Number.isNaN(d.getTime())) return iso; return d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric", }); } catch { return iso; } } export interface ListItemProps { object: ViewObject; onSelect: (id: string) => void; selected?: boolean; className?: string; rowIndex?: number; } export function ListItem({ object, onSelect, selected = false, className, rowIndex = 0, }: ListItemProps) { const openDetail = usePanelStore((s) => s.open); const [status, setStatus] = React.useState(object.status); React.useEffect(() => { setStatus(object.status); }, [object.status]); const priority = getPriorityValue(object); const dueRaw = getDueDateValue(object); const cycleStatus = (e: React.MouseEvent) => { e.preventDefault(); e.stopPropagation(); const current = status ?? "open"; const idx = STATUS_CYCLE.indexOf( current as (typeof STATUS_CYCLE)[number], ); const next = STATUS_CYCLE[(idx === -1 ? 0 : idx + 1) % STATUS_CYCLE.length]; setStatus(next); }; const initials = (name: string | null) => (name ?? "?") .split(/\s+/) .map((p) => p[0]) .join("") .slice(0, 2) .toUpperCase(); const zebra = rowIndex % 2 === 1; return (
{String(status ?? "open").replace("_", " ")}