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
52 lines
2.1 KiB
TypeScript
52 lines
2.1 KiB
TypeScript
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>
|
|
);
|
|
}
|