-
Notifications
You must be signed in to change notification settings - Fork 5
feat(web-ui): empty state with task generation CTA on task board (#477) #499
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 { | ||||||
|
|
@@ -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; | ||||||
|
|
||||||
| // ─── Filter state ────────────────────────────────────────────── | ||||||
| const [searchQuery, setSearchQuery] = useState(''); | ||||||
| const [statusFilter, setStatusFilter] = useState<TaskStatus | null>(null); | ||||||
|
|
@@ -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" /> | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use a gray-scale status token instead of 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, 📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||
| 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} | ||||||
|
|
@@ -366,7 +416,7 @@ export function TaskBoardView({ workspacePath }: TaskBoardViewProps) { | |||||
| onDeselectAll={handleDeselectAll} | ||||||
| loadingTaskIds={loadingTaskIds} | ||||||
| requirementsMap={requirementsMap} | ||||||
| /> | ||||||
| />} | ||||||
|
|
||||||
| {/* Task detail modal */} | ||||||
| <TaskDetailModal | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid treating PRD status as “absent” before the PRD query resolves.
Line 43 defaults
hasPrdtofalsewhile PRD data is still loading (or errored), so the empty state can briefly show incorrect “No PRD yet” context.Suggested fix
Also applies to: 373-383
🤖 Prompt for AI Agents