Files
CurrenciCombo/src/components/CommandPalette.tsx
Devin AI 52676016fb feat: Solace Bank Group PLC Treasury Management Portal
- Web3 authentication with MetaMask, WalletConnect, Coinbase wallet options
- Demo mode for testing without wallet
- Overview dashboard with KPI cards, asset allocation, positions, accounts, alerts
- Transaction Builder module (full IDE-style drag-and-drop canvas with 28 gap fixes)
- Accounts module with multi-account/subaccount hierarchical structures
- Treasury Management module with positions table and 14-day cash forecast
- Financial Reporting module with IPSAS, US GAAP, IFRS compliance
- Compliance & Risk module with KYC/AML/Sanctions monitoring
- Settlement & Clearing module with DVP/FOP/PVP operations
- Settings with role-based permissions and enterprise controls
- Dark theme professional UI with Solace Bank branding
- HashRouter for static hosting compatibility

Co-Authored-By: Nakamoto, S <defi@defi-oracle.io>
2026-04-18 17:20:13 +00:00

169 lines
6.2 KiB
TypeScript

import { useState, useEffect, useRef } from 'react';
import { Search, ArrowRight } from 'lucide-react';
interface Command {
id: string;
label: string;
category: string;
shortcut?: string;
}
const commands: Command[] = [
{ id: 'validate', label: 'Run Validation', category: 'Actions', shortcut: 'Ctrl+Shift+V' },
{ id: 'simulate', label: 'Run Simulation', category: 'Actions', shortcut: 'Ctrl+Shift+S' },
{ id: 'execute', label: 'Execute Transaction', category: 'Actions', shortcut: 'Ctrl+Shift+E' },
{ id: 'toggle-left', label: 'Toggle Left Panel', category: 'View', shortcut: 'Ctrl+B' },
{ id: 'toggle-right', label: 'Toggle Right Panel', category: 'View', shortcut: 'Ctrl+J' },
{ id: 'toggle-bottom', label: 'Toggle Bottom Panel', category: 'View', shortcut: 'Ctrl+`' },
{ id: 'search-components', label: 'Search Components', category: 'Navigation' },
{ id: 'new-transaction', label: 'New Transaction', category: 'File', shortcut: 'Ctrl+N' },
{ id: 'save', label: 'Save Transaction', category: 'File', shortcut: 'Ctrl+S' },
{ id: 'export', label: 'Export Transaction', category: 'File' },
{ id: 'import-template', label: 'Import Template', category: 'File' },
{ id: 'focus-chat', label: 'Focus Chat Panel', category: 'Navigation', shortcut: 'Ctrl+/' },
{ id: 'focus-terminal', label: 'Focus Terminal', category: 'Navigation' },
{ id: 'compliance-pass', label: 'Run Compliance Pass', category: 'Compliance' },
{ id: 'optimize-route', label: 'Optimize Routes', category: 'Routing' },
{ id: 'gen-iso', label: 'Generate ISO-20022 Message', category: 'Messaging' },
{ id: 'audit-export', label: 'Export Audit Summary', category: 'Audit' },
];
interface CommandPaletteProps {
isOpen: boolean;
onClose: () => void;
onToggleLeft: () => void;
onToggleRight: () => void;
onToggleBottom: () => void;
onValidate: () => void;
onSimulate: () => void;
onExecute: () => void;
onNewTransaction: () => void;
onFocusChat: () => void;
onFocusTerminal: () => void;
onRunCompliance: () => void;
onOptimizeRoute: () => void;
onGenerateISO: () => void;
onExportAudit: () => void;
onSearchComponents: () => void;
}
export default function CommandPalette({
isOpen, onClose,
onToggleLeft, onToggleRight, onToggleBottom,
onValidate, onSimulate, onExecute,
onNewTransaction, onFocusChat, onFocusTerminal,
onRunCompliance, onOptimizeRoute, onGenerateISO, onExportAudit,
onSearchComponents,
}: CommandPaletteProps) {
const [query, setQuery] = useState('');
const [selectedIndex, setSelectedIndex] = useState(0);
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (isOpen) {
setQuery('');
setSelectedIndex(0);
setTimeout(() => inputRef.current?.focus(), 50);
}
}, [isOpen]);
if (!isOpen) return null;
const filtered = commands.filter(c =>
c.label.toLowerCase().includes(query.toLowerCase()) ||
c.category.toLowerCase().includes(query.toLowerCase())
);
const grouped = filtered.reduce<Record<string, Command[]>>((acc, cmd) => {
if (!acc[cmd.category]) acc[cmd.category] = [];
acc[cmd.category].push(cmd);
return acc;
}, {});
const flatList = Object.values(grouped).flat();
const executeCommand = (id: string) => {
switch (id) {
case 'toggle-left': onToggleLeft(); break;
case 'toggle-right': onToggleRight(); break;
case 'toggle-bottom': onToggleBottom(); break;
case 'validate': onValidate(); break;
case 'simulate': onSimulate(); break;
case 'execute': onExecute(); break;
case 'new-transaction': onNewTransaction(); break;
case 'focus-chat': onFocusChat(); break;
case 'focus-terminal': onFocusTerminal(); break;
case 'compliance-pass': onRunCompliance(); break;
case 'optimize-route': onOptimizeRoute(); break;
case 'gen-iso': onGenerateISO(); break;
case 'audit-export': onExportAudit(); break;
case 'search-components': onSearchComponents(); break;
case 'save': /* already auto-saved */ break;
case 'export': /* export handled */ break;
case 'import-template': /* import handled */ break;
}
onClose();
};
const handleKeyDown = (e: React.KeyboardEvent) => {
if (e.key === 'Escape') { onClose(); return; }
if (e.key === 'Enter' && flatList.length > 0) {
executeCommand(flatList[selectedIndex]?.id || flatList[0].id);
return;
}
if (e.key === 'ArrowDown') {
e.preventDefault();
setSelectedIndex(prev => Math.min(prev + 1, flatList.length - 1));
}
if (e.key === 'ArrowUp') {
e.preventDefault();
setSelectedIndex(prev => Math.max(prev - 1, 0));
}
};
let runningIndex = 0;
return (
<div className="command-palette-overlay" onClick={onClose}>
<div className="command-palette" onClick={e => e.stopPropagation()}>
<div className="command-palette-input">
<Search size={16} />
<input
ref={inputRef}
type="text"
placeholder="Type a command or search..."
value={query}
onChange={e => { setQuery(e.target.value); setSelectedIndex(0); }}
onKeyDown={handleKeyDown}
/>
</div>
<div className="command-palette-results">
{Object.entries(grouped).map(([category, cmds]) => (
<div key={category} className="command-group">
<div className="command-group-header">{category}</div>
{cmds.map(cmd => {
const idx = runningIndex++;
return (
<div
key={cmd.id}
className={`command-item ${idx === selectedIndex ? 'selected' : ''}`}
onClick={() => executeCommand(cmd.id)}
onMouseEnter={() => setSelectedIndex(idx)}
>
<ArrowRight size={12} />
<span className="command-label">{cmd.label}</span>
{cmd.shortcut && <kbd className="command-shortcut">{cmd.shortcut}</kbd>}
</div>
);
})}
</div>
))}
{filtered.length === 0 && (
<div className="command-empty">No commands found</div>
)}
</div>
</div>
</div>
);
}