mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-26 07:13:09 +00:00
refactor(ui): migrate to shadcn/ui components and fix scroll issues
Migrate UI component library from custom implementations to shadcn/ui: - Add shadcn/ui primitives (Button, Card, Dialog, Input, etc.) - Replace custom styles with Tailwind CSS v4 theme configuration - Remove custom-theme.css in favor of globals.css with @theme directive Fix scroll overflow issues in multiple components: - ProjectSelector: "New Project" button no longer overlays project list - FolderBrowser: folder list now scrolls properly within modal - AgentCard: log modal content stays within bounds - ConversationHistory: conversation list scrolls correctly - KanbanColumn: feature cards scroll within fixed height - ScheduleModal: schedule form content scrolls properly Key technical changes: - Replace ScrollArea component with native overflow-y-auto divs - Add min-h-0 to flex containers to allow proper shrinking - Restructure dropdown layouts with flex-col for fixed footers New files: - ui/components.json (shadcn/ui configuration) - ui/src/components/ui/* (20 UI primitive components) - ui/src/lib/utils.ts (cn utility for class merging) - ui/tsconfig.app.json (app-specific TypeScript config) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,26 +2,27 @@ import { CheckCircle2, Circle, Loader2, MessageCircle } from 'lucide-react'
|
||||
import type { Feature, ActiveAgent } from '../lib/types'
|
||||
import { DependencyBadge } from './DependencyBadge'
|
||||
import { AgentAvatar } from './AgentAvatar'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
|
||||
interface FeatureCardProps {
|
||||
feature: Feature
|
||||
onClick: () => void
|
||||
isInProgress?: boolean
|
||||
allFeatures?: Feature[]
|
||||
activeAgent?: ActiveAgent // Agent working on this feature
|
||||
activeAgent?: ActiveAgent
|
||||
}
|
||||
|
||||
// Generate consistent color for category using CSS variable references
|
||||
// These map to the --color-neo-category-* variables defined in globals.css
|
||||
// Generate consistent color for category
|
||||
function getCategoryColor(category: string): string {
|
||||
const colors = [
|
||||
'var(--color-neo-category-pink)',
|
||||
'var(--color-neo-category-cyan)',
|
||||
'var(--color-neo-category-green)',
|
||||
'var(--color-neo-category-yellow)',
|
||||
'var(--color-neo-category-orange)',
|
||||
'var(--color-neo-category-purple)',
|
||||
'var(--color-neo-category-blue)',
|
||||
'bg-pink-500',
|
||||
'bg-cyan-500',
|
||||
'bg-green-500',
|
||||
'bg-yellow-500',
|
||||
'bg-orange-500',
|
||||
'bg-purple-500',
|
||||
'bg-blue-500',
|
||||
]
|
||||
|
||||
let hash = 0
|
||||
@@ -38,86 +39,85 @@ export function FeatureCard({ feature, onClick, isInProgress, allFeatures = [],
|
||||
const hasActiveAgent = !!activeAgent
|
||||
|
||||
return (
|
||||
<button
|
||||
<Card
|
||||
onClick={onClick}
|
||||
className={`
|
||||
w-full text-left neo-card p-4 cursor-pointer relative
|
||||
${isInProgress ? 'animate-pulse-neo' : ''}
|
||||
${feature.passes ? 'border-neo-done' : ''}
|
||||
${isBlocked && !feature.passes ? 'border-neo-danger opacity-80' : ''}
|
||||
${hasActiveAgent ? 'ring-2 ring-neo-progress ring-offset-2' : ''}
|
||||
cursor-pointer transition-all hover:border-primary py-3
|
||||
${isInProgress ? 'animate-pulse' : ''}
|
||||
${feature.passes ? 'border-primary/50' : ''}
|
||||
${isBlocked && !feature.passes ? 'border-destructive/50 opacity-80' : ''}
|
||||
${hasActiveAgent ? 'ring-2 ring-primary ring-offset-2' : ''}
|
||||
`}
|
||||
>
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between gap-2 mb-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="neo-badge"
|
||||
style={{ backgroundColor: categoryColor, color: 'var(--color-neo-text-on-bright)' }}
|
||||
>
|
||||
{feature.category}
|
||||
</span>
|
||||
<DependencyBadge feature={feature} allFeatures={allFeatures} compact />
|
||||
</div>
|
||||
<span className="font-mono text-sm text-neo-text-secondary">
|
||||
#{feature.priority}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Name */}
|
||||
<h3 className="font-display font-bold mb-1 line-clamp-2">
|
||||
{feature.name}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-sm text-neo-text-secondary line-clamp-2 mb-3">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{/* Agent working on this feature */}
|
||||
{activeAgent && (
|
||||
<div className="flex items-center gap-2 mb-3 py-2 px-2 rounded bg-[var(--color-neo-progress)]/10 border border-[var(--color-neo-progress)]/30">
|
||||
<AgentAvatar name={activeAgent.agentName} state={activeAgent.state} size="sm" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-bold text-neo-progress">
|
||||
{activeAgent.agentName} is working on this!
|
||||
</div>
|
||||
{activeAgent.thought && (
|
||||
<div className="flex items-center gap-1 mt-0.5">
|
||||
<MessageCircle size={10} className="text-neo-text-secondary shrink-0" />
|
||||
<p className="text-[10px] text-neo-text-secondary truncate italic">
|
||||
{activeAgent.thought}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
<CardContent className="p-4 space-y-3">
|
||||
{/* Header */}
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge className={`${categoryColor} text-white`}>
|
||||
{feature.category}
|
||||
</Badge>
|
||||
<DependencyBadge feature={feature} allFeatures={allFeatures} compact />
|
||||
</div>
|
||||
<span className="font-mono text-sm text-muted-foreground">
|
||||
#{feature.priority}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Status */}
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
{isInProgress ? (
|
||||
<>
|
||||
<Loader2 size={16} className="animate-spin text-neo-progress" />
|
||||
<span className="text-neo-progress font-bold">Processing...</span>
|
||||
</>
|
||||
) : feature.passes ? (
|
||||
<>
|
||||
<CheckCircle2 size={16} className="text-neo-done" />
|
||||
<span className="text-neo-done font-bold">Complete</span>
|
||||
</>
|
||||
) : isBlocked ? (
|
||||
<>
|
||||
<Circle size={16} className="text-neo-danger" />
|
||||
<span className="text-neo-danger">Blocked</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Circle size={16} className="text-neo-text-secondary" />
|
||||
<span className="text-neo-text-secondary">Pending</span>
|
||||
</>
|
||||
{/* Name */}
|
||||
<h3 className="font-semibold line-clamp-2">
|
||||
{feature.name}
|
||||
</h3>
|
||||
|
||||
{/* Description */}
|
||||
<p className="text-sm text-muted-foreground line-clamp-2">
|
||||
{feature.description}
|
||||
</p>
|
||||
|
||||
{/* Agent working on this feature */}
|
||||
{activeAgent && (
|
||||
<div className="flex items-center gap-2 py-2 px-2 rounded-md bg-primary/10 border border-primary/30">
|
||||
<AgentAvatar name={activeAgent.agentName} state={activeAgent.state} size="sm" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-semibold text-primary">
|
||||
{activeAgent.agentName} is working on this!
|
||||
</div>
|
||||
{activeAgent.thought && (
|
||||
<div className="flex items-center gap-1 mt-0.5">
|
||||
<MessageCircle size={10} className="text-muted-foreground shrink-0" />
|
||||
<p className="text-[10px] text-muted-foreground truncate italic">
|
||||
{activeAgent.thought}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</button>
|
||||
|
||||
{/* Status */}
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
{isInProgress ? (
|
||||
<>
|
||||
<Loader2 size={16} className="animate-spin text-primary" />
|
||||
<span className="text-primary font-medium">Processing...</span>
|
||||
</>
|
||||
) : feature.passes ? (
|
||||
<>
|
||||
<CheckCircle2 size={16} className="text-primary" />
|
||||
<span className="text-primary font-medium">Complete</span>
|
||||
</>
|
||||
) : isBlocked ? (
|
||||
<>
|
||||
<Circle size={16} className="text-destructive" />
|
||||
<span className="text-destructive">Blocked</span>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Circle size={16} className="text-muted-foreground" />
|
||||
<span className="text-muted-foreground">Pending</span>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user