PR G: portal /transactions page + 12-state machine view (#11)
Some checks failed
CI / Frontend Lint (push) Failing after 6s
CI / Frontend Type Check (push) Failing after 6s
CI / Frontend Build (push) Failing after 6s
CI / Frontend E2E Tests (push) Failing after 9s
CI / Orchestrator Build (push) Failing after 6s
CI / Contracts Compile (push) Failing after 6s
CI / Contracts Test (push) Failing after 6s
Security Scan / Dependency Vulnerability Scan (push) Failing after 4s
Security Scan / OWASP ZAP Scan (push) Failing after 4s

This commit was merged in pull request #11.
This commit is contained in:
2026-04-22 17:18:52 +00:00
parent 3ef71332dc
commit b66ec0a78f
7 changed files with 714 additions and 2 deletions

View File

@@ -4,12 +4,13 @@ import { useAuth } from '../../contexts/AuthContext';
import {
LayoutDashboard, Zap, Building2, Landmark, FileText, Shield, CheckSquare,
Settings, LogOut, ChevronLeft, ChevronRight, Bell, User, Copy,
ExternalLink, ChevronDown
ExternalLink, ChevronDown, GitBranch
} from 'lucide-react';
const navItems = [
{ id: 'dashboard', label: 'Overview', icon: LayoutDashboard, path: '/dashboard' },
{ id: 'transaction-builder', label: 'Transaction Builder', icon: Zap, path: '/transaction-builder' },
{ id: 'transactions', label: 'Transactions', icon: GitBranch, path: '/transactions' },
{ id: 'accounts', label: 'Accounts', icon: Building2, path: '/accounts' },
{ id: 'treasury', label: 'Treasury', icon: Landmark, path: '/treasury' },
{ id: 'reporting', label: 'Reporting', icon: FileText, path: '/reporting' },

View File

@@ -0,0 +1,51 @@
import { TRANSACTION_STATES, type StateTransition, type TransactionState } from '../../services/orchestrator';
interface StateMachineViewProps {
current: TransactionState;
transitions: StateTransition[];
}
/**
* Renders the 12-state transaction machine from the architecture note
* §8. Visited states are highlighted in the order they were entered;
* the current state is emphasised. Intended as an audit-friendly view
* for the /transactions page, NOT a full graph editor.
*/
export default function StateMachineView({ current, transitions }: StateMachineViewProps) {
const visited = new Set<string>(transitions.map((t) => t.to_state));
if (transitions.length > 0 && transitions[0].from_state === null) {
visited.add(transitions[0].to_state);
}
return (
<div className="state-machine-view">
<div className="state-machine-grid">
{TRANSACTION_STATES.map((state) => {
const isCurrent = state === current;
const isVisited = visited.has(state);
const isTerminal = state === 'COMMITTED' || state === 'ABORTED' || state === 'CLOSED';
const classes = [
'state-pill',
isCurrent ? 'state-pill--current' : '',
!isCurrent && isVisited ? 'state-pill--visited' : '',
!isVisited ? 'state-pill--pending' : '',
isTerminal ? 'state-pill--terminal' : '',
]
.filter(Boolean)
.join(' ');
return (
<div key={state} className={classes} data-testid={`state-${state}`}>
<span className="state-pill-dot" aria-hidden="true" />
<span className="state-pill-label">{state.replace(/_/g, ' ')}</span>
</div>
);
})}
</div>
<div className="state-machine-legend">
<span className="legend-item"><span className="dot dot--current" />current</span>
<span className="legend-item"><span className="dot dot--visited" />visited</span>
<span className="legend-item"><span className="dot dot--pending" />not yet reached</span>
</div>
</div>
);
}