import { useEffect, useState } from 'react'; import { useNavigate, useParams } from 'react-router-dom'; import { listPlans, getPlanState, getPlanEvents, type PlanSummary, type PlanStateDetail, type PlanEvent } from '../services/orchestrator'; import StateMachineView from '../components/portal/StateMachineView'; import { endpoints } from '../config/endpoints'; type Source = 'live' | 'mocked' | 'degraded'; function SourceBadge({ source }: { source: Source }) { const label = source === 'live' ? 'LIVE' : source === 'degraded' ? 'DEGRADED' : 'DEMO'; return {label}; } export default function TransactionsPage() { const { planId } = useParams<{ planId?: string }>(); return planId ? : ; } function TransactionsList() { const navigate = useNavigate(); const [plans, setPlans] = useState(null); const [source, setSource] = useState('mocked'); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; setPlans(null); setError(null); listPlans() .then((res) => { if (cancelled) return; setPlans(res.plans); setSource(res.source); }) .catch((err) => { if (cancelled) return; setError(err instanceof Error ? err.message : String(err)); }); return () => { cancelled = true; }; }, []); return (

Transactions

Multi-layer atomic settlement plans. State machine per architecture note §8. {!endpoints.orchestrator.deployed && ( {' '}Orchestrator not deployed — showing demo plans. )}

Recent plans

{error &&
{error}
} {plans === null ? (
Loading…
) : plans.length === 0 ? (
No plans yet.
) : ( {plans.map((p) => ( navigate(`/transactions/${encodeURIComponent(p.plan_id)}`)} data-testid={`plan-row-${p.plan_id}`} > ))}
Plan ID State Instrument Owner Updated
{p.plan_id} {p.status.replace(/_/g, ' ')} {p.instrument_hint ?? '—'} {p.actor_id ?? '—'} {new Date(p.updated_at).toLocaleString()}
)}
); } function TransactionDetail({ planId }: { planId: string }) { const navigate = useNavigate(); const [detail, setDetail] = useState(null); const [events, setEvents] = useState(null); const [source, setSource] = useState('mocked'); const [error, setError] = useState(null); useEffect(() => { let cancelled = false; setDetail(null); setEvents(null); setError(null); Promise.all([getPlanState(planId), getPlanEvents(planId)]) .then(([s, e]) => { if (cancelled) return; setDetail(s.detail); setEvents(e.events); setSource(s.source === 'live' && e.source === 'live' ? 'live' : s.source); }) .catch((err) => { if (cancelled) return; setError(err instanceof Error ? err.message : String(err)); }); return () => { cancelled = true; }; }, [planId]); return (

Plan {planId}

{detail ? ( <>Current state: {detail.current_state.replace(/_/g, ' ')} ) : ( 'Loading plan state…' )}

{error &&
{error}
}

12-state machine

{detail ? ( ) : (
Loading…
)}

Audit trail

{detail === null ? (
Loading…
) : detail.transitions.length === 0 ? (
No transitions recorded.
) : ( {detail.transitions.map((t, i) => ( ))}
# From → To Actor Role Reason At
{i + 1} {t.from_state ?? '∅'} → {t.to_state} {t.actor_id} {t.actor_role} {t.reason ?? '—'} {new Date(t.occurred_at).toLocaleString()}
)}

Signed event stream

{events === null ? (
Loading…
) : events.length === 0 ? (
No events.
) : ( {events.map((e) => ( ))}
# Type Signature Prev hash At
{e.id} {e.type} {e.signature} {e.prev_hash ?? '∅'} {new Date(e.created_at).toLocaleString()}
)}
); }