mirror of
https://github.com/leonvanzyl/autocoder.git
synced 2026-03-17 10:53:09 +00:00
All 5 WebSocket endpoints (expand, spec, assistant, terminal, project) were closing the connection before calling accept() when validation failed. Starlette converts pre-accept close into an HTTP 403, giving clients no meaningful error information. Server changes: - Move websocket.accept() before all validation checks in every WS handler - Send JSON error message before closing so clients get actionable errors - Fix validate_project_name usage (raises HTTPException, not returns bool) - ConnectionManager.connect() no longer calls accept() (caller's job) Client changes: - All 3 WS hooks (useWebSocket, useExpandChat, useSpecChat) skip reconnection on 4xxx close codes (application errors won't self-resolve) - Gate expand button, keyboard shortcut, and modal on hasSpec - Add hasSpec to useEffect dependency array to prevent stale closure - Update keyboard shortcuts help text for E key context Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
79 lines
2.5 KiB
TypeScript
79 lines
2.5 KiB
TypeScript
import { KanbanColumn } from './KanbanColumn'
|
|
import type { Feature, FeatureListResponse, ActiveAgent } from '../lib/types'
|
|
import { Card, CardContent } from '@/components/ui/card'
|
|
|
|
interface KanbanBoardProps {
|
|
features: FeatureListResponse | undefined
|
|
onFeatureClick: (feature: Feature) => void
|
|
onAddFeature?: () => void
|
|
onExpandProject?: () => void
|
|
activeAgents?: ActiveAgent[]
|
|
onCreateSpec?: () => void
|
|
hasSpec?: boolean
|
|
}
|
|
|
|
export function KanbanBoard({ features, onFeatureClick, onAddFeature, onExpandProject, activeAgents = [], onCreateSpec, hasSpec = true }: KanbanBoardProps) {
|
|
const hasFeatures = features && (features.pending.length + features.in_progress.length + features.done.length) > 0
|
|
|
|
// Combine all features for dependency status calculation
|
|
const allFeatures = features
|
|
? [...features.pending, ...features.in_progress, ...features.done]
|
|
: []
|
|
|
|
if (!features) {
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
{['Pending', 'In Progress', 'Done'].map(title => (
|
|
<Card key={title} className="py-4">
|
|
<CardContent className="p-4">
|
|
<div className="h-8 bg-muted animate-pulse rounded mb-4" />
|
|
<div className="space-y-3">
|
|
{[1, 2, 3].map(i => (
|
|
<div key={i} className="h-24 bg-muted animate-pulse rounded" />
|
|
))}
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
|
<KanbanColumn
|
|
title="Pending"
|
|
count={features.pending.length}
|
|
features={features.pending}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="pending"
|
|
onFeatureClick={onFeatureClick}
|
|
onAddFeature={onAddFeature}
|
|
onExpandProject={onExpandProject}
|
|
showExpandButton={hasFeatures && hasSpec}
|
|
onCreateSpec={onCreateSpec}
|
|
showCreateSpec={!hasSpec && !hasFeatures}
|
|
/>
|
|
<KanbanColumn
|
|
title="In Progress"
|
|
count={features.in_progress.length}
|
|
features={features.in_progress}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="progress"
|
|
onFeatureClick={onFeatureClick}
|
|
/>
|
|
<KanbanColumn
|
|
title="Done"
|
|
count={features.done.length}
|
|
features={features.done}
|
|
allFeatures={allFeatures}
|
|
activeAgents={activeAgents}
|
|
color="done"
|
|
onFeatureClick={onFeatureClick}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|