feat: migrate all icons to Lucide React
All checks were successful
Build & Publish / publish (push) Successful in 1m2s

Replace unicode characters, emoji, and inline SVGs with lucide-react
components across the entire design system and page layer. Update
tests to assert on SVG elements instead of text content.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-03-27 23:25:43 +01:00
parent 2ffc268b44
commit 433d582da6
24 changed files with 128 additions and 123 deletions

View File

@@ -1,5 +1,6 @@
import { useState, useMemo } from 'react'
import { useParams, Link } from 'react-router-dom'
import { ChevronRight } from 'lucide-react'
import styles from './AgentHealth.module.css'
// Layout
@@ -389,7 +390,7 @@ export function AgentHealth() {
{scope.level !== 'all' && (
<>
<Link to="/agents" className={styles.scopeLink}>All Agents</Link>
<span className={styles.scopeSep}>&#9656;</span>
<span className={styles.scopeSep}><ChevronRight size={12} /></span>
<span className={styles.scopeCurrent}>{scope.appId}</span>
</>
)}

View File

@@ -1,5 +1,6 @@
import { useMemo } from 'react'
import { useParams, Link } from 'react-router-dom'
import { ChevronRight } from 'lucide-react'
import styles from './AgentInstance.module.css'
// Layout
@@ -177,9 +178,9 @@ export function AgentInstance() {
{/* Scope trail + badges */}
<div className={styles.scopeTrail}>
<Link to="/agents" className={styles.scopeLink}>All Agents</Link>
<span className={styles.scopeSep}>&#9656;</span>
<span className={styles.scopeSep}><ChevronRight size={12} /></span>
<Link to={`/agents/${appId}`} className={styles.scopeLink}>{appId}</Link>
<span className={styles.scopeSep}>&#9656;</span>
<span className={styles.scopeSep}><ChevronRight size={12} /></span>
<span className={styles.scopeCurrent}>{agent.name}</span>
<Badge label={agent.status.toUpperCase()} color={statusColor} />
<Badge label={agent.version} color="auto" variant="outlined" />

View File

@@ -1,5 +1,6 @@
import { useState, useMemo } from 'react'
import React, { useState, useMemo } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { TrendingUp, TrendingDown, ArrowRight, ExternalLink, AlertTriangle } from 'lucide-react'
import styles from './Dashboard.module.css'
// Layout
@@ -43,10 +44,10 @@ const ACCENT_TO_COLOR: Record<KpiMetric['accent'], string> = {
warning: 'var(--warning)',
}
const TREND_ICONS: Record<KpiMetric['trend'], string> = {
up: '\u2191',
down: '\u2193',
neutral: '\u2192',
const TREND_ICONS: Record<KpiMetric['trend'], React.ReactNode> = {
up: <TrendingUp size={12} />,
down: <TrendingDown size={12} />,
neutral: <ArrowRight size={12} />,
}
function sentimentToVariant(sentiment: KpiMetric['trendSentiment']): 'success' | 'error' | 'muted' {
@@ -60,7 +61,7 @@ function sentimentToVariant(sentiment: KpiMetric['trendSentiment']): 'success' |
const kpiItems: KpiItem[] = kpiMetrics.map((m) => ({
label: m.label,
value: m.unit ? `${m.value} ${m.unit}` : m.value,
trend: { label: `${TREND_ICONS[m.trend]} ${m.trendValue}`, variant: sentimentToVariant(m.trendSentiment) },
trend: { label: <><span style={{ display: 'inline-flex', verticalAlign: 'middle' }}>{TREND_ICONS[m.trend]}</span> {m.trendValue}</>, variant: sentimentToVariant(m.trendSentiment) },
subtitle: m.detail,
sparkline: m.sparkline,
borderColor: ACCENT_TO_COLOR[m.accent],
@@ -206,7 +207,7 @@ export function Dashboard() {
navigate(`/exchanges/${row.id}`)
}}
>
&#x2197;
<ExternalLink size={14} />
</button>
),
}
@@ -303,7 +304,7 @@ export function Dashboard() {
className={styles.openDetailLink}
onClick={() => navigate(`/exchanges/${selectedExchange.id}`)}
>
Open full details &#x2192;
Open full details <ArrowRight size={14} style={{ verticalAlign: 'middle' }} />
</button>
</div>
@@ -428,7 +429,7 @@ export function Dashboard() {
expandedContent={(row) =>
row.errorMessage ? (
<div className={styles.inlineError}>
<span className={styles.inlineErrorIcon}></span>
<span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
<div>
<div className={styles.inlineErrorText}>{row.errorMessage}</div>
<div className={styles.inlineErrorHint}>Click to view full stack trace</div>

View File

@@ -1,4 +1,5 @@
import { useState } from 'react'
import { Hexagon, ArrowRight, Diamond, Eye, Pencil, RotateCcw, Trash2, ChevronDown } from 'lucide-react'
import styles from './CompositesSection.module.css'
import {
Accordion,
@@ -157,25 +158,25 @@ const TREE_NODES = [
{
id: 'app1',
label: 'cameleer-prod',
icon: '⬡',
icon: <Hexagon size={14} />,
children: [
{
id: 'route1',
label: 'order-ingest',
icon: '→',
icon: <ArrowRight size={14} />,
children: [
{ id: 'proc1', label: 'ValidateOrder', icon: '◈', meta: '12ms' },
{ id: 'proc2', label: 'EnrichPayload', icon: '◈', meta: '8ms' },
{ id: 'proc3', label: 'RouteToQueue', icon: '◈', meta: '3ms' },
{ id: 'proc1', label: 'ValidateOrder', icon: <Diamond size={12} />, meta: '12ms' },
{ id: 'proc2', label: 'EnrichPayload', icon: <Diamond size={12} />, meta: '8ms' },
{ id: 'proc3', label: 'RouteToQueue', icon: <Diamond size={12} />, meta: '3ms' },
],
},
{
id: 'route2',
label: 'payment-validate',
icon: '→',
icon: <ArrowRight size={14} />,
children: [
{ id: 'proc4', label: 'TokenizeCard', icon: '◈', meta: '22ms' },
{ id: 'proc5', label: 'AuthorizePayment', icon: '◈', meta: '45ms' },
{ id: 'proc4', label: 'TokenizeCard', icon: <Diamond size={12} />, meta: '22ms' },
{ id: 'proc5', label: 'AuthorizePayment', icon: <Diamond size={12} />, meta: '45ms' },
],
},
],
@@ -505,13 +506,13 @@ export function CompositesSection() {
description="Click-triggered dropdown menu with icons, dividers, and disabled items."
>
<Dropdown
trigger={<Button size="sm" variant="secondary">Actions </Button>}
trigger={<Button size="sm" variant="secondary">Actions <ChevronDown size={12} /></Button>}
items={[
{ label: 'View details', icon: '👁', onClick: () => undefined },
{ label: 'Edit route', icon: '✏', onClick: () => undefined },
{ label: 'View details', icon: <Eye size={14} />, onClick: () => undefined },
{ label: 'Edit route', icon: <Pencil size={14} />, onClick: () => undefined },
{ divider: true, label: '' },
{ label: 'Restart', icon: '↺', onClick: () => undefined },
{ label: 'Delete', icon: '✕', onClick: () => undefined, disabled: true },
{ label: 'Restart', icon: <RotateCcw size={14} />, onClick: () => undefined },
{ label: 'Delete', icon: <Trash2 size={14} />, onClick: () => undefined, disabled: true },
]}
/>
</DemoCard>

View File

@@ -1,4 +1,5 @@
import { useState } from 'react'
import { Search } from 'lucide-react'
import styles from './PrimitivesSection.module.css'
import {
Alert,
@@ -358,7 +359,7 @@ export function PrimitivesSection() {
description="Text input with optional leading icon and placeholder."
>
<Input placeholder="Plain input" />
<Input icon="🔍" placeholder="With icon" />
<Input icon={<Search size={14} />} placeholder="With icon" />
</DemoCard>
{/* 15b. InlineEdit */}

View File

@@ -1,5 +1,6 @@
import { useMemo } from 'react'
import { useParams, useNavigate } from 'react-router-dom'
import { AlertTriangle } from 'lucide-react'
import styles from './RouteDetail.module.css'
// Layout
@@ -337,7 +338,7 @@ export function RouteDetail() {
expandedContent={(row) =>
row.errorMessage ? (
<div className={styles.inlineError}>
<span className={styles.inlineErrorIcon}></span>
<span className={styles.inlineErrorIcon}><AlertTriangle size={14} /></span>
<div>
<div className={styles.errorClass}>{row.errorClass}</div>
<div className={styles.errorText}>{row.errorMessage}</div>