fix(web): add title= to truncated text spans (UI audit #1)

Adds hover-tooltip affordance for ellipsis-truncated values across:
- EntriesTab: symbol, stock_instance_id
- AdminTab: env keys/values, bot config keys/values (refactored second
  block from arrow-fn to block-body to scope displayValue)
- HistoryTab: trade reason
- MarketOpportunities: symbol (both top and bottom lists)
- TradeProfileManager: profile name, profile email

Addresses Pattern E in docs/ui/UI_AUDIT.md.

Generated with [Devin](https://cli.devin.ai/docs)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
EOF
This commit is contained in:
Devin 2026-05-10 08:40:32 +00:00
parent 9df8a255ce
commit 1a5172c69a
5 changed files with 20 additions and 17 deletions

View File

@ -29,7 +29,7 @@ export const TopVolatile = ({ botState }: MarketOpportunitiesProps) => {
{topSymbols
.map(symbol => (
<div key={symbol} className="flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[var(--bl-border-subtle)] bg-[var(--bl-surface-muted)] px-3 py-2">
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]">{symbol}</span>
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]" title={symbol}>{symbol}</span>
<Badge variant={botState.symbols[symbol].change24h >= 0 ? 'success' : 'danger'} size="sm">
{botState.symbols[symbol].change24h >= 0 ? '+' : ''}{botState.symbols[symbol].change24h?.toFixed(2)}%
</Badge>
@ -71,7 +71,7 @@ export const AISetups = ({ botState }: MarketOpportunitiesProps) => {
{aiSymbols
.map(symbol => (
<div key={symbol} className="flex min-w-0 items-center justify-between gap-3 rounded-lg border border-[var(--bl-border-subtle)] bg-[var(--bl-surface-muted)] px-3 py-2">
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]">{symbol}</span>
<span className="min-w-0 truncate font-mono text-sm font-semibold text-[var(--bl-text-primary)]" title={symbol}>{symbol}</span>
<Badge variant="accent" size="sm">
{botState.symbols[symbol].rules['AIAnalysisRule']?.metadata?.confidence}% Conf
</Badge>

View File

@ -666,7 +666,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
<div className="px-5 pt-5 pb-4">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0 flex-1">
<h3 className="mb-2 truncate text-[15px] font-bold text-[var(--foreground)]">{p.name}</h3>
<h3 className="mb-2 truncate text-[15px] font-bold text-[var(--foreground)]" title={p.name}>{p.name}</h3>
<div className="flex items-center gap-2">
<span className={`inline-flex items-center gap-1.5 px-2.5 py-1 rounded-lg text-[9px] font-bold uppercase tracking-wider border ${p.is_active
? 'border-emerald-500/25 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400'
@ -676,7 +676,7 @@ export const TradeProfileManager = ({ botState = DEFAULT_BOT_STATE }: TradeProfi
{p.is_active ? 'Active' : 'Paused'}
</span>
{email && (
<span className="max-w-[160px] truncate text-[10px] text-[var(--muted-foreground)]">{email}</span>
<span className="max-w-[160px] truncate text-[10px] text-[var(--muted-foreground)]" title={email}>{email}</span>
)}
</div>
</div>

View File

@ -562,8 +562,8 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
.filter(([key]) => key.startsWith('VITE_') || key === 'MODE')
.map(([key, value]) => (
<div key={key} className={`flex items-center justify-between gap-4 px-4 py-3 ${adminPanelClass} rounded-xl`}>
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider truncate">{key}</span>
<span className="text-[10px] font-mono text-blue-400/80 bg-blue-500/[0.06] px-2.5 py-1 rounded-lg truncate max-w-[260px]">
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider truncate" title={key}>{key}</span>
<span className="text-[10px] font-mono text-blue-400/80 bg-blue-500/[0.06] px-2.5 py-1 rounded-lg truncate max-w-[260px]" title={String(value)}>
{String(value)}
</span>
</div>
@ -579,14 +579,17 @@ export const AdminTab = ({ botState, socket }: AdminTabProps) => {
</div>
{botConfig ? (
<div className="grid grid-cols-1 md:grid-cols-2 gap-2">
{Object.entries(botConfig).map(([key, value]) => (
<div key={key} className={`flex items-center justify-between gap-4 px-4 py-3 ${adminPanelClass} rounded-xl`}>
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider truncate">{key}</span>
<span className="text-[10px] font-mono text-orange-400/80 bg-orange-500/[0.06] px-2.5 py-1 rounded-lg truncate max-w-[260px]">
{Array.isArray(value) ? value.join(', ') : String(value)}
</span>
</div>
))}
{Object.entries(botConfig).map(([key, value]) => {
const displayValue = Array.isArray(value) ? value.join(', ') : String(value);
return (
<div key={key} className={`flex items-center justify-between gap-4 px-4 py-3 ${adminPanelClass} rounded-xl`}>
<span className="text-[10px] font-bold text-zinc-500 uppercase tracking-wider truncate" title={key}>{key}</span>
<span className="text-[10px] font-mono text-orange-400/80 bg-orange-500/[0.06] px-2.5 py-1 rounded-lg truncate max-w-[260px]" title={displayValue}>
{displayValue}
</span>
</div>
);
})}
</div>
) : (
<div className={`flex items-center justify-center gap-3 py-12 ${adminPanelClass} border-dashed border-red-500/10 rounded-xl`}>

View File

@ -165,7 +165,7 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
<article key={entry.stock_instance_id} className="entry-card group">
<div className="mb-5 flex items-start justify-between gap-4">
<div className="min-w-0 space-y-2">
<h4 className="m-0 truncate text-xl font-semibold tracking-tight text-[var(--foreground)]">{entry.symbol}</h4>
<h4 className="m-0 truncate text-xl font-semibold tracking-tight text-[var(--foreground)]" title={entry.symbol}>{entry.symbol}</h4>
<div className="flex items-center gap-2">
<span className={entryModeClass(entry.is_real_trade)}>
{entry.is_real_trade ? 'Real' : 'Paper'}
@ -220,7 +220,7 @@ export const EntriesTab = ({ botState = DEFAULT_BOT_STATE }: EntriesTabProps) =>
</div>
<div className="mt-5 flex items-center justify-between gap-3 border-t border-[var(--border)] pt-4">
<span className="truncate font-mono text-[11px] text-[var(--muted-foreground)]">{entry.stock_instance_id}</span>
<span className="truncate font-mono text-[11px] text-[var(--muted-foreground)]" title={entry.stock_instance_id}>{entry.stock_instance_id}</span>
<ShieldCheck size={15} className="shrink-0 text-[var(--muted-foreground)]" />
</div>
</article>

View File

@ -643,7 +643,7 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => {
</div>
</DataTableCell>
<DataTableCell>
<span className="inline-block max-w-[150px] truncate text-xs font-semibold text-[var(--bl-text-secondary)]">
<span className="inline-block max-w-[150px] truncate text-xs font-semibold text-[var(--bl-text-secondary)]" title={t.reason}>
{t.reason}
</span>
</DataTableCell>