Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 55 additions & 5 deletions web-ui/src/components/tasks/TaskBoardView.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
'use client';

import { useState, useCallback, useMemo } from 'react';
import Link from 'next/link';
import { useRouter } from 'next/navigation';
import useSWR from 'swr';
import { TaskBoardContent } from './TaskBoardContent';
import { TaskDetailModal } from './TaskDetailModal';
import { TaskFilters } from './TaskFilters';
import { BatchActionsBar } from './BatchActionsBar';
import { BulkActionConfirmDialog, type BulkActionType } from './BulkActionConfirmDialog';
import { Cancel01Icon } from '@hugeicons/react';
import { tasksApi } from '@/lib/api';
import { Cancel01Icon, Task01Icon } from '@hugeicons/react';
import { Button } from '@/components/ui/button';
import { tasksApi, prdApi } from '@/lib/api';
import { useRequirementsLookup } from '@/hooks/useRequirementsLookup';
import type {
TaskStatus,
TaskListResponse,
BatchStrategy,
ApiError,
PrdListResponse,
} from '@/types';

interface TaskBoardViewProps {
Expand All @@ -32,6 +35,13 @@ export function TaskBoardView({ workspacePath }: TaskBoardViewProps) {
);
const { requirementsMap } = useRequirementsLookup(workspacePath);

// PRD existence check — drives empty state context message
const { data: prdData } = useSWR<PrdListResponse>(
`/api/v2/prd?path=${workspacePath}`,
() => prdApi.getAll(workspacePath)
);
const hasPrd = (prdData?.total ?? 0) > 0;

Comment on lines +39 to +44
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid treating PRD status as “absent” before the PRD query resolves.

Line 43 defaults hasPrd to false while PRD data is still loading (or errored), so the empty state can briefly show incorrect “No PRD yet” context.

Suggested fix
-  const { data: prdData } = useSWR<PrdListResponse>(
+  const { data: prdData, isLoading: isPrdLoading, error: prdError } = useSWR<PrdListResponse>(
     `/api/v2/prd?path=${workspacePath}`,
     () => prdApi.getAll(workspacePath)
   );
   const hasPrd = (prdData?.total ?? 0) > 0;
-            {hasPrd ? (
+            {isPrdLoading ? (
+              <span className="flex items-center gap-1 justify-center">
+                <span className="h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />
+                Checking PRD status…
+              </span>
+            ) : prdError ? (
+              <span className="flex items-center gap-1 justify-center">
+                <span className="h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />
+                Couldn’t verify PRD status
+              </span>
+            ) : hasPrd ? (
               <span className="flex items-center gap-1 justify-center">
                 <span className="h-1.5 w-1.5 rounded-full bg-green-500" />
                 PRD found — ready to generate tasks
               </span>
             ) : (

Also applies to: 373-383

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/tasks/TaskBoardView.tsx` around lines 39 - 44, The code
treats PRD as absent while the SWR request is unresolved; change the hasPrd
logic to only compute a boolean after the request completes by reading useSWR's
return (e.g., const { data: prdData, error: prdError, isValidating } =
useSWR...) and set hasPrd to a tri-state (undefined while loading) or only
true/false when prdData is defined (e.g., hasPrd = prdData ? (prdData.total ??
0) > 0 : undefined), then update any UI checks that show the “No PRD yet” empty
state to only show when prdData is settled (use prdData/prdError/isValidating)
rather than relying on a default false.

// ─── Filter state ──────────────────────────────────────────────
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState<TaskStatus | null>(null);
Expand Down Expand Up @@ -351,8 +361,48 @@ export function TaskBoardView({ workspacePath }: TaskBoardViewProps) {
</div>
)}

{/* Kanban board */}
<TaskBoardContent
{/* Empty state — shown when no tasks exist after loading completes */}
{tasks.length === 0 && (
<div className="flex flex-col items-center justify-center rounded-lg border bg-muted/30 py-16 text-center">
<Task01Icon className="mb-4 h-10 w-10 text-muted-foreground/50" />
<h2 className="text-base font-semibold text-foreground">No tasks yet</h2>
<p className="mt-1 max-w-sm text-sm text-muted-foreground">
Generate tasks from your PRD or create them manually to start building.
</p>
<p className="mt-2 text-xs text-muted-foreground">
{hasPrd ? (
<span className="flex items-center gap-1 justify-center">
<span className="h-1.5 w-1.5 rounded-full bg-green-500" />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use a gray-scale status token instead of bg-green-500.

The hardcoded green class breaks the gray-only UI palette used in this project.

Suggested fix
-                <span className="h-1.5 w-1.5 rounded-full bg-green-500" />
+                <span className="h-1.5 w-1.5 rounded-full bg-foreground/60" />

As per coding guidelines, web-ui/src/**/*.{ts,tsx} must use Shadcn/UI (Nova preset) with gray color scheme and Hugeicons.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span className="h-1.5 w-1.5 rounded-full bg-green-500" />
<span className="h-1.5 w-1.5 rounded-full bg-foreground/60" />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web-ui/src/components/tasks/TaskBoardView.tsx` at line 375, In TaskBoardView
locate the status token span with class "h-1.5 w-1.5 rounded-full bg-green-500"
and replace the hardcoded green class with the project's gray-scale token from
the Shadcn/UI (Nova preset) design system (or swap the raw span for the shadcn
Indicator/Badge component configured for the gray color scheme); ensure you use
the gray utility (e.g., the Nova gray token used across web-ui) so the status
dot matches the gray-only UI palette.

PRD found — ready to generate tasks
</span>
) : (
<span className="flex items-center gap-1 justify-center">
<span className="h-1.5 w-1.5 rounded-full bg-muted-foreground/40" />
No PRD yet — create one first
</span>
)}
</p>
<div className="mt-6 flex gap-3">
{hasPrd ? (
<>
<Button asChild>
<Link href="/prd">Generate from PRD →</Link>
</Button>
<Button variant="outline" asChild>
<Link href="/prd">View PRD</Link>
</Button>
</>
) : (
<Button asChild>
<Link href="/prd">Create PRD →</Link>
</Button>
)}
</div>
</div>
)}

{/* Kanban board — hidden when no tasks */}
{tasks.length > 0 && <TaskBoardContent
tasks={filteredTasks}
selectionMode={selectionMode}
selectedTaskIds={selectedTaskIds}
Expand All @@ -366,7 +416,7 @@ export function TaskBoardView({ workspacePath }: TaskBoardViewProps) {
onDeselectAll={handleDeselectAll}
loadingTaskIds={loadingTaskIds}
requirementsMap={requirementsMap}
/>
/>}

{/* Task detail modal */}
<TaskDetailModal
Expand Down
Loading