"use client"; import { useMemo } from "react"; import { ChevronRight, Plus } from "lucide-react"; import { Accordion } from "@base-ui/react/accordion"; import { Tooltip, TooltipTrigger, TooltipContent } from "@multica/ui/components/ui/tooltip"; import { Button } from "@multica/ui/components/ui/button"; import type { Issue, IssueStatus } from "@multica/core/types"; import { useLoadMoreDoneIssues } from "@multica/core/issues/mutations"; import type { MyIssuesFilter } from "@multica/core/issues/queries"; import { STATUS_CONFIG } from "@multica/core/issues/config"; import { useModalStore } from "@multica/core/modals"; import { useViewStore } from "@multica/core/issues/stores/view-store-context"; import { useIssueSelectionStore } from "@multica/core/issues/stores/selection-store"; import { sortIssues } from "../utils/sort"; import { StatusIcon } from "./status-icon"; import { ListRow, type ChildProgress } from "./list-row"; import { InfiniteScrollSentinel } from "./infinite-scroll-sentinel"; const EMPTY_PROGRESS_MAP = new Map(); export function ListView({ issues, visibleStatuses, childProgressMap = EMPTY_PROGRESS_MAP, doneTotal: doneTotalOverride, myIssuesScope, myIssuesFilter, }: { issues: Issue[]; visibleStatuses: IssueStatus[]; childProgressMap?: Map; /** Override the done-group count (e.g. with a server-filtered total). */ doneTotal?: number; /** When set, use the My Issues load-more hook instead of the workspace one. */ myIssuesScope?: string; myIssuesFilter?: MyIssuesFilter; }) { const sortBy = useViewStore((s) => s.sortBy); const sortDirection = useViewStore((s) => s.sortDirection); const listCollapsedStatuses = useViewStore( (s) => s.listCollapsedStatuses ); const toggleListCollapsed = useViewStore( (s) => s.toggleListCollapsed ); const selectedIds = useIssueSelectionStore((s) => s.selectedIds); const select = useIssueSelectionStore((s) => s.select); const deselect = useIssueSelectionStore((s) => s.deselect); const myIssuesOpts = myIssuesScope ? { scope: myIssuesScope, filter: myIssuesFilter ?? {} } : undefined; const { loadMore, hasMore, isLoading: loadingMore, doneTotal: hookDoneTotal } = useLoadMoreDoneIssues(myIssuesOpts); const displayDoneTotal = doneTotalOverride ?? hookDoneTotal; const issuesByStatus = useMemo(() => { const map = new Map(); for (const status of visibleStatuses) { const filtered = issues.filter((i) => i.status === status); map.set(status, sortIssues(filtered, sortBy, sortDirection)); } return map; }, [issues, visibleStatuses, sortBy, sortDirection]); const expandedStatuses = useMemo( () => visibleStatuses.filter( (s) => !listCollapsedStatuses.includes(s) ), [visibleStatuses, listCollapsedStatuses] ); return (
{ for (const status of visibleStatuses) { const wasExpanded = expandedStatuses.includes(status); const isExpanded = value.includes(status); if (wasExpanded !== isExpanded) { toggleListCollapsed(status as IssueStatus); } } }} > {visibleStatuses.map((status) => { const cfg = STATUS_CONFIG[status]; const statusIssues = issuesByStatus.get(status) ?? []; const statusIssueIds = statusIssues.map((i) => i.id); const selectedCount = statusIssueIds.filter((id) => selectedIds.has(id)).length; const allSelected = statusIssues.length > 0 && selectedCount === statusIssues.length; const someSelected = selectedCount > 0; return (
{ if (el) el.indeterminate = someSelected && !allSelected; }} onChange={() => { if (allSelected) { deselect(statusIssueIds); } else { select(statusIssueIds); } }} className="cursor-pointer accent-primary" />
{cfg.label} {status === "done" ? displayDoneTotal : statusIssues.length}
useModalStore .getState() .open("create-issue", { status }) } /> } > Add issue
{statusIssues.length > 0 ? ( <> {statusIssues.map((issue) => ( ))} {status === "done" && hasMore && ( )} ) : (

No issues

)}
); })}
); }