From fb6d6bbf2fb6ef64184519e2096ddb0a5a7a3b90 Mon Sep 17 00:00:00 2001 From: Stefan de Vogelaere Date: Fri, 23 Jan 2026 02:26:57 +0100 Subject: [PATCH] fix(ui): address PR #644 review comments Keyboard accessibility: - Add role="button", tabIndex, onKeyDown, and aria-label to clickable divs in project-status-card, recent-activity-feed, and running-agents-panel Bug fixes: - Fix handleActivityClick to use projectPath instead of projectId for initializeProject and check result before navigating - Fix error handling in use-multi-project-status to use data.error string directly instead of data.error?.message Improvements: - Use GitBranch icon instead of Folder for branch display in running-agents-panel - Add error logging for failed project loads in overview.ts - Use type import for FeatureLoader in projects/index.ts - Add data-testid to mobile Overview button in dashboard-view - Add locale options for consistent time formatting in overview-view Co-Authored-By: Claude Opus 4.5 --- apps/server/src/routes/projects/index.ts | 2 +- .../src/routes/projects/routes/overview.ts | 1 + .../src/components/views/dashboard-view.tsx | 1 + .../ui/src/components/views/overview-view.tsx | 7 +++- .../views/overview/project-status-card.tsx | 11 +++++++ .../views/overview/recent-activity-feed.tsx | 33 ++++++++++++++----- .../views/overview/running-agents-panel.tsx | 18 ++++++++-- apps/ui/src/hooks/use-multi-project-status.ts | 2 +- 8 files changed, 62 insertions(+), 13 deletions(-) diff --git a/apps/server/src/routes/projects/index.ts b/apps/server/src/routes/projects/index.ts index df0b558d..24ecef14 100644 --- a/apps/server/src/routes/projects/index.ts +++ b/apps/server/src/routes/projects/index.ts @@ -3,7 +3,7 @@ */ import { Router } from 'express'; -import { FeatureLoader } from '../../services/feature-loader.js'; +import type { FeatureLoader } from '../../services/feature-loader.js'; import type { AutoModeService } from '../../services/auto-mode-service.js'; import type { SettingsService } from '../../services/settings-service.js'; import type { NotificationService } from '../../services/notification-service.js'; diff --git a/apps/server/src/routes/projects/routes/overview.ts b/apps/server/src/routes/projects/routes/overview.ts index 18f6c8b0..dfbbd206 100644 --- a/apps/server/src/routes/projects/routes/overview.ts +++ b/apps/server/src/routes/projects/routes/overview.ts @@ -188,6 +188,7 @@ export function createOverviewHandler( unreadNotificationCount, }; } catch (error) { + logError(error, `Failed to load project status: ${projectRef.name}`); // Return a minimal status for projects that fail to load return { projectId: projectRef.id, diff --git a/apps/ui/src/components/views/dashboard-view.tsx b/apps/ui/src/components/views/dashboard-view.tsx index 842ba251..a8ca953f 100644 --- a/apps/ui/src/components/views/dashboard-view.tsx +++ b/apps/ui/src/components/views/dashboard-view.tsx @@ -579,6 +579,7 @@ export function DashboardView() { size="icon" onClick={() => navigate({ to: '/overview' })} title="Projects Overview" + data-testid="projects-overview-button-mobile" > diff --git a/apps/ui/src/components/views/overview-view.tsx b/apps/ui/src/components/views/overview-view.tsx index 68ae60f5..6a40ed0d 100644 --- a/apps/ui/src/components/views/overview-view.tsx +++ b/apps/ui/src/components/views/overview-view.tsx @@ -501,7 +501,12 @@ export function OverviewView() { {/* Footer timestamp */}
- Last updated: {new Date(overview.generatedAt).toLocaleTimeString()} + Last updated:{' '} + {new Date(overview.generatedAt).toLocaleTimeString(undefined, { + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + })}
)} diff --git a/apps/ui/src/components/views/overview/project-status-card.tsx b/apps/ui/src/components/views/overview/project-status-card.tsx index 490d880b..a2f2565c 100644 --- a/apps/ui/src/components/views/overview/project-status-card.tsx +++ b/apps/ui/src/components/views/overview/project-status-card.tsx @@ -87,8 +87,17 @@ export function ProjectStatusCard({ project, onProjectClick }: ProjectStatusCard } }, [project, onProjectClick, upsertAndSetCurrentProject, navigate]); + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleClick(); + } + }; + return (
diff --git a/apps/ui/src/components/views/overview/recent-activity-feed.tsx b/apps/ui/src/components/views/overview/recent-activity-feed.tsx index 0f797a1c..9eb80189 100644 --- a/apps/ui/src/components/views/overview/recent-activity-feed.tsx +++ b/apps/ui/src/components/views/overview/recent-activity-feed.tsx @@ -120,16 +120,19 @@ export function RecentActivityFeed({ activities, maxItems = 10 }: RecentActivity const handleActivityClick = useCallback( async (activity: RecentActivity) => { try { - const initResult = await initializeProject( - // We need to find the project path - use projectId as workaround - // In real implementation, this would look up the path from projects list - activity.projectId - ); - - // Navigate to the project - const projectPath = activity.projectId; + // Get project path from the activity (projectId is actually the path in our data model) + const projectPath = activity.projectPath || activity.projectId; const projectName = activity.projectName; + const initResult = await initializeProject(projectPath); + + if (!initResult.success) { + toast.error('Failed to initialize project', { + description: initResult.error || 'Unknown error', + }); + return; + } + upsertAndSetCurrentProject(projectPath, projectName); if (activity.featureId) { @@ -147,6 +150,16 @@ export function RecentActivityFeed({ activities, maxItems = 10 }: RecentActivity [navigate, upsertAndSetCurrentProject] ); + const handleActivityKeyDown = useCallback( + (e: React.KeyboardEvent, activity: RecentActivity) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleActivityClick(activity); + } + }, + [handleActivityClick] + ); + if (displayActivities.length === 0) { return (
@@ -166,8 +179,12 @@ export function RecentActivityFeed({ activities, maxItems = 10 }: RecentActivity return (
handleActivityClick(activity)} + onKeyDown={(e) => handleActivityKeyDown(e, activity)} + aria-label={`${config.label}: ${activity.featureName || activity.message} in ${activity.projectName}`} data-testid={`activity-item-${activity.id}`} > {/* Icon */} diff --git a/apps/ui/src/components/views/overview/running-agents-panel.tsx b/apps/ui/src/components/views/overview/running-agents-panel.tsx index e2d9413c..9f61166c 100644 --- a/apps/ui/src/components/views/overview/running-agents-panel.tsx +++ b/apps/ui/src/components/views/overview/running-agents-panel.tsx @@ -11,7 +11,7 @@ import { initializeProject } from '@/lib/project-init'; import { toast } from 'sonner'; import { cn } from '@/lib/utils'; import type { ProjectStatus } from '@automaker/types'; -import { Bot, Activity, Folder, ArrowRight } from 'lucide-react'; +import { Bot, Activity, GitBranch, ArrowRight } from 'lucide-react'; import { Button } from '@/components/ui/button'; interface RunningAgentsPanelProps { @@ -65,6 +65,16 @@ export function RunningAgentsPanel({ projects }: RunningAgentsPanelProps) { [navigate, upsertAndSetCurrentProject] ); + const handleAgentKeyDown = useCallback( + (e: React.KeyboardEvent, agent: RunningAgentInfo) => { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + handleAgentClick(agent); + } + }, + [handleAgentClick] + ); + if (runningAgents.length === 0) { return (
@@ -80,8 +90,12 @@ export function RunningAgentsPanel({ projects }: RunningAgentsPanelProps) { {runningAgents.map((agent) => (
handleAgentClick(agent)} + onKeyDown={(e) => handleAgentKeyDown(e, agent)} + aria-label={`View running agent for ${agent.projectName}`} data-testid={`running-agent-${agent.projectId}`} > {/* Animated icon */} @@ -111,7 +125,7 @@ export function RunningAgentsPanel({ projects }: RunningAgentsPanelProps) { )} {agent.activeBranch && ( - + {agent.activeBranch} )} diff --git a/apps/ui/src/hooks/use-multi-project-status.ts b/apps/ui/src/hooks/use-multi-project-status.ts index ab377795..2282ec7e 100644 --- a/apps/ui/src/hooks/use-multi-project-status.ts +++ b/apps/ui/src/hooks/use-multi-project-status.ts @@ -64,7 +64,7 @@ async function fetchProjectsOverview(): Promise { const data = await response.json(); if (!data.success) { - throw new Error(data.error?.message || 'Failed to fetch project overview'); + throw new Error(data.error || 'Failed to fetch project overview'); } return {