- 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 <[email protected]>
167 lines
6.5 KiB
TypeScript
167 lines
6.5 KiB
TypeScript
import { useState } from 'react';
|
|
import {
|
|
Search, Bell, ChevronDown, Play, FlaskConical, ShieldCheck, Zap,
|
|
Command, User, LogOut, Settings, Shield
|
|
} from 'lucide-react';
|
|
import type { SessionMode } from '../types';
|
|
import { sampleNotifications } from '../data/sampleData';
|
|
|
|
const modeColors: Record<SessionMode, string> = {
|
|
Sandbox: '#eab308',
|
|
Simulate: '#3b82f6',
|
|
Live: '#22c55e',
|
|
'Compliance Review': '#a855f7',
|
|
};
|
|
|
|
interface TitleBarProps {
|
|
mode: SessionMode;
|
|
onModeChange: (mode: SessionMode) => void;
|
|
onToggleCommandPalette: () => void;
|
|
onValidate: () => void;
|
|
onSimulate: () => void;
|
|
onExecute: () => void;
|
|
}
|
|
|
|
export default function TitleBar({ mode, onModeChange, onToggleCommandPalette, onValidate, onSimulate, onExecute }: TitleBarProps) {
|
|
const [showModeMenu, setShowModeMenu] = useState(false);
|
|
const [showNotifications, setShowNotifications] = useState(false);
|
|
const [showUserMenu, setShowUserMenu] = useState(false);
|
|
const [notifications, setNotifications] = useState(sampleNotifications);
|
|
|
|
const modes: SessionMode[] = ['Sandbox', 'Simulate', 'Live', 'Compliance Review'];
|
|
const unreadCount = notifications.filter(n => !n.read).length;
|
|
|
|
const markAllRead = () => {
|
|
setNotifications(prev => prev.map(n => ({ ...n, read: true })));
|
|
};
|
|
|
|
const notifTypeColors: Record<string, string> = {
|
|
info: '#3b82f6',
|
|
success: '#22c55e',
|
|
warning: '#eab308',
|
|
error: '#ef4444',
|
|
};
|
|
|
|
return (
|
|
<div className="title-bar">
|
|
<div className="title-bar-left">
|
|
<div className="title-bar-logo">
|
|
<Zap size={18} color="#3b82f6" />
|
|
<span className="title-bar-name">TransactFlow</span>
|
|
</div>
|
|
<div className="title-bar-separator" />
|
|
<span className="title-bar-workspace">Institutional Workspace</span>
|
|
<div className="title-bar-separator" />
|
|
<div className="mode-selector" onClick={() => setShowModeMenu(!showModeMenu)}>
|
|
<div className="mode-dot" style={{ background: modeColors[mode] }} />
|
|
<span>{mode}</span>
|
|
<ChevronDown size={12} />
|
|
{showModeMenu && (
|
|
<div className="mode-dropdown">
|
|
{modes.map(m => (
|
|
<div
|
|
key={m}
|
|
className={`mode-option ${m === mode ? 'active' : ''}`}
|
|
onClick={(e) => { e.stopPropagation(); onModeChange(m); setShowModeMenu(false); }}
|
|
>
|
|
<div className="mode-dot" style={{ background: modeColors[m] }} />
|
|
{m}
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="title-bar-center">
|
|
<button className="title-bar-search" onClick={onToggleCommandPalette}>
|
|
<Search size={13} />
|
|
<span>Search or run command...</span>
|
|
<kbd>Ctrl+K</kbd>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="title-bar-right">
|
|
<button className="title-bar-action validate" title="Validate (Ctrl+Shift+V)" onClick={onValidate}>
|
|
<ShieldCheck size={15} />
|
|
<span>Validate</span>
|
|
</button>
|
|
<button className="title-bar-action simulate" title="Simulate (Ctrl+Shift+S)" onClick={onSimulate}>
|
|
<FlaskConical size={15} />
|
|
<span>Simulate</span>
|
|
</button>
|
|
<button className="title-bar-action execute" title="Execute (Ctrl+Shift+E)" onClick={onExecute}>
|
|
<Play size={15} />
|
|
<span>Execute</span>
|
|
</button>
|
|
<div className="title-bar-separator" />
|
|
|
|
{/* Notification bell with dropdown */}
|
|
<div className="notification-wrapper">
|
|
<button className="icon-btn" title="Notifications" onClick={() => { setShowNotifications(!showNotifications); setShowUserMenu(false); }}>
|
|
<Bell size={16} />
|
|
{unreadCount > 0 && <span className="notification-badge">{unreadCount}</span>}
|
|
</button>
|
|
{showNotifications && (
|
|
<div className="notification-dropdown">
|
|
<div className="notification-dropdown-header">
|
|
<span>Notifications</span>
|
|
{unreadCount > 0 && (
|
|
<button className="mark-read-btn" onClick={markAllRead}>Mark all read</button>
|
|
)}
|
|
</div>
|
|
{notifications.map(n => (
|
|
<div key={n.id} className={`notification-item ${n.read ? 'read' : ''}`} onClick={() => setNotifications(prev => prev.map(x => x.id === n.id ? { ...x, read: true } : x))}>
|
|
<div className="notification-dot" style={{ background: notifTypeColors[n.type] }} />
|
|
<div className="notification-content">
|
|
<span className="notification-title">{n.title}</span>
|
|
<span className="notification-message">{n.message}</span>
|
|
<span className="notification-time">{n.timestamp.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}</span>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<button className="icon-btn" title="Command Palette" onClick={onToggleCommandPalette}>
|
|
<Command size={16} />
|
|
</button>
|
|
|
|
{/* User menu with dropdown */}
|
|
<div className="user-menu-wrapper">
|
|
<button className="icon-btn user-btn" title="User Settings" onClick={() => { setShowUserMenu(!showUserMenu); setShowNotifications(false); }}>
|
|
<User size={16} />
|
|
</button>
|
|
{showUserMenu && (
|
|
<div className="user-menu-dropdown">
|
|
<div className="user-menu-profile">
|
|
<div className="user-avatar">JD</div>
|
|
<div>
|
|
<div className="user-name">Jane Doe</div>
|
|
<div className="user-role">Admin · Compliance Officer</div>
|
|
</div>
|
|
</div>
|
|
<div className="user-menu-divider" />
|
|
<div className="user-menu-item">
|
|
<User size={13} /> <span>Profile</span>
|
|
</div>
|
|
<div className="user-menu-item">
|
|
<Settings size={13} /> <span>Settings</span>
|
|
</div>
|
|
<div className="user-menu-item">
|
|
<Shield size={13} /> <span>Permissions</span>
|
|
<span className="user-menu-badge">Admin</span>
|
|
</div>
|
|
<div className="user-menu-divider" />
|
|
<div className="user-menu-item logout">
|
|
<LogOut size={13} /> <span>Sign Out</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|