refactor(ui): unify operational alert banners

This commit is contained in:
Saravana Achu Mac 2026-05-09 02:15:33 -07:00
parent 3951767ab1
commit d4846b73cc
2 changed files with 39 additions and 60 deletions

View File

@ -7,7 +7,7 @@ import {
import { useCanonicalLifecycle } from '../hooks/useCanonicalLifecycle';
import { fetchTradeHistory } from '../lib/tradeHistoryApi';
import { fetchPositionsBootstrap } from '../lib/positionsApi';
import { Badge, Button, Input, Select } from '../components/ui/Primitives';
import { AlertBanner, Badge, Button, Input, Select } from '../components/ui/Primitives';
interface TradeRecord {
@ -438,15 +438,15 @@ export const HistoryTab = ({ botState }: HistoryTabProps) => {
</div>
{!hasCanonicalLifecycle && (
<div className="rounded-xl border border-amber-500/30 bg-amber-500/10 text-amber-300 text-xs px-3 py-2">
Canonical lifecycle is unavailable{canonicalLoading ? ' (loading)' : ''}. History is using fallback ledger sources.
<AlertBanner tone="warning" title="Canonical lifecycle unavailable">
History is using fallback ledger sources{canonicalLoading ? ' while loading' : ''}.
{canonicalError ? ` ${canonicalError}` : ''}
</div>
</AlertBanner>
)}
{canonicalSnapshot?.diagnostics?.truncated && (
<div className="rounded-xl border border-red-500/30 bg-red-500/10 text-red-300 text-xs px-3 py-2">
Canonical lifecycle snapshot is truncated ({canonicalSnapshot.diagnostics.orderRows} rows). Review a narrower scope for exact audit totals.
</div>
<AlertBanner tone="error" title="Lifecycle snapshot truncated">
{canonicalSnapshot.diagnostics.orderRows} rows were returned. Review a narrower scope for exact audit totals.
</AlertBanner>
)}
{/* Performance Metrics Bar */}

View File

@ -8,7 +8,7 @@ import { createRequestId } from '../../../shared/request-id.js';
import { Layers, ListFilter, Link2, GitBranch, AlertTriangle, Lock, RefreshCw, CheckCircle, XCircle } from 'lucide-react';
import { useCanonicalLifecycle } from '../hooks/useCanonicalLifecycle';
import { fetchPositionsBootstrap } from '../lib/positionsApi';
import { Badge, Button, Input, Select } from '../components/ui/Primitives';
import { AlertBanner, Badge, Button, Input, Select } from '../components/ui/Primitives';
interface PositionsTabProps {
botState: BotState;
@ -1343,66 +1343,45 @@ export const PositionsTab = ({ botState, onManageHolding }: PositionsTabProps) =
</header>
{!hasCanonicalLifecycle && (
<div className="rounded-xl border border-amber-500/30 bg-amber-500/10 text-amber-300 text-xs px-3 py-2">
Canonical lifecycle is unavailable{canonicalLoading ? ' (loading)' : ''}. Truth labels are in fallback mode (DB/runtime-derived).
<AlertBanner tone="warning" title="Canonical lifecycle unavailable">
Truth labels are in fallback mode (DB/runtime-derived){canonicalLoading ? ' while loading' : ''}.
{canonicalError ? ` ${canonicalError}` : ''}
</div>
</AlertBanner>
)}
{canonicalSnapshot?.diagnostics?.truncated && (
<div className="rounded-xl border border-red-500/30 bg-red-500/10 text-red-300 text-xs px-3 py-2">
Canonical lifecycle snapshot is truncated ({canonicalSnapshot.diagnostics.orderRows} rows). Narrow profile scope before operational decisions.
</div>
<AlertBanner tone="error" title="Lifecycle snapshot truncated">
{canonicalSnapshot.diagnostics.orderRows} rows were returned. Narrow profile scope before operational decisions.
</AlertBanner>
)}
{/* Stale Orders Warning Banner */}
{staleWarningOrders.length > 0 && (
<div className="bg-yellow-500/10 border border-yellow-500/30 rounded-lg p-4 flex items-start gap-3">
<span className="text-yellow-400 text-xl"></span>
<div className="flex-1">
<h4 className="text-yellow-400 font-bold text-sm mb-1">
{staleWarningOrders.length} Stale Order{staleWarningOrders.length > 1 ? 's' : ''} Detected
</h4>
<p className="text-gray-400 text-xs">
Some orders have remained in <code className="bg-black/30 px-1 rounded">pending_new</code> for more than 5 minutes
without stronger fill or position evidence. The background sync service is re-checking their exchange status.
</p>
</div>
</div>
<AlertBanner tone="warning" title={`${staleWarningOrders.length} Stale Order${staleWarningOrders.length > 1 ? 's' : ''} Detected`}>
Some orders have remained in <code className="rounded bg-black/10 px-1">pending_new</code> for more than 5 minutes
without stronger fill or position evidence. The background sync service is re-checking their exchange status.
</AlertBanner>
)}
{positionMismatches.length > 0 && (
<AlertBanner tone="error" title={`Lifecycle Mismatch Diagnostics (${positionMismatches.length})`}>
<p className="mb-3">
One or more open positions do not have a clean entry-order lineage by profile and trade ID.
</p>
<div className="space-y-2">
{positionMismatches.map((issue) => (
<div key={issue.id} className="flex flex-wrap items-center gap-2 rounded-lg border border-[var(--border)] bg-[var(--card)] px-2 py-1 text-[10px]">
<Badge variant={issue.severity === 'critical' ? 'danger' : 'warning'} size="sm">
{issue.severity}
</Badge>
<span className="font-semibold text-[var(--foreground)]">{issue.profileName}</span>
<span className="font-mono text-[var(--foreground)]">{issue.symbol}</span>
<span className="font-mono text-[var(--muted-foreground)]">{issue.tradeId}</span>
<span className="text-[var(--muted-foreground)]">{issue.reason}</span>
</div>
))}
</div>
</AlertBanner>
)}
{positionMismatches.length > 0 && (
<div className="bg-red-500/10 border border-red-500/30 rounded-lg p-4">
<div className="flex items-start gap-3">
<AlertTriangle className="text-red-400 mt-0.5" size={18} />
<div className="flex-1 space-y-3">
<div>
<h4 className="text-red-400 font-bold text-sm">
Lifecycle Mismatch Diagnostics ({positionMismatches.length})
</h4>
<p className="text-gray-400 text-xs">
One or more open positions do not have a clean entry-order lineage by profile and trade ID.
</p>
</div>
<div className="space-y-2">
{positionMismatches.map((issue) => (
<div key={issue.id} className="flex flex-wrap items-center gap-2 text-[10px] border border-white/10 rounded px-2 py-1">
<span className={`px-1.5 py-0.5 rounded font-bold uppercase tracking-wider ${issue.severity === 'critical'
? 'bg-red-500/20 text-red-300 border border-red-500/20'
: 'bg-yellow-500/20 text-yellow-300 border border-yellow-500/20'
}`}>
{issue.severity}
</span>
<span className="text-gray-300 font-semibold">{issue.profileName}</span>
<span className="text-white font-mono">{issue.symbol}</span>
<span className="text-zinc-500 font-mono">{issue.tradeId}</span>
<span className="text-zinc-400">{issue.reason}</span>
</div>
))}
</div>
</div>
</div>
</div>
)}
<section className="positions-section">
<div className="flex items-center gap-2 mb-4">