refactor(ui): replace one-off visual language with shared Badge components

- Replace .stat-chip with Badge in PresetMarketplace, BacktestTab, MembershipTab
- Replace .saved-setup-id-chip with Badge in SimpleView
- Replace .screener-sector-chip with Badge in ScreenerView
- Replace .health-pill with Badge in TradeProfileManager
- Remove CSS definitions for one-off classes from index.css
- All components now use shared Badge from product adapter
- Verify: audit:ui (0 findings), audit:ui:strict (0 findings), typecheck, build
This commit is contained in:
Saravana Achu Mac 2026-05-09 12:56:52 -07:00
parent a17de130c7
commit 94ce743bd0
10 changed files with 37 additions and 87 deletions

View File

@ -19,6 +19,7 @@ import { STRATEGY_PRESETS } from '../lib/PresetRegistry';
import { Button } from './ui/button'; import { Button } from './ui/button';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './ui/card';
import { PageHeader } from './ui/page-header'; import { PageHeader } from './ui/page-header';
import { Badge } from './ui/Primitives';
interface PresetMarketplaceProps { interface PresetMarketplaceProps {
onSelect: (preset: StrategyPreset) => void; onSelect: (preset: StrategyPreset) => void;
@ -74,7 +75,7 @@ const StrategyMarketplaceCard: React.FC<{
</div> </div>
</div> </div>
</div> </div>
<div className="stat-chip">V{index + 1}.4</div> <Badge variant="neutral">V{index + 1}.4</Badge>
</div> </div>
<div className="space-y-3"> <div className="space-y-3">
@ -185,7 +186,7 @@ export const PresetMarketplace: React.FC<PresetMarketplaceProps> = ({ onSelect,
description="Browse reusable strategy profiles with preconfigured risk posture and execution bias." description="Browse reusable strategy profiles with preconfigured risk posture and execution bias."
/> />
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<div className="stat-chip">{allPresets.length} presets</div> <Badge variant="neutral">{allPresets.length} presets</Badge>
{onClose ? ( {onClose ? (
<Button variant="outline" onClick={onClose}> <Button variant="outline" onClick={onClose}>
Return Return

View File

@ -28,7 +28,7 @@ import { Button } from './ui/button';
import { Card } from './ui/card'; import { Card } from './ui/card';
import { Input } from './ui/input'; import { Input } from './ui/input';
import { Select } from './ui/select'; import { Select } from './ui/select';
import { Textarea } from './ui/Primitives'; import { Textarea, Badge } from './ui/Primitives';
import { cn } from '../lib/utils'; import { cn } from '../lib/utils';
// ChatControl is now rendered globally in App.tsx // ChatControl is now rendered globally in App.tsx
@ -642,7 +642,6 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
const bp = botState.accountSnapshot?.buying_power ?? 0; const bp = botState.accountSnapshot?.buying_power ?? 0;
const isCovered = (p.allocated_capital || 0) <= bp; const isCovered = (p.allocated_capital || 0) <= bp;
const coverageStatus = isCovered ? 'Covered' : 'Insufficient funds'; const coverageStatus = isCovered ? 'Covered' : 'Insufficient funds';
const coverageColor = isCovered ? 'ok' : 'drift'; // reuse 'drift' style for warning
const lifecycleStatus = stats.tradeCount > 0 ? 'OK' : (p.is_active ? 'Blocked' : 'Idle'); const lifecycleStatus = stats.tradeCount > 0 ? 'OK' : (p.is_active ? 'Blocked' : 'Idle');
const reconciliationDrift = (botState.health?.reconciliationMismatchCount || 0) > 0 ? 'Drift' : 'Clean'; const reconciliationDrift = (botState.health?.reconciliationMismatchCount || 0) > 0 ? 'Drift' : 'Clean';
@ -752,15 +751,15 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
</div> </div>
<div className="profile-health-strip flex flex-wrap gap-1 px-4 py-2"> <div className="profile-health-strip flex flex-wrap gap-1 px-4 py-2">
<span className={`health-pill ${capitalHealth === 'Issue' ? 'issue' : 'ok'}`}>Capital: {capitalHealth}</span> <Badge variant={capitalHealth === 'Issue' ? 'error' : 'success'}>Capital: {capitalHealth}</Badge>
{botState.accountSnapshot && ( {botState.accountSnapshot && (
<span className={`health-pill ${coverageColor}`} title={`Buying Power: $${bp.toFixed(2)}`}> <Badge variant={isCovered ? 'success' : 'warning'} title={`Buying Power: $${bp.toFixed(2)}`}>
LP: {coverageStatus} LP: {coverageStatus}
</span> </Badge>
)} )}
<span className={`health-pill ${lifecycleStatus === 'OK' ? 'ok' : 'blocked'}`}>Lifecycle: {lifecycleStatus}</span> <Badge variant={lifecycleStatus === 'OK' ? 'success' : 'error'}>Lifecycle: {lifecycleStatus}</Badge>
<span className={`health-pill ${reconciliationDrift === 'Drift' ? 'drift' : 'clean'}`}>Reconciliation: {reconciliationDrift}</span> <Badge variant={reconciliationDrift === 'Drift' ? 'warning' : 'success'}>Reconciliation: {reconciliationDrift}</Badge>
<span className={`health-pill ${lockStatus === 'Contended' ? 'drift' : 'ok'}`}>Locks: {lockStatus}</span> <Badge variant={lockStatus === 'Contended' ? 'warning' : 'success'}>Locks: {lockStatus}</Badge>
</div> </div>
{/* Win rate section */} {/* Win rate section */}

View File

@ -1080,26 +1080,6 @@ body {
box-shadow: var(--card-shadow); box-shadow: var(--card-shadow);
} }
.stat-chip {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
border-radius: 999px;
border: 1px solid var(--border);
background: var(--card-elevated);
color: var(--muted-foreground);
font-size: 11px;
font-weight: 700;
}
.stat-chip[data-tone="success"] {
color: #16a34a;
}
.stat-chip[data-tone="danger"] {
color: #dc2626;
}
.trading-sidebar-nav { .trading-sidebar-nav {
flex: 1; flex: 1;
@ -2238,11 +2218,6 @@ body {
white-space: nowrap; white-space: nowrap;
} }
.saved-setup-id-chip,
.saved-setup-updated {
text-transform: none !important;
letter-spacing: 0 !important;
}
.saved-setup-timeline { .saved-setup-timeline {
display: grid; display: grid;
@ -2332,19 +2307,6 @@ body {
gap: 7px; gap: 7px;
} }
.screener-sector-chip {
min-height: 32px !important;
border-radius: 999px !important;
padding: 0 12px !important;
font-size: 11px !important;
font-weight: 700 !important;
}
.screener-sector-chip[data-active="true"] {
border-color: var(--accent) !important;
background: var(--accent-soft) !important;
color: var(--accent) !important;
}
.screener-more-select { .screener-more-select {
width: 150px; width: 150px;

View File

@ -7,6 +7,7 @@ import { fetchTradeProfiles } from '../lib/profileApi';
import { PageHeader } from '../components/ui/page-header'; import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card'; import { Card, CardContent } from '../components/ui/card';
import { Button } from '../components/ui/button'; import { Button } from '../components/ui/button';
import { Badge } from '../components/ui/Primitives';
import { Select } from '../components/ui/select'; import { Select } from '../components/ui/select';
interface BacktestTabProps { interface BacktestTabProps {
@ -84,18 +85,18 @@ export const BacktestTab: React.FC<BacktestTabProps> = ({ previewAsCustomer = fa
<Card style={{ marginBottom: 16 }}> <Card style={{ marginBottom: 16 }}>
<CardContent style={{ padding: '14px 16px' }}> <CardContent style={{ padding: '14px 16px' }}>
<div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' }}> <div style={{ display: 'flex', flexWrap: 'wrap', gap: '8px', marginBottom: '10px' }}>
<span className="stat-chip"> <Badge variant="neutral">
Role: {isAdminAccount ? 'admin' : 'customer'} Role: {isAdminAccount ? 'admin' : 'customer'}
</span> </Badge>
<span className="stat-chip"> <Badge variant="neutral">
Preview Mode: {previewAsCustomer ? 'customer view' : 'off'} Preview Mode: {previewAsCustomer ? 'customer view' : 'off'}
</span> </Badge>
<span className="stat-chip" data-tone={runtimeFlags.enableBacktest ? 'success' : 'danger'}> <Badge variant={runtimeFlags.enableBacktest ? 'success' : 'error'}>
ENABLE_BACKTEST: {String(runtimeFlags.enableBacktest)} ENABLE_BACKTEST: {String(runtimeFlags.enableBacktest)}
</span> </Badge>
<span className="stat-chip" data-tone={runtimeFlags.customerEnabled ? 'success' : 'danger'}> <Badge variant={runtimeFlags.customerEnabled ? 'success' : 'error'}>
BACKTEST_CUSTOMER_ENABLED: {String(runtimeFlags.customerEnabled)} BACKTEST_CUSTOMER_ENABLED: {String(runtimeFlags.customerEnabled)}
</span> </Badge>
</div> </div>
{!backtestGateLoading && !backtestEnabled && ( {!backtestGateLoading && !backtestEnabled && (
<p style={{ margin: 0, fontSize: '12px', color: 'var(--bl-danger-muted)' }}> <p style={{ margin: 0, fontSize: '12px', color: 'var(--bl-danger-muted)' }}>

View File

@ -15,6 +15,7 @@ import { TIER_POLICIES } from '../lib/TierPolicy';
import { PageHeader } from '../components/ui/page-header'; import { PageHeader } from '../components/ui/page-header';
import { Card, CardContent } from '../components/ui/card'; import { Card, CardContent } from '../components/ui/card';
import { Button } from '../components/ui/button'; import { Button } from '../components/ui/button';
import { Badge } from '../components/ui/Primitives';
const PlanCard: React.FC<{ const PlanCard: React.FC<{
id: string; id: string;
@ -66,9 +67,9 @@ const PlanCard: React.FC<{
</div> </div>
</div> </div>
{isElite && ( {isElite && (
<span className="stat-chip" data-tone="success"> <Badge variant="success">
Peak Peak
</span> </Badge>
)} )}
</div> </div>
@ -77,10 +78,10 @@ const PlanCard: React.FC<{
<h3 style={{ fontSize: 42, fontWeight: 950, color: 'var(--foreground)', letterSpacing: '-0.04em', margin: 0 }}>{price}</h3> <h3 style={{ fontSize: 42, fontWeight: 950, color: 'var(--foreground)', letterSpacing: '-0.04em', margin: 0 }}>{price}</h3>
<span style={{ fontSize: 14, color: 'var(--muted-foreground)', fontWeight: 900, textTransform: 'uppercase' }}>/mo</span> <span style={{ fontSize: 14, color: 'var(--muted-foreground)', fontWeight: 900, textTransform: 'uppercase' }}>/mo</span>
</div> </div>
<div className="stat-chip"> <Badge variant="neutral">
<Sparkles size={14} style={{ color: 'var(--bl-warning)' }} /> <Sparkles size={14} style={{ color: 'var(--bl-warning)' }} />
Professional grade DNA Professional grade DNA
</div> </Badge>
</div> </div>
<p style={{ <p style={{
@ -163,9 +164,9 @@ export const MembershipTab: React.FC = () => {
/> />
<div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 28 }}> <div style={{ display: 'flex', gap: 12, flexWrap: 'wrap', marginBottom: 28 }}>
<span className="stat-chip">3 tiers</span> <Badge variant="neutral">3 tiers</Badge>
<span className="stat-chip">Preview pricing</span> <Badge variant="neutral">Preview pricing</Badge>
<span className="stat-chip">Product direction only</span> <Badge variant="neutral">Product direction only</Badge>
</div> </div>
<div style={{ <div style={{

View File

@ -8,7 +8,7 @@ import { tradingRuntime } from '../lib/runtime';
import { createRequestId } from '../../../shared/request-id.js'; import { createRequestId } from '../../../shared/request-id.js';
import { SkeletonBlock } from '../components/Skeleton'; import { SkeletonBlock } from '../components/Skeleton';
import { PageHeader } from '../components/ui/page-header'; import { PageHeader } from '../components/ui/page-header';
import { Button, Input, Select } from '../components/ui/Primitives'; import { Button, Input, Select, Badge } from '../components/ui/Primitives';
import { Card, CardContent } from '../components/ui/card'; import { Card, CardContent } from '../components/ui/card';
// ─── Types ──────────────────────────────────────────────────────────────────── // ─── Types ────────────────────────────────────────────────────────────────────
@ -200,16 +200,14 @@ export function ScreenerView() {
<div className="screener-sector-row"> <div className="screener-sector-row">
<SlidersHorizontal size={13} color="var(--muted-foreground)" /> <SlidersHorizontal size={13} color="var(--muted-foreground)" />
{SECTORS.slice(0, 6).map(s => ( {SECTORS.slice(0, 6).map(s => (
<Button <Badge
key={s} key={s}
variant={sector === s ? 'info' : 'neutral'}
className="cursor-pointer"
onClick={() => setSector(s)} onClick={() => setSector(s)}
variant={sector === s ? 'secondary' : 'outline'}
size="sm"
className="screener-sector-chip"
data-active={sector === s}
> >
{s} {s}
</Button> </Badge>
))} ))}
<Select <Select
aria-label="More sectors" aria-label="More sectors"

View File

@ -14,7 +14,7 @@ import {
import { fetchTradeProfiles, type TradeProfilePayload } from '../lib/profileApi'; import { fetchTradeProfiles, type TradeProfilePayload } from '../lib/profileApi';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../components/ui/card';
import { PageHeader } from '../components/ui/page-header'; import { PageHeader } from '../components/ui/page-header';
import { Button, Input, Select } from '../components/ui/Primitives'; import { Button, Input, Select, Badge } from '../components/ui/Primitives';
import { import {
DEFAULT_TRADE_PLANS_UI_STATE, DEFAULT_TRADE_PLANS_UI_STATE,
reduceTradePlansUiState, reduceTradePlansUiState,
@ -1515,30 +1515,18 @@ export function SimpleView() {
</span> </span>
) : null} ) : null}
{(runtimeSnapshot?.tradeId || entry.linked_trade_id) ? ( {(runtimeSnapshot?.tradeId || entry.linked_trade_id) ? (
<Button <Badge variant="neutral" className="cursor-pointer" onClick={() => void copyIdentifier('trade', String(runtimeSnapshot?.tradeId || entry.linked_trade_id))}>
type="button"
variant="subtle"
size="sm"
onClick={() => void copyIdentifier('trade', String(runtimeSnapshot?.tradeId || entry.linked_trade_id))}
className="saved-setup-id-chip"
>
{copiedKey === `trade:${String(runtimeSnapshot?.tradeId || entry.linked_trade_id)}` {copiedKey === `trade:${String(runtimeSnapshot?.tradeId || entry.linked_trade_id)}`
? 'Trade copied' ? 'Trade copied'
: `Trade ${String(runtimeSnapshot?.tradeId || entry.linked_trade_id).slice(0, 18)}`} : `Trade ${String(runtimeSnapshot?.tradeId || entry.linked_trade_id).slice(0, 18)}`}
</Button> </Badge>
) : null} ) : null}
{runtimeSnapshot?.orderId ? ( {runtimeSnapshot?.orderId ? (
<Button <Badge variant="neutral" className="cursor-pointer" onClick={() => void copyIdentifier('order', runtimeSnapshot.orderId)}>
type="button"
variant="subtle"
size="sm"
onClick={() => void copyIdentifier('order', runtimeSnapshot.orderId)}
className="saved-setup-id-chip"
>
{copiedKey === `order:${runtimeSnapshot.orderId}` {copiedKey === `order:${runtimeSnapshot.orderId}`
? 'Order copied' ? 'Order copied'
: `Order ${runtimeSnapshot.orderId.slice(0, 12)}`} : `Order ${runtimeSnapshot.orderId.slice(0, 12)}`}
</Button> </Badge>
) : null} ) : null}
{updatedAt ? ( {updatedAt ? (
<span className="saved-setup-updated"> <span className="saved-setup-updated">