feat(web-ui): empty state with task generation CTA on task board (#477)#499
feat(web-ui): empty state with task generation CTA on task board (#477)#499
Conversation
- Add empty state when tasks.length === 0 (after loading completes — no flash) - Shows Task01Icon, explanatory text, and PRD availability context - PRD found: green dot "PRD found — ready to generate tasks" - No PRD: muted dot "No PRD yet — create one first" - Primary CTA: "Generate from PRD →" → /prd - Secondary CTA: "View PRD" (outline) → /prd - TaskBoardContent hidden when tasks.length === 0 - PRD existence from useSWR prdApi.getAll() (separate cache key)
WalkthroughAdded a PRD existence lookup and conditional rendering to TaskBoardView: when no tasks exist it shows an empty-state panel with context-aware messaging and CTAs (generate from PRD or create task); otherwise it renders the existing kanban content. PRD presence is determined via SWR Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Review: feat(web-ui): empty state with task generation CTA (#477)Clean, focused implementation for the empty state. A few observations: Issues1. Both CTAs navigate to the same route The primary CTA says "Generate from PRD →" but both buttons go to 2. PRD context dot flashes "No PRD yet" on first render
const hasPrd = prdData !== undefined && (prdData.total ?? 0) > 0;
// Only render the PRD context <p> when prdData !== undefined3. Both CTAs link to MinorJSX formatting on Arrow glyph in button — What's working well
The two-CTA-same-destination issue is the most user-facing concern — worth resolving before merge. |
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
web-ui/src/components/tasks/TaskBoardView.tsx (1)
38-43: Consider PRD loading state to prevent misleading status.While
prdDatais still loading,hasPrddefaults tofalse, which could briefly display "No PRD yet" even when a PRD exists. Since the task skeleton guards against the empty state flash, users may see the empty state with incorrect PRD context for a moment.Consider either:
- Also checking
prdData === undefinedto show a neutral state while loading- Using the PRD
isLoadingstate to conditionally render the status indicator💡 Optional: Show loading/neutral state for PRD status
- const { data: prdData } = useSWR<PrdListResponse>( + const { data: prdData, isLoading: isPrdLoading } = useSWR<PrdListResponse>( `/api/v2/prd?path=${workspacePath}`, () => prdApi.getAll(workspacePath) ); const hasPrd = (prdData?.total ?? 0) > 0;Then in the empty state, conditionally show a neutral indicator while
isPrdLoadingis true:{isPrdLoading ? ( <span className="flex items-center gap-1 justify-center"> <span className="h-1.5 w-1.5 rounded-full bg-muted-foreground/40 animate-pulse" /> Checking PRD status… </span> ) : hasPrd ? ( // existing PRD found UI ) : ( // existing no PRD UI )}🤖 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 38 - 43, The PRD presence check currently sets hasPrd from prdData which defaults to false while loading, causing a brief incorrect "No PRD" UI; update the useSWR call in TaskBoardView.tsx to also destructure the loading flag (e.g., isValidating or isLoading) alongside prdData, create an isPrdLoading boolean, and change the empty-state rendering to treat loading as neutral (check isPrdLoading first, then hasPrd via (prdData?.total ?? 0) > 0) so the UI shows a loading/neutral indicator while PRD data is being fetched instead of immediately showing "No PRD yet".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/src/components/tasks/TaskBoardView.tsx`:
- Around line 385-396: The secondary CTA currently labeled "View PRD" navigates
to /prd but should be "Create Task" and open the new task dialog per the issue;
in the TaskBoardView component replace the secondary Button/Link pair with a
Button that triggers the task-creation flow (e.g., call the existing
onCreateTask handler or modal opener such as
openNewTaskDialog/openCreateTaskModal) instead of linking to /prd, update the
button text to "Create Task", and if no handler yet, wire a prop like
onCreateTask to TaskBoardView and invoke it on click (or add a TODO comment if
leaving View PRD temporarily).
---
Nitpick comments:
In `@web-ui/src/components/tasks/TaskBoardView.tsx`:
- Around line 38-43: The PRD presence check currently sets hasPrd from prdData
which defaults to false while loading, causing a brief incorrect "No PRD" UI;
update the useSWR call in TaskBoardView.tsx to also destructure the loading flag
(e.g., isValidating or isLoading) alongside prdData, create an isPrdLoading
boolean, and change the empty-state rendering to treat loading as neutral (check
isPrdLoading first, then hasPrd via (prdData?.total ?? 0) > 0) so the UI shows a
loading/neutral indicator while PRD data is being fetched instead of immediately
showing "No PRD yet".
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 34e653d4-4b0d-4105-bde4-031982c50423
📒 Files selected for processing (1)
web-ui/src/components/tasks/TaskBoardView.tsx
| <div className="mt-6 flex gap-3"> | ||
| <Button asChild> | ||
| <Link href="/prd"> | ||
| Generate from PRD → | ||
| </Link> | ||
| </Button> | ||
| <Button variant="outline" asChild> | ||
| <Link href="/prd"> | ||
| View PRD | ||
| </Link> | ||
| </Button> | ||
| </div> |
There was a problem hiding this comment.
Secondary CTA differs from issue requirements.
The linked issue #477 specifies the secondary CTA should be "Create Task" to open the new task dialog, but the implementation has "View PRD" which navigates to /prd — the same destination as the primary CTA.
Having both buttons navigate to the same page provides limited value. Consider aligning with the original requirements to offer a direct "Create Task" action that opens the task creation dialog.
,
💡 Suggested fix to match issue requirements
If a task creation handler exists (e.g., onCreateTask or opening a modal), the secondary CTA could be:
- <Button variant="outline" asChild>
- <Link href="/prd">
- View PRD
- </Link>
- </Button>
+ <Button variant="outline" onClick={handleOpenCreateTaskDialog}>
+ Create Task
+ </Button>If task creation functionality doesn't exist yet, consider keeping "View PRD" as a temporary solution but noting this as a TODO for a follow-up.
🤖 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 385 - 396, The
secondary CTA currently labeled "View PRD" navigates to /prd but should be
"Create Task" and open the new task dialog per the issue; in the TaskBoardView
component replace the secondary Button/Link pair with a Button that triggers the
task-creation flow (e.g., call the existing onCreateTask handler or modal opener
such as openNewTaskDialog/openCreateTaskModal) instead of linking to /prd,
update the button text to "Create Task", and if no handler yet, wire a prop like
onCreateTask to TaskBoardView and invoke it on click (or add a TODO comment if
leaving View PRD temporarily).
…e+View when PRD exists
Review: feat(web-ui): empty state with task generation CTA (#477)The prior Claude review and CodeRabbit both flagged the two key issues well — I won't repeat the full analysis, but here's where things stand and what's still unresolved: Still unaddressed from prior reviews1. Duplicate CTA destination (primary blocker) Both the "Generate from PRD →" and "View PRD" buttons link to The fix is either:
2. PRD context dot flashes "No PRD yet" on first render Also flagged previously. The fix is straightforward — destructure const { data: prdData, isLoading: isPrdLoading } = useSWR<PrdListResponse>(...)Then guard the status New observation:
|
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@web-ui/src/components/tasks/TaskBoardView.tsx`:
- Around line 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.
- 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.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4bf319b2-b887-4147-b158-117517fbf80b
📒 Files selected for processing (1)
web-ui/src/components/tasks/TaskBoardView.tsx
| const { data: prdData } = useSWR<PrdListResponse>( | ||
| `/api/v2/prd?path=${workspacePath}`, | ||
| () => prdApi.getAll(workspacePath) | ||
| ); | ||
| const hasPrd = (prdData?.total ?? 0) > 0; | ||
|
|
There was a problem hiding this comment.
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.
| <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" /> |
There was a problem hiding this comment.
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.
| <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.
Summary
tasks.length === 0after loading completes (no flash — guarded by existingisLoadingskeleton)/prd/prdTaskBoardContentconditionally rendered only whentasks.length > 0useSWR+prdApi.getAll()(separate cache key)Closes #477
Test plan
Summary by CodeRabbit