fix(web): add load-more pagination for Done column on issue board (#492)

* fix(web): add load-more pagination for Done column on issue board

The Done column was capped at 50 issues with no way to load more.
Track doneTotal in the TQ cache and add a useLoadMoreDoneIssues hook
that fetches the next page and merges it into the unified issue cache.
The Done column now shows a "Load more" button when there are
additional items.

- shared/types/api.ts: add doneTotal to ListIssuesResponse
- core/issues/queries.ts: store doneTotal from the done-status response
- core/issues/mutations.ts: add useLoadMoreDoneIssues hook, update
  create/delete mutations to maintain doneTotal
- core/issues/ws-updaters.ts: maintain doneTotal on WS events
- features/issues/components/board-column.tsx: accept optional footer
- features/issues/components/board-view.tsx: render Load more button
  in Done column

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* fix(web): address review issues in done-column load-more

1. Fix total over-counting: loadMore no longer inflates total since
   the initial query already includes all done issues in total count.
2. Fix onIssueUpdated: maintain doneTotal when issue status changes
   to/from done via WS events.
3. Make doneTotal optional in ListIssuesResponse since it's a
   frontend-only field not returned by the backend API. All reads
   now use ?? 0 fallback.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
LinYushen 2026-04-08 14:58:51 +08:00 committed by GitHub
parent a7afd4b959
commit ec934f3a8b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 142 additions and 49 deletions

View file

@ -1,6 +1,6 @@
"use client";
import { useMemo } from "react";
import { useMemo, type ReactNode } from "react";
import { EyeOff, MoreHorizontal, Plus } from "lucide-react";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
import { useDroppable } from "@dnd-kit/core";
@ -23,10 +23,12 @@ export function BoardColumn({
status,
issueIds,
issueMap,
footer,
}: {
status: IssueStatus;
issueIds: string[];
issueMap: Map<string, Issue>;
footer?: ReactNode;
}) {
const cfg = STATUS_CONFIG[status];
const { setNodeRef, isOver } = useDroppable({ id: status });
@ -106,6 +108,7 @@ export function BoardColumn({
No issues
</p>
)}
{footer}
</div>
</div>
);

View file

@ -15,9 +15,10 @@ import {
type DragOverEvent,
} from "@dnd-kit/core";
import { arrayMove } from "@dnd-kit/sortable";
import { Eye, MoreHorizontal } from "lucide-react";
import { Eye, Loader2, MoreHorizontal } from "lucide-react";
import type { Issue, IssueStatus } from "@/shared/types";
import { Button } from "@/components/ui/button";
import { useLoadMoreDoneIssues } from "@core/issues/mutations";
import {
DropdownMenu,
DropdownMenuTrigger,
@ -110,6 +111,7 @@ export function BoardView({
}) {
const sortBy = useViewStore((s) => s.sortBy);
const sortDirection = useViewStore((s) => s.sortDirection);
const { loadMore, hasMore, isLoading: loadingMore } = useLoadMoreDoneIssues();
// --- Drag state ---
const [activeIssue, setActiveIssue] = useState<Issue | null>(null);
@ -272,6 +274,21 @@ export function BoardView({
status={status}
issueIds={columns[status] ?? []}
issueMap={issueMapRef.current}
footer={
status === "done" && hasMore ? (
<button
type="button"
className="mt-1 flex w-full items-center justify-center gap-1.5 rounded-md py-2 text-xs text-muted-foreground hover:bg-accent/60 transition-colors disabled:opacity-50"
onClick={loadMore}
disabled={loadingMore}
>
{loadingMore ? (
<Loader2 className="size-3 animate-spin" />
) : null}
{loadingMore ? "Loading..." : "Load more"}
</button>
) : undefined
}
/>
))}