Skip to content

Commit c8d0db5

Browse files
authored
feat(ui): replace emojis with Hugeicons in Dashboard and SessionStatus (#220)
Replace emoji characters with Hugeicons components for consistency with the Nova design system guidelines: Dashboard.tsx changes: - Multi-Agent Team: 🤖 → UserGroupIcon - Agent State: 🔄 → Loading03Icon - Recent Activity: 📝 → WorkHistoryIcon - Activity items: ✅🧪⚠️✓🤖👋💾 → appropriate icons - Task Statistics: 📊 → AnalyticsUpIcon - Review Task Breakdown: 📋 → ClipboardIcon - Active Tasks: 🎯 → Target02Icon - Issues & Tasks: 🎯 → Target02Icon - Code Review Findings: 🔍 → Search01Icon - Quality Gates: ✅ → CheckListIcon - Cost & Token Metrics: 📊 → AnalyticsUpIcon - Checkpoints: 💾 → FloppyDiskIcon SessionStatus.tsx changes: - Loading/main state: 📋 → ClipboardIcon - Error state: ⚠️ → Alert02Icon Test updates: - Dashboard.test.tsx: Added mocks for all new icons - SessionStatus.test.tsx: Added Hugeicons mock, updated test assertions - Integration tests: Added all icon mocks
1 parent 659e771 commit c8d0db5

File tree

6 files changed

+139
-29
lines changed

6 files changed

+139
-29
lines changed

web-ui/__tests__/components/Dashboard.test.tsx

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,16 +11,33 @@ import * as api from '@/lib/api';
1111
import * as websocket from '@/lib/websocket';
1212
import * as agentAssignment from '@/api/agentAssignment';
1313

14-
// Mock Hugeicons (used by PhaseProgress component)
14+
// Mock Hugeicons (used by PhaseProgress, Dashboard, and SessionStatus components)
1515
jest.mock('@hugeicons/react', () => ({
16+
// PhaseProgress icons
1617
Search01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="search-icon" />,
1718
TaskEdit01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="task-edit-icon" />,
1819
Wrench01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="wrench-icon" />,
19-
CheckmarkCircle01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="checkmark-icon" />,
20+
CheckmarkCircle01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="checkmark-circle-icon" />,
2021
Award01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="award-icon" />,
2122
RocketIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="rocket-icon" />,
2223
HelpCircleIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="help-icon" />,
2324
Idea01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="idea-icon" />,
25+
// Dashboard section header icons
26+
UserGroupIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="user-group-icon" />,
27+
Loading03Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="loading-icon" />,
28+
WorkHistoryIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="work-history-icon" />,
29+
AnalyticsUpIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="analytics-icon" />,
30+
ClipboardIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="clipboard-icon" />,
31+
CheckListIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="checklist-icon" />,
32+
Target02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="target-icon" />,
33+
FloppyDiskIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="floppy-icon" />,
34+
// Activity item icons
35+
TestTube01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="test-tube-icon" />,
36+
Alert02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="alert-icon" />,
37+
CheckmarkSquare01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="checkmark-square-icon" />,
38+
BotIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="bot-icon" />,
39+
Logout02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="logout-icon" />,
40+
GitCommitIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="git-commit-icon" />,
2441
}));
2542

2643
// Create a shared mock WebSocket client that will be used across all tests

web-ui/__tests__/components/SessionStatus.test.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@
33
* Tests for session lifecycle display (014-session-lifecycle, T030)
44
*/
55

6+
// Mock Hugeicons used by SessionStatus
7+
jest.mock('@hugeicons/react', () => ({
8+
ClipboardIcon: ({ className }: { className?: string }) => (
9+
<svg className={className} data-testid="clipboard-icon" />
10+
),
11+
Alert02Icon: ({ className }: { className?: string }) => (
12+
<svg className={className} data-testid="alert-icon" />
13+
),
14+
}));
15+
616
// Mock the api-client module BEFORE imports
717
jest.mock('@/lib/api-client', () => ({
818
authFetch: jest.fn(),
@@ -34,7 +44,7 @@ describe('SessionStatus', () => {
3444
render(<SessionStatus projectId={1} />);
3545

3646
expect(screen.getByText('Loading session...')).toBeInTheDocument();
37-
expect(screen.getByText('📋')).toBeInTheDocument();
47+
expect(screen.getByTestId('clipboard-icon')).toBeInTheDocument();
3848
});
3949

4050
it('shows blue background for loading state', () => {
@@ -61,15 +71,15 @@ describe('SessionStatus', () => {
6171
});
6272
});
6373

64-
it('shows warning emoji for error state', async () => {
74+
it('shows alert icon for error state', async () => {
6575
mockAuthFetch.mockRejectedValueOnce(
6676
new Error('Failed to fetch')
6777
);
6878

6979
render(<SessionStatus projectId={1} />);
7080

7181
await waitFor(() => {
72-
expect(screen.getByText('⚠️')).toBeInTheDocument();
82+
expect(screen.getByTestId('alert-icon')).toBeInTheDocument();
7383
});
7484
});
7585

web-ui/__tests__/integration/dashboard-realtime-updates.test.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ import * as websocket from '@/lib/websocket';
1313
jest.mock('@/lib/api');
1414
jest.mock('@/lib/websocket');
1515

16-
// Mock Hugeicons (used by PhaseProgress component)
16+
// Mock Hugeicons (used by PhaseProgress, Dashboard, and SessionStatus components)
1717
jest.mock('@hugeicons/react', () => ({
18+
// PhaseProgress icons
1819
Search01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="search-icon" />,
1920
TaskEdit01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="task-edit-icon" />,
2021
Wrench01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="wrench-icon" />,
@@ -23,6 +24,22 @@ jest.mock('@hugeicons/react', () => ({
2324
RocketIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="rocket-icon" />,
2425
HelpCircleIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="help-icon" />,
2526
Idea01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="idea-icon" />,
27+
// Dashboard section header icons
28+
UserGroupIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="user-group-icon" />,
29+
Loading03Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="loading-icon" />,
30+
WorkHistoryIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="work-history-icon" />,
31+
AnalyticsUpIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="analytics-icon" />,
32+
ClipboardIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="clipboard-icon" />,
33+
CheckListIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="checklist-icon" />,
34+
Target02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="target-icon" />,
35+
FloppyDiskIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="floppy-icon" />,
36+
// Activity item icons
37+
TestTube01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="test-tube-icon" />,
38+
Alert02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="alert-icon" />,
39+
CheckmarkSquare01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="checkmark-square-icon" />,
40+
BotIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="bot-icon" />,
41+
Logout02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="logout-icon" />,
42+
GitCommitIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="git-commit-icon" />,
2643
}));
2744

2845
jest.mock('@/components/ChatInterface', () => ({

web-ui/__tests__/integration/prd-button-sync.test.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,21 @@ jest.mock('@hugeicons/react', () => ({
3535
RocketIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="rocket-icon" />,
3636
HelpCircleIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="help-icon" />,
3737
Idea01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="idea-icon" />,
38+
// Dashboard section header icons
39+
UserGroupIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="user-group-icon" />,
40+
Loading03Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="loading-icon" />,
41+
WorkHistoryIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="work-history-icon" />,
42+
AnalyticsUpIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="analytics-icon" />,
43+
ClipboardIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="clipboard-icon" />,
44+
CheckListIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="checklist-icon" />,
45+
Target02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="target-icon" />,
46+
FloppyDiskIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="floppy-icon" />,
47+
// Activity item icons
48+
TestTube01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="test-tube-icon" />,
49+
CheckmarkSquare01Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="checkmark-square-icon" />,
50+
BotIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="bot-icon" />,
51+
Logout02Icon: ({ className }: { className?: string }) => <svg className={className} data-testid="logout-icon" />,
52+
GitCommitIcon: ({ className }: { className?: string }) => <svg className={className} data-testid="git-commit-icon" />,
3853
}));
3954
jest.mock('@/components/ChatInterface', () => ({
4055
__esModule: true,

web-ui/src/components/Dashboard.tsx

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,24 @@ import TaskStats from './tasks/TaskStats';
3737
import PhaseProgress from './PhaseProgress';
3838
import TaskList from './TaskList';
3939
import TaskReview from './TaskReview';
40+
import {
41+
UserGroupIcon,
42+
Loading03Icon,
43+
WorkHistoryIcon,
44+
CheckmarkCircle01Icon,
45+
TestTube01Icon,
46+
Alert02Icon,
47+
CheckmarkSquare01Icon,
48+
BotIcon,
49+
Logout02Icon,
50+
GitCommitIcon,
51+
AnalyticsUpIcon,
52+
ClipboardIcon,
53+
Search01Icon,
54+
CheckListIcon,
55+
Target02Icon,
56+
FloppyDiskIcon,
57+
} from '@hugeicons/react';
4058

4159
/**
4260
* Maps backend phase names to PhaseProgress component phase names.
@@ -593,7 +611,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
593611

594612
{/* Agents Section - Multi-Agent Per Project Architecture */}
595613
<div className="bg-card rounded-lg shadow p-6 mb-6" data-testid="agent-status-panel">
596-
<h2 className="text-lg font-semibold mb-4">🤖 Multi-Agent Team</h2>
614+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
615+
<UserGroupIcon className="h-5 w-5" />
616+
Multi-Agent Team
617+
</h2>
597618
<AgentList
598619
projectId={projectId}
599620
onAgentClick={handleAgentClick}
@@ -606,7 +627,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
606627
{agents.length > 0 && (
607628
<div className="bg-card rounded-lg shadow p-6 mb-6" data-testid="agent-state-panel">
608629
<div className="flex items-center justify-between mb-4">
609-
<h2 className="text-lg font-semibold">🔄 Agent State (Real-time)</h2>
630+
<h2 className="text-lg font-semibold flex items-center gap-2">
631+
<Loading03Icon className="h-5 w-5" />
632+
Agent State (Real-time)
633+
</h2>
610634
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-secondary text-secondary-foreground">
611635
{agents.length} agents active
612636
</span>
@@ -633,23 +657,25 @@ export default function Dashboard({ projectId }: DashboardProps) {
633657

634658
{/* Recent Activity */}
635659
<div className="bg-card rounded-lg shadow p-6">
636-
<h2 className="text-lg font-semibold mb-4">📝 Recent Activity</h2>
660+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
661+
<WorkHistoryIcon className="h-5 w-5" />
662+
Recent Activity
663+
</h2>
637664
<div className="space-y-2">
638665
{activity.length > 0 ? (
639666
activity.map((item, index) => (
640667
<div key={index} className="flex items-start gap-3 text-sm">
641668
<span className="text-muted-foreground min-w-[60px]">
642669
{new Date(item.timestamp).toLocaleTimeString()}
643670
</span>
644-
<span className="flex-1">
645-
{item.type === 'task_completed' && '✅'}
646-
{item.type === 'test_result' && '🧪'}
647-
{item.type === 'task_blocked' && '⚠️'}
648-
{item.type === 'task_unblocked' && '✓'}
649-
{item.type === 'agent_created' && '🤖'}
650-
{item.type === 'agent_retired' && '👋'}
651-
{item.type === 'commit_created' && '💾'}
652-
{' '}
671+
<span className="flex-1 flex items-center gap-1">
672+
{item.type === 'task_completed' && <CheckmarkCircle01Icon className="h-4 w-4 text-green-600 flex-shrink-0" />}
673+
{item.type === 'test_result' && <TestTube01Icon className="h-4 w-4 text-blue-600 flex-shrink-0" />}
674+
{item.type === 'task_blocked' && <Alert02Icon className="h-4 w-4 text-yellow-600 flex-shrink-0" />}
675+
{item.type === 'task_unblocked' && <CheckmarkSquare01Icon className="h-4 w-4 text-green-600 flex-shrink-0" />}
676+
{item.type === 'agent_created' && <BotIcon className="h-4 w-4 text-primary flex-shrink-0" />}
677+
{item.type === 'agent_retired' && <Logout02Icon className="h-4 w-4 text-muted-foreground flex-shrink-0" />}
678+
{item.type === 'commit_created' && <GitCommitIcon className="h-4 w-4 text-purple-600 flex-shrink-0" />}
653679
<span className="font-medium">{item.agent}:</span> {item.message}
654680
</span>
655681
</div>
@@ -669,7 +695,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
669695
<div role="tabpanel" id="tasks-panel" aria-labelledby="tasks-tab" data-testid="tasks-panel">
670696
{/* Task Statistics Section */}
671697
<div className="bg-card rounded-lg shadow p-6 mb-6">
672-
<h2 className="text-lg font-semibold mb-4">📊 Task Statistics</h2>
698+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
699+
<AnalyticsUpIcon className="h-5 w-5" />
700+
Task Statistics
701+
</h2>
673702
<TaskStats />
674703
</div>
675704

@@ -678,7 +707,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
678707
/* Planning Phase: Show TaskReview for task approval */
679708
<div className="bg-card rounded-lg shadow p-6 mb-6">
680709
<div className="flex items-center justify-between mb-4">
681-
<h2 className="text-lg font-semibold">📋 Review Task Breakdown</h2>
710+
<h2 className="text-lg font-semibold flex items-center gap-2">
711+
<ClipboardIcon className="h-5 w-5" />
712+
Review Task Breakdown
713+
</h2>
682714
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200">
683715
Awaiting Approval
684716
</span>
@@ -691,7 +723,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
691723
/* Development/Review Phase: Show TaskList for active tasks */
692724
<div className="bg-card rounded-lg shadow p-6 mb-6">
693725
<div className="flex items-center justify-between mb-4">
694-
<h2 className="text-lg font-semibold">🎯 Active Tasks</h2>
726+
<h2 className="text-lg font-semibold flex items-center gap-2">
727+
<Target02Icon className="h-5 w-5" />
728+
Active Tasks
729+
</h2>
695730
<span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200">
696731
In Development
697732
</span>
@@ -704,7 +739,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
704739
/* Discovery/Complete/Shipped: Show traditional TaskTreeView */
705740
<div className="bg-card rounded-lg shadow p-6 mb-6">
706741
<div className="flex items-center justify-between mb-4">
707-
<h2 className="text-lg font-semibold">🎯 Issues & Tasks</h2>
742+
<h2 className="text-lg font-semibold flex items-center gap-2">
743+
<Target02Icon className="h-5 w-5" />
744+
Issues & Tasks
745+
</h2>
708746
{issuesData && (
709747
<span className="text-sm text-muted-foreground">
710748
{issuesData.total_issues} issues, {issuesData.total_tasks} tasks
@@ -745,7 +783,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
745783
{/* Review Findings Panel (T065, Sprint 10) */}
746784
<div className="mb-6" data-testid="review-findings-panel">
747785
<div className="bg-card rounded-lg shadow p-6">
748-
<h2 className="text-lg font-semibold mb-4">🔍 Code Review Findings</h2>
786+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
787+
<Search01Icon className="h-5 w-5" />
788+
Code Review Findings
789+
</h2>
749790
<ReviewSummary reviewResult={reviewData} loading={reviewLoading} />
750791
</div>
751792
</div>
@@ -762,7 +803,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
762803
onError={handleQualityGatesError}
763804
>
764805
<div className="bg-card rounded-lg shadow p-6">
765-
<h2 className="text-lg font-semibold mb-4">✅ Quality Gates</h2>
806+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
807+
<CheckListIcon className="h-5 w-5" />
808+
Quality Gates
809+
</h2>
766810
<QualityGatesPanel projectId={projectId} tasks={tasks} />
767811
</div>
768812
</ErrorBoundary>
@@ -786,7 +830,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
786830
{activeTab === 'metrics' && (
787831
<div role="tabpanel" id="metrics-panel" aria-labelledby="metrics-tab" data-testid="metrics-panel">
788832
<div className="bg-card rounded-lg shadow p-6">
789-
<h2 className="text-lg font-semibold mb-4">📊 Cost & Token Metrics</h2>
833+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
834+
<AnalyticsUpIcon className="h-5 w-5" />
835+
Cost & Token Metrics
836+
</h2>
790837
<CostDashboard projectId={projectId} />
791838
</div>
792839
</div>
@@ -843,7 +890,10 @@ export default function Dashboard({ projectId }: DashboardProps) {
843890
{activeTab === 'checkpoints' && (
844891
<div role="tabpanel" id="checkpoints-panel" aria-labelledby="checkpoints-tab">
845892
<div className="bg-card rounded-lg shadow p-6" data-testid="checkpoint-panel">
846-
<h2 className="text-lg font-semibold mb-4">💾 Checkpoints</h2>
893+
<h2 className="text-lg font-semibold mb-4 flex items-center gap-2">
894+
<FloppyDiskIcon className="h-5 w-5" />
895+
Checkpoints
896+
</h2>
847897
<CheckpointList projectId={projectId} refreshInterval={30000} />
848898
</div>
849899
</div>

0 commit comments

Comments
 (0)