diff --git a/apps/frontend/src/renderer/components/CreateSpecView.tsx b/apps/frontend/src/renderer/components/CreateSpecView.tsx index 384468b8b..e051874b3 100644 --- a/apps/frontend/src/renderer/components/CreateSpecView.tsx +++ b/apps/frontend/src/renderer/components/CreateSpecView.tsx @@ -44,6 +44,11 @@ import { cn } from '../lib/utils'; import type { PatternSuggestion } from '../../shared/types'; export type { PatternSuggestion }; +/** + * Import skeleton component for loading state + */ +import { PatternSuggestionsSkeleton } from './skeletons/PatternSuggestionsSkeleton'; + /** * Pattern action state */ @@ -440,16 +445,7 @@ export function CreateSpecView({ {/* Loading State */} - {isLoading && ( - - -
-
- Finding relevant patterns... -
- - - )} + {isLoading && } {/* Error State */} {error && ( diff --git a/apps/frontend/src/renderer/components/skeletons/PatternSuggestionsSkeleton.tsx b/apps/frontend/src/renderer/components/skeletons/PatternSuggestionsSkeleton.tsx new file mode 100644 index 000000000..fe805b30f --- /dev/null +++ b/apps/frontend/src/renderer/components/skeletons/PatternSuggestionsSkeleton.tsx @@ -0,0 +1,110 @@ +import { Card, CardContent, CardHeader } from '../ui/card'; + +interface PatternSuggestionsSkeletonProps { + /** Number of pattern cards to render */ + count?: number; + /** Whether to show header section */ + showHeader?: boolean; +} + +/** + * Skeleton loader for PatternSuggestions in CreateSpecView + * Matches the structure: header with badges, pattern cards with category badges, + * confidence levels, pattern text, action buttons, and expandable details + */ +export function PatternSuggestionsSkeleton({ + count = 3, + showHeader = true +}: PatternSuggestionsSkeletonProps) { + return ( +
+ {/* Header Section */} + {showHeader && ( +
+
+ {/* Title with icon */} +
+
+
+
+ {/* Subtitle */} +
+
+ {/* Summary badges */} +
+
+
+
+
+
+ )} + + {/* Pattern Cards */} +
+ {Array.from({ length: count }).map((_, index) => ( + + +
+ {/* Main content area */} +
+ {/* Category Badge and Confidence Level */} +
+
+
+
+ + {/* Pattern Text Lines */} +
+
+
+ {index % 2 === 0 && ( +
+ )} +
+
+ + {/* Action Buttons */} +
+
+
+
+
+
+ + + + {/* Expandable Details Section */} +
+ {/* "Show details" button placeholder */} +
+ + {/* Details content (simulate expanded state ~50% of the time) */} + {index % 2 === 0 && ( +
+ {/* Reasoning line */} +
+
+
+
+ + {/* Metadata row */} +
+
+
+
+
+
+
+
+
+
+
+ )} +
+ + + ))} +
+
+ ); +} diff --git a/apps/frontend/src/renderer/components/skeletons/SpecDetailSkeleton.tsx b/apps/frontend/src/renderer/components/skeletons/SpecDetailSkeleton.tsx new file mode 100644 index 000000000..3b44acd56 --- /dev/null +++ b/apps/frontend/src/renderer/components/skeletons/SpecDetailSkeleton.tsx @@ -0,0 +1,108 @@ +import { Card } from '../ui/card'; + +interface SpecDetailSkeletonProps { + /** Number of phase skeletons to render */ + phaseCount?: number; + /** Whether to show description placeholder */ + showDescription?: boolean; +} + +/** + * Skeleton loader for SpecDetail/TaskOverview implementation plan + * Matches the structure: section header, phases, subtasks, metadata + */ +export function SpecDetailSkeleton({ phaseCount = 3, showDescription = true }: SpecDetailSkeletonProps) { + return ( +
+ {/* Section Header */} +
+
+
+
+
+
+ + {/* Optional Description */} + {showDescription && ( +
+
+
+
+ )} + + {/* Phases */} +
+ {Array.from({ length: phaseCount }).map((_, phaseIndex) => ( + + {/* Phase Header */} +
+ {/* Chevron placeholder */} +
+ + {/* Status icon placeholder */} +
+ + {/* Phase title and badge */} +
+
+
+
+
+ + {/* Progress text */} +
+
+
+ + {/* Subtasks (simulate expanded phase) */} +
+ {/* Separator */} +
+ + {/* Subtask items */} + {Array.from({ length: 3 + (phaseIndex % 2) }).map((_, subtaskIndex) => ( +
+ {/* Status badge placeholder */} +
+ + {/* Subtask content */} +
+ {/* Subtask description */} +
+ {/* Verification text (optional) */} + {subtaskIndex % 2 === 0 && ( +
+ )} +
+
+ ))} +
+ + ))} +
+ + {/* QA Report Section (skeleton) */} +
+ {/* QA Report header */} +
+
+
+
+ + {/* QA content lines */} +
+
+
+
+
+
+
+
+ ); +} diff --git a/apps/frontend/src/renderer/components/skeletons/index.ts b/apps/frontend/src/renderer/components/skeletons/index.ts new file mode 100644 index 000000000..9f13550dd --- /dev/null +++ b/apps/frontend/src/renderer/components/skeletons/index.ts @@ -0,0 +1,7 @@ +export { ChangelogSkeleton } from './ChangelogSkeleton'; +export { IssueListSkeleton } from './IssueListSkeleton'; +export { PRListSkeleton } from './PRListSkeleton'; +export { ProjectListSkeleton } from './ProjectListSkeleton'; +export { TaskCardSkeleton } from './TaskCardSkeleton'; +export { SpecDetailSkeleton } from './SpecDetailSkeleton'; +export { PatternSuggestionsSkeleton } from './PatternSuggestionsSkeleton'; diff --git a/apps/frontend/src/renderer/components/task-detail/TaskOverview.tsx b/apps/frontend/src/renderer/components/task-detail/TaskOverview.tsx index 8e4dbc36a..f87108be6 100644 --- a/apps/frontend/src/renderer/components/task-detail/TaskOverview.tsx +++ b/apps/frontend/src/renderer/components/task-detail/TaskOverview.tsx @@ -16,6 +16,7 @@ import { import { Badge } from '../ui/badge'; import { Separator } from '../ui/separator'; import { ScrollArea } from '../ui/scroll-area'; +import { SpecDetailSkeleton } from '../skeletons/SpecDetailSkeleton'; import { cn } from '../../lib/utils'; import type { Task, ImplementationPlan, Phase, SubtaskStatus, QAEscalation } from '../../../shared/types'; @@ -124,11 +125,7 @@ export function TaskOverview({ task }: TaskOverviewProps) { }; if (isLoading) { - return ( -
- -
- ); + return ; } if (error) {