"use client"; import Link from "next/link"; import { ChevronDown, ChevronRight, ExternalLink, Pencil, Plus } from "lucide-react"; import { useMemo, useState, useTransition } from "react"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import type { ListIndexItem, ListWithItemsDto } from "../server/queries"; import { addItem, createList, toggleItem, updateListProperties } from "../server/actions"; export function ListsIndex({ lists }: { lists: ListWithItemsDto[] }) { const [listRows, setListRows] = useState(lists); const [type, setType] = useState("shopping"); const [name, setName] = useState(""); const [, startTransition] = useTransition(); const grouped = useMemo(() => { const groups = new Map(); for (const list of listRows) { groups.set(list.type, [...(groups.get(list.type) ?? []), list]); } return [...groups.entries()].sort(([a], [b]) => a.localeCompare(b)); }, [listRows]); function addList() { startTransition(async () => { const created = await createList({ type, name }); setListRows((current) => [ ...current, { id: created.id, type: created.type, name: created.name, archived: created.archived, openCount: 0, doneCount: 0, createdAt: created.createdAt.toISOString(), items: [], }, ]); setName(""); }); } function handleToggle(listId: string, item: ListIndexItem, done: boolean) { setListRows((current) => current.map((list) => list.id !== listId ? list : { ...list, openCount: list.openCount + (done ? -1 : 1), doneCount: list.doneCount + (done ? 1 : -1), items: list.items.map((i) => (i.id === item.id ? { ...i, done } : i)), }, ), ); startTransition(async () => { await toggleItem({ id: item.id, done }); }); } function handleAddItem(listId: string, text: string) { setListRows((current) => current.map((list) => list.id !== listId ? list : { ...list, openCount: list.openCount + 1, items: [ ...list.items, { id: `temp-${Date.now()}`, text, done: false, position: list.items.length, }, ], }, ), ); startTransition(async () => { await addItem({ listId, text }); const { listListsWithItems } = await import("../server/queries"); const refreshed = await listListsWithItems(); setListRows(refreshed); }); } function handleUpdateList(listId: string, patch: { name?: string; type?: string }) { setListRows((current) => current.map((list) => (list.id === listId ? { ...list, ...patch } : list)), ); startTransition(async () => { await updateListProperties({ id: listId, ...patch }); }); } return (

Lists

Shopping, tasks, and whatever comes next.

setType(event.target.value)} />
setName(event.target.value)} />
{grouped.map(([groupType, groupLists]) => (
{groupType}
{groupLists.map((list) => ( ))}
))}
); } function ListCard({ list, onToggle, onAddItem, onUpdateList, }: { list: ListWithItemsDto; onToggle: (listId: string, item: ListIndexItem, done: boolean) => void; onAddItem: (listId: string, text: string) => void; onUpdateList: (listId: string, patch: { name?: string; type?: string }) => void; }) { const [expanded, setExpanded] = useState(true); const [editing, setEditing] = useState(false); const [draftName, setDraftName] = useState(list.name); const [draftType, setDraftType] = useState(list.type); const [itemDraft, setItemDraft] = useState(""); function saveListProperties() { const name = draftName.trim(); const type = draftType.trim(); if (!name || !type) return; onUpdateList(list.id, { name, type }); setEditing(false); } function submitItem() { const text = itemDraft.trim(); if (!text) return; onAddItem(list.id, text); setItemDraft(""); } return (
{editing ? (
setDraftName(e.target.value)} /> setDraftType(e.target.value)} />
) : ( <>

{list.name}

{list.type} · {list.openCount} open · {list.doneCount} done
)}
{!editing && ( )}
{expanded && (
{list.items.length === 0 ? (

{list.openCount === 0 ? "All done!" : "No items to show."}

) : (
{list.items.map((item) => (
{item.text}
))} {list.openCount > list.items.filter((i) => !i.done).length && (
+{list.openCount - list.items.filter((i) => !i.done).length} more — open list
)}
)}
setItemDraft(e.target.value)} placeholder="Add a task…" onKeyDown={(e) => { if (e.key === "Enter") { e.preventDefault(); submitItem(); } }} />
)}
); }