mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 20:03:37 +00:00
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 <noreply@anthropic.com>
This commit is contained in:
@@ -579,6 +579,7 @@ export function DashboardView() {
|
||||
size="icon"
|
||||
onClick={() => navigate({ to: '/overview' })}
|
||||
title="Projects Overview"
|
||||
data-testid="projects-overview-button-mobile"
|
||||
>
|
||||
<LayoutDashboard className="w-4 h-4" />
|
||||
</Button>
|
||||
|
||||
@@ -501,7 +501,12 @@ export function OverviewView() {
|
||||
|
||||
{/* Footer timestamp */}
|
||||
<div className="text-center text-xs text-muted-foreground pt-4">
|
||||
Last updated: {new Date(overview.generatedAt).toLocaleTimeString()}
|
||||
Last updated:{' '}
|
||||
{new Date(overview.generatedAt).toLocaleTimeString(undefined, {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -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 (
|
||||
<div
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className={cn(
|
||||
'group relative rounded-xl border bg-card/60 backdrop-blur-sm transition-all duration-300 cursor-pointer hover:-translate-y-0.5',
|
||||
project.healthStatus === 'active' && 'border-green-500/30 hover:border-green-500/50',
|
||||
@@ -98,6 +107,8 @@ export function ProjectStatusCard({ project, onProjectClick }: ProjectStatusCard
|
||||
project.healthStatus === 'idle' && 'border-border hover:border-brand-500/40'
|
||||
)}
|
||||
onClick={handleClick}
|
||||
onKeyDown={handleKeyDown}
|
||||
aria-label={`Open project ${project.projectName}`}
|
||||
data-testid={`project-status-card-${project.projectId}`}
|
||||
>
|
||||
<div className="p-4">
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-muted-foreground">
|
||||
@@ -166,8 +179,12 @@ export function RecentActivityFeed({ activities, maxItems = 10 }: RecentActivity
|
||||
return (
|
||||
<div
|
||||
key={activity.id}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="group flex items-start gap-3 p-2 rounded-lg hover:bg-muted/50 cursor-pointer transition-colors"
|
||||
onClick={() => 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 */}
|
||||
|
||||
@@ -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 (
|
||||
<div className="flex flex-col items-center justify-center py-8 text-muted-foreground">
|
||||
@@ -80,8 +90,12 @@ export function RunningAgentsPanel({ projects }: RunningAgentsPanelProps) {
|
||||
{runningAgents.map((agent) => (
|
||||
<div
|
||||
key={agent.projectId}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
className="group flex items-center gap-3 p-3 rounded-lg border border-green-500/20 bg-green-500/5 hover:bg-green-500/10 cursor-pointer transition-all"
|
||||
onClick={() => 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 && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Folder className="w-3 h-3" />
|
||||
<GitBranch className="w-3 h-3" />
|
||||
{agent.activeBranch}
|
||||
</span>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user