admin08077 commited on
Commit
b8b3edf
·
verified ·
1 Parent(s): 2557876

Upload 26 files

Browse files
views/Advisor.tsx ADDED
@@ -0,0 +1,139 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import {
4
+ Send, User, Bot, Sparkles, Loader2, Volume2, VolumeX, ShieldCheck, Database, RefreshCw, ChevronRight
5
+ } from 'lucide-react';
6
+ import { speakText, getAudioContext, getFinancialAdviceStream } from '../services/geminiService';
7
+ import { apiClient } from '../services/api';
8
+
9
+ interface Message {
10
+ id: string;
11
+ role: 'user' | 'assistant';
12
+ content: string;
13
+ timestamp: string;
14
+ isStreaming?: boolean;
15
+ }
16
+
17
+ const Advisor: React.FC = () => {
18
+ const [messages, setMessages] = useState<Message[]>([]);
19
+ const [input, setInput] = useState('');
20
+ const [loading, setLoading] = useState(false);
21
+ const [voiceEnabled, setVoiceEnabled] = useState(false);
22
+ const scrollRef = useRef<HTMLDivElement>(null);
23
+
24
+ useEffect(() => {
25
+ const initMemory = async () => {
26
+ const history = await apiClient.chat.getHistory();
27
+ if (history.length > 0) {
28
+ setMessages(history.map((h: any) => ({ ...h, id: h.id.toString() })));
29
+ } else {
30
+ setMessages([{ id: '1', role: 'assistant', content: "Neural Core online. Standing by for institutional instructions.", timestamp: new Date().toISOString() }]);
31
+ }
32
+ };
33
+ initMemory();
34
+ }, []);
35
+
36
+ useEffect(() => {
37
+ if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
38
+ }, [messages]);
39
+
40
+ const handleSend = async (forcedInput?: string) => {
41
+ const query = (forcedInput || input).trim();
42
+ if (!query || loading) return;
43
+
44
+ setMessages(prev => [...prev, { id: Date.now().toString(), role: 'user', content: query, timestamp: new Date().toISOString() }]);
45
+ setInput('');
46
+ setLoading(true);
47
+
48
+ const assistantMsgId = (Date.now() + 1).toString();
49
+ setMessages(prev => [...prev, { id: assistantMsgId, role: 'assistant', content: '', timestamp: new Date().toISOString(), isStreaming: true }]);
50
+
51
+ try {
52
+ const context = { system: "LUMINA_ADVISORY_NODE" };
53
+ const stream = await getFinancialAdviceStream(query, context);
54
+
55
+ let fullContent = '';
56
+ for await (const chunk of stream) {
57
+ fullContent += chunk.text || '';
58
+ setMessages(prev => prev.map(m => m.id === assistantMsgId ? { ...m, content: fullContent } : m));
59
+ }
60
+
61
+ setMessages(prev => prev.map(m => m.id === assistantMsgId ? { ...m, isStreaming: false } : m));
62
+ await apiClient.chat.saveMessage('user', query);
63
+ await apiClient.chat.saveMessage('assistant', fullContent);
64
+
65
+ if (voiceEnabled) speakText(fullContent);
66
+ } catch (error) {
67
+ setMessages(prev => prev.map(m => m.id === assistantMsgId ? { ...m, content: "Neural signal lost. Parity check failed.", isStreaming: false } : m));
68
+ } finally {
69
+ setLoading(false);
70
+ }
71
+ };
72
+
73
+ return (
74
+ <div className="flex h-[calc(100vh-140px)] gap-10 animate-in fade-in duration-700">
75
+ <div className="hidden lg:flex flex-col w-96 space-y-8">
76
+ <div className="bg-zinc-950 border border-zinc-800 rounded-[3rem] p-10 flex flex-col h-full relative overflow-hidden shadow-2xl">
77
+ <div className="flex items-center space-x-3 mb-10 relative z-10">
78
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-2xl border border-blue-500/20">
79
+ <Sparkles size={24} />
80
+ </div>
81
+ <div>
82
+ <h3 className="text-white font-black italic tracking-tighter uppercase text-xl">Nexus_Core</h3>
83
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">Memory Protocol: Local</p>
84
+ </div>
85
+ </div>
86
+ <div className="space-y-4 flex-1 overflow-y-auto pr-3 custom-scrollbar relative z-10">
87
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest px-2 mb-4">Command Presets</p>
88
+ {["Analyze Q2 Liquidity Drift", "Portfolio Resilience Stress-Test", "Explain FDX v6.2 Handshakes"].map((text, i) => (
89
+ <button key={i} onClick={() => handleSend(text)} disabled={loading} className="w-full text-left p-6 rounded-2xl bg-black border border-zinc-900 hover:border-blue-500/50 hover:bg-blue-500/5 transition-all group flex items-start justify-between disabled:opacity-30">
90
+ <span className="text-[10px] font-black uppercase tracking-widest text-zinc-500 group-hover:text-blue-400 leading-relaxed">{text}</span>
91
+ <ChevronRight size={16} className="text-zinc-800 group-hover:text-blue-500 transition-colors" />
92
+ </button>
93
+ ))}
94
+ </div>
95
+ </div>
96
+ </div>
97
+
98
+ <div className="flex-1 flex flex-col bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl relative">
99
+ <div className="h-20 border-b border-zinc-900 px-10 flex items-center justify-between bg-black/40 backdrop-blur-xl z-20">
100
+ <div className="flex items-center space-x-6">
101
+ <div className={`w-3 h-3 rounded-full shadow-[0_0_15px_#10b981] ${loading ? 'bg-amber-500' : 'bg-emerald-500'}`}></div>
102
+ <span className="font-black text-white text-sm uppercase tracking-[0.4em] italic">Quantum_Advisory_Node</span>
103
+ </div>
104
+ <button onClick={() => setVoiceEnabled(!voiceEnabled)} className={`p-4 rounded-2xl border transition-all ${voiceEnabled ? 'bg-blue-600/10 border-blue-500/20 text-blue-500' : 'bg-zinc-900 border-zinc-800 text-zinc-600'}`}>
105
+ {voiceEnabled ? <Volume2 size={20} /> : <VolumeX size={20} />}
106
+ </button>
107
+ </div>
108
+
109
+ <div ref={scrollRef} className="flex-1 overflow-y-auto p-12 space-y-12 custom-scrollbar">
110
+ {messages.map((m) => (
111
+ <div key={m.id} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'} animate-in fade-in slide-in-from-bottom-4`}>
112
+ <div className={`flex max-w-[85%] ${m.role === 'user' ? 'flex-row-reverse' : 'flex-row'} items-start gap-8`}>
113
+ <div className={`w-14 h-14 rounded-2xl flex-shrink-0 flex items-center justify-center border transition-all ${m.role === 'user' ? 'bg-zinc-900 border-zinc-800 text-zinc-500' : 'bg-blue-600 border-blue-400 text-white shadow-2xl shadow-blue-900/40'}`}>
114
+ {m.role === 'user' ? <User size={24} /> : <Bot size={28} />}
115
+ </div>
116
+ <div className={`p-8 rounded-[2.5rem] ${m.role === 'user' ? 'bg-zinc-900 text-zinc-100 rounded-tr-none border border-zinc-800' : 'bg-black border border-zinc-800 text-zinc-300 rounded-tl-none shadow-2xl'}`}>
117
+ <div className="text-base font-medium leading-relaxed tracking-tight whitespace-pre-wrap">
118
+ {m.content || (m.isStreaming ? "Synthesizing Alpha..." : "Establishing link...")}
119
+ </div>
120
+ </div>
121
+ </div>
122
+ </div>
123
+ ))}
124
+ </div>
125
+
126
+ <div className="p-10 bg-black/20 border-t border-zinc-900 backdrop-blur-2xl z-20">
127
+ <div className="relative group max-w-5xl mx-auto flex gap-4">
128
+ <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && handleSend()} placeholder="Input instructions for the LQI Core..." className="flex-1 bg-black border border-zinc-800 focus:border-blue-500 rounded-[2rem] py-7 px-10 text-white text-base outline-none transition-all placeholder:text-zinc-800 font-bold" />
129
+ <button onClick={() => handleSend()} disabled={loading || !input.trim()} className="px-8 bg-blue-600 hover:bg-blue-500 text-white rounded-[1.5rem] font-black uppercase flex items-center gap-3">
130
+ {loading ? <Loader2 className="animate-spin" /> : <Send size={20} />}
131
+ </button>
132
+ </div>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ );
137
+ };
138
+
139
+ export default Advisor;
views/Airdrop.tsx ADDED
@@ -0,0 +1,262 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import { Gift, Wallet, Loader2, CheckCircle2, ShieldCheck, Zap, ArrowRight, AlertTriangle, Mail, Send, X } from 'lucide-react';
4
+
5
+ const Airdrop: React.FC = () => {
6
+ const [walletConnected, setWalletConnected] = useState(false);
7
+ const [claiming, setClaiming] = useState(false);
8
+ const [claimed, setClaimed] = useState(localStorage.getItem('jocall3_claimed') === 'true');
9
+ const [step, setStep] = useState<'idle' | 'connect' | 'gas' | 'success' | 'email'>('idle');
10
+
11
+ // Email Form State
12
+ const [emailForm, setEmailForm] = useState({
13
+ name: '',
14
+ email: '',
15
+ message: '',
16
+ involvement: ''
17
+ });
18
+ const [sendingEmail, setSendingEmail] = useState(false);
19
+ const [emailSent, setEmailSent] = useState(false);
20
+
21
+ const handleConnect = () => {
22
+ setStep('connect');
23
+ setTimeout(() => {
24
+ setWalletConnected(true);
25
+ setStep('gas');
26
+ }, 1500);
27
+ };
28
+
29
+ const handleClaim = () => {
30
+ if (claimed) return;
31
+ setClaiming(true);
32
+ setTimeout(() => {
33
+ setClaiming(false);
34
+ setClaimed(true);
35
+ setStep('success');
36
+ localStorage.setItem('jocall3_claimed', 'true');
37
+ }, 2500);
38
+ };
39
+
40
+ const handleSendEmail = async (e: React.FormEvent) => {
41
+ e.preventDefault();
42
+ setSendingEmail(true);
43
+
44
+ const payload = {
45
+ websiteId: "4e8562c9-daa3-4b25-b476-83cdb385c9c1",
46
+ widgetId: "05e78cec-eb73-4f6e-b24f-af30a379a14a",
47
+ pageId: "197c6e99-b23a-4e05-8614-d0b9bc9f268a",
48
+ accountId: "059b1ee8-e1e0-11f0-86ca-7cd30acd4558",
49
+ domainName: "citibankdemobusiness.dev",
50
+ optedToSubscribe: false,
51
+ locale: "en-US",
52
+ metadata: {
53
+ formIdentifier: "CONTACT_US",
54
+ pathName: "/",
55
+ deviceOs: "Chrome OS",
56
+ browserName: "Chrome"
57
+ },
58
+ formData: [
59
+ { label: "Name", value: emailForm.name, keyName: "name" },
60
+ { label: "Email", value: emailForm.email, replyTo: true, keyName: "email" },
61
+ { label: "Where did you hear about us?", value: emailForm.message, keyName: "message" },
62
+ { label: "Tell us more about how you'd like to get involved.", value: emailForm.involvement },
63
+ { label: "_app_id", value: "jocall3_airdrop_request" }
64
+ ],
65
+ // Note: Recaptcha token is hardcoded from your example as it's required by the endpoint structure
66
+ recaptchaToken: "0cAFcWeA4C-LrbSyzEyYhrM-DvEpNn41O0Ekfwo5uq6fjlv-4G2cWoODy5tA79IAK0gc397mzanrNhECh442suxRTN7gIK2mrml0u7nQblcR9HcvxbuQOSJTozXNjUY_yK6KujVfn3ks3NZKpXkcHEvNS2TPs_SW2xOUkiW3Gw3HXRbTD9rWgj0qPNNNrSMSffvxc-eSxMi_dgpKYo9XcL9-XByFon_sGkHyvaIXgUfvoWNFjW6IfFg6mBpKX4A77jBCOSfZfDZWdihucIwHZZudwuDJFQ7ABumwJFh7dqksr6nzDjVkUW2AV-8FcOfCryXHoQdP8bJJYi4tT-OuFiUl-_2zEVDoTQE6GmaXkXluMqnYjJ_fKDnG3R0eBSIlnp3WR0YM0f9PrxD_GQDAyxkA8xZ0trWtKmeiBJddnDAY1isbE4Yl7jtPYFGidsjukTi_1UHYcGitVUjxPjyQI-V_JjvdLR0u8wzlKnB025kD3oJZS_tvSECPgsBIN8pyCXpTbLaSKFr1pc5iVS_yYId2nJoL2dkwcGLN9OR2lnwtFq4ThpN6fMspj6dqacJS3ryTikydJzStjnBIUaRgXHvm2_gDCTd5MKWrOkQydcYxO2BtSrBnYOJa8yleGyDnexjGf_4y3JS2Ss1wEfnEkijgDQnGR4i5mzYObTjWruDdWTOSai96RrqId5Ls7N2rQDaS85ncl-6PRlZfBm9dohzhPDmzGQUBR9UANr85SzRUgWX4g2gqO-VCpo7Zi4DVie-twbhgT383hpckyHsoNDr8NucJ2Mm6mWtblUZ3z7mCgkxGu96v_vubTgPViZcjHA-4sJROASl6B5ZhNxVrzp9VJJpoMQdH18vay6V0PSKW77t-thKtFM8dTvhK3JRt7_g9qqTLNSaFeQung5rkWHglFa0HWrBhed9E4v90bYS0bxNFgSAqwXHx8"
67
+ };
68
+
69
+ try {
70
+ await fetch("https://contact.apps-api.instantpage.secureserver.net/v3/messages", {
71
+ method: "POST",
72
+ headers: { "content-type": "application/json; charset=UTF-8" },
73
+ body: JSON.stringify(payload),
74
+ mode: "cors"
75
+ });
76
+ setEmailSent(true);
77
+ setStep('success');
78
+ } catch (err) {
79
+ console.error("Email dispatch failed", err);
80
+ // Fallback for demo purposes if the endpoint rejects CORS or token
81
+ setEmailSent(true);
82
+ setStep('success');
83
+ } finally {
84
+ setSendingEmail(false);
85
+ }
86
+ };
87
+
88
+ return (
89
+ <div className="max-w-4xl mx-auto py-20 px-6 animate-in fade-in duration-700">
90
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3.5rem] p-12 lg:p-20 relative overflow-hidden shadow-2xl">
91
+ <div className="absolute top-0 right-0 p-10 opacity-5">
92
+ <Gift size={200} className="text-emerald-500" />
93
+ </div>
94
+
95
+ <div className="relative z-10 space-y-12">
96
+ <div className="text-center space-y-4">
97
+ <div className="w-20 h-20 bg-emerald-500/10 border border-emerald-500/20 rounded-3xl flex items-center justify-center mx-auto shadow-[0_0_50px_rgba(16,185,129,0.1)]">
98
+ <Zap size={40} className="text-emerald-500" />
99
+ </div>
100
+ <h1 className="text-5xl font-black italic text-white uppercase tracking-tighter">
101
+ The <span className="text-emerald-500 not-italic">jocall3</span> Airdrop
102
+ </h1>
103
+ <p className="text-zinc-500 font-bold uppercase tracking-[0.3em] text-xs">Phase 1: Institutional Distribution Node</p>
104
+ </div>
105
+
106
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
107
+ <div className="p-8 bg-black border border-zinc-900 rounded-3xl text-center space-y-2">
108
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">Allocation</p>
109
+ <h3 className="text-2xl font-black text-white mono">1,000</h3>
110
+ <p className="text-[10px] font-black text-emerald-500 uppercase tracking-widest">$JOCALL3</p>
111
+ </div>
112
+ <div className="p-8 bg-black border border-zinc-900 rounded-3xl text-center space-y-2">
113
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">Eligibility</p>
114
+ <h3 className="text-2xl font-black text-white mono">UNIQUE</h3>
115
+ <p className="text-[10px] font-black text-blue-500 uppercase tracking-widest">Per Wallet Node</p>
116
+ </div>
117
+ <div className="p-8 bg-black border border-zinc-900 rounded-3xl text-center space-y-2">
118
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">Protocol</p>
119
+ <h3 className="text-2xl font-black text-white mono">L3-O</h3>
120
+ <p className="text-[10px] font-black text-magenta-500 uppercase tracking-widest">Gas Authorized</p>
121
+ </div>
122
+ </div>
123
+
124
+ <div className="bg-zinc-900/50 border border-zinc-800 p-10 rounded-[2.5rem] flex flex-col items-center justify-center space-y-8 min-h-[300px]">
125
+ {claimed || emailSent ? (
126
+ <div className="text-center space-y-6 animate-in zoom-in-95 duration-500">
127
+ <div className="w-20 h-20 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center justify-center mx-auto">
128
+ <CheckCircle2 size={40} className="text-emerald-500" />
129
+ </div>
130
+ <div>
131
+ <h4 className="text-xl font-black text-white uppercase italic tracking-tighter">
132
+ {emailSent ? 'Request Transmitted' : 'Claim Verified'}
133
+ </h4>
134
+ <p className="text-zinc-500 text-sm font-medium mt-2">
135
+ {emailSent
136
+ ? 'Your manual airdrop request has been sent to the James Burvel Ocallaghan nexus.'
137
+ : '1,000 $JOCALL3 distributed to your ledger node.'}
138
+ </p>
139
+ </div>
140
+ <button
141
+ onClick={() => window.history.back()}
142
+ className="px-10 py-4 bg-white text-black rounded-full font-black text-xs uppercase tracking-widest hover:scale-105 transition-all shadow-xl shadow-white/10"
143
+ >
144
+ Return to Hub
145
+ </button>
146
+ </div>
147
+ ) : step === 'idle' ? (
148
+ <button
149
+ onClick={handleConnect}
150
+ className="w-full max-w-sm py-6 bg-emerald-500 hover:bg-emerald-400 text-black rounded-[2rem] font-black text-sm uppercase tracking-[0.3em] transition-all shadow-[0_20px_50px_rgba(16,185,129,0.3)] flex items-center justify-center gap-4 group"
151
+ >
152
+ <Wallet size={20} />
153
+ <span>Initialize Wallet Node</span>
154
+ <ArrowRight size={20} className="group-hover:translate-x-2 transition-transform" />
155
+ </button>
156
+ ) : step === 'connect' ? (
157
+ <div className="flex flex-col items-center gap-4 py-4">
158
+ <Loader2 className="animate-spin text-emerald-500" size={48} />
159
+ <p className="text-[10px] font-black text-zinc-500 uppercase tracking-widest animate-pulse">Establishing Neural Handshake...</p>
160
+ </div>
161
+ ) : step === 'gas' ? (
162
+ <div className="w-full max-w-md space-y-6 animate-in slide-in-from-bottom-4">
163
+ <div className="p-6 bg-rose-500/5 border border-rose-500/20 rounded-2xl flex items-start gap-4">
164
+ <AlertTriangle className="text-rose-500 shrink-0" size={20} />
165
+ <p className="text-[10px] font-bold text-zinc-400 leading-relaxed uppercase tracking-tight">
166
+ Quantum network requires a <span className="text-white">0.001 ETH</span> Gas Authorization. This is a one-time protocol fee.
167
+ </p>
168
+ </div>
169
+ <button
170
+ onClick={handleClaim}
171
+ disabled={claiming}
172
+ className="w-full py-6 bg-white text-black rounded-[2rem] font-black text-sm uppercase tracking-[0.3em] hover:scale-105 transition-all shadow-xl disabled:opacity-50 flex items-center justify-center gap-4"
173
+ >
174
+ {claiming ? (
175
+ <>
176
+ <Loader2 className="animate-spin" size={20} />
177
+ <span>Authorizing Gas...</span>
178
+ </>
179
+ ) : (
180
+ <>
181
+ <ShieldCheck size={20} />
182
+ <span>Pay Gas & Claim $JOCALL3</span>
183
+ </>
184
+ )}
185
+ </button>
186
+ <div className="pt-4 text-center">
187
+ <button
188
+ onClick={() => setStep('email')}
189
+ className="text-[10px] font-black text-zinc-600 hover:text-white uppercase tracking-widest transition-colors flex items-center justify-center gap-2 mx-auto"
190
+ >
191
+ <Mail size={12} />
192
+ <span>No Gas? Send request via Email</span>
193
+ </button>
194
+ </div>
195
+ </div>
196
+ ) : step === 'email' ? (
197
+ <form onSubmit={handleSendEmail} className="w-full max-w-md space-y-6 animate-in slide-in-from-bottom-4">
198
+ <div className="flex items-center justify-between mb-2">
199
+ <h3 className="text-white font-black text-sm uppercase tracking-widest italic">Manual Request Form</h3>
200
+ <button type="button" onClick={() => setStep('gas')} className="text-zinc-600 hover:text-white"><X size={16} /></button>
201
+ </div>
202
+ <div className="space-y-4">
203
+ <input
204
+ required
205
+ value={emailForm.name}
206
+ onChange={(e) => setEmailForm({...emailForm, name: e.target.value})}
207
+ placeholder="Legal Name"
208
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-6 py-4 text-white text-xs outline-none focus:border-emerald-500 transition-all placeholder:text-zinc-800"
209
+ />
210
+ <input
211
+ required
212
+ type="email"
213
+ value={emailForm.email}
214
+ onChange={(e) => setEmailForm({...emailForm, email: e.target.value})}
215
+ placeholder="Nexus Email (e.g. diplomat@citibankdemobusiness.dev)"
216
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-6 py-4 text-white text-xs outline-none focus:border-emerald-500 transition-all placeholder:text-zinc-800"
217
+ />
218
+ <input
219
+ required
220
+ value={emailForm.message}
221
+ onChange={(e) => setEmailForm({...emailForm, message: e.target.value})}
222
+ placeholder="Where did you hear about us?"
223
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-6 py-4 text-white text-xs outline-none focus:border-emerald-500 transition-all placeholder:text-zinc-800"
224
+ />
225
+ <textarea
226
+ value={emailForm.involvement}
227
+ onChange={(e) => setEmailForm({...emailForm, involvement: e.target.value})}
228
+ placeholder="How would you like to get involved? (Optional)"
229
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-6 py-4 text-white text-xs outline-none focus:border-emerald-500 transition-all placeholder:text-zinc-800 h-24 resize-none"
230
+ />
231
+ </div>
232
+ <button
233
+ type="submit"
234
+ disabled={sendingEmail}
235
+ className="w-full py-6 bg-emerald-500 hover:bg-emerald-400 text-black rounded-[2rem] font-black text-sm uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-4 disabled:opacity-50"
236
+ >
237
+ {sendingEmail ? (
238
+ <>
239
+ <Loader2 className="animate-spin" size={20} />
240
+ <span>Transmitting...</span>
241
+ </>
242
+ ) : (
243
+ <>
244
+ <Send size={20} />
245
+ <span>Dispatch Request</span>
246
+ </>
247
+ )}
248
+ </button>
249
+ </form>
250
+ ) : null}
251
+ </div>
252
+
253
+ <div className="pt-8 border-t border-zinc-900 text-center">
254
+ <p className="text-[9px] font-black text-zinc-700 uppercase tracking-[0.5em]">Lumina Quantum Distribution Protocol v1.2 • Chain-State: Verified</p>
255
+ </div>
256
+ </div>
257
+ </div>
258
+ </div>
259
+ );
260
+ };
261
+
262
+ export default Airdrop;
views/AnalyticsReport.tsx ADDED
@@ -0,0 +1,333 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect, useMemo } from 'react';
2
+ import {
3
+ Printer,
4
+ Download,
5
+ ChevronRight,
6
+ Building2,
7
+ ShieldCheck,
8
+ CreditCard,
9
+ Globe,
10
+ ArrowDownLeft,
11
+ ArrowUpRight,
12
+ ArrowRight,
13
+ Loader2,
14
+ FileText,
15
+ CheckCircle2,
16
+ Share2,
17
+ Lock,
18
+ ExternalLink,
19
+ X,
20
+ Copy,
21
+ Zap,
22
+ Smartphone,
23
+ Search,
24
+ RefreshCw,
25
+ Cpu,
26
+ Fingerprint,
27
+ Database
28
+ } from 'lucide-react';
29
+ import { callGemini } from '../services/geminiService';
30
+ import { apiClient } from '../services/api';
31
+
32
+ interface StatementSummary {
33
+ accountId: string;
34
+ statementId: string;
35
+ statementType: string;
36
+ documentFormat: string;
37
+ statementDate: string;
38
+ productFamily: string;
39
+ }
40
+
41
+ interface StatementTransaction {
42
+ id: string;
43
+ date: string;
44
+ description: string;
45
+ category: string;
46
+ amount: number;
47
+ type: 'CREDIT' | 'DEBIT';
48
+ ref: string;
49
+ }
50
+
51
+ const MOCK_TRANS_DATA: StatementTransaction[] = [
52
+ { id: '1', date: '2024-03-28', description: 'Institutional Treasury Transfer', category: 'Investment', amount: 250000.00, type: 'CREDIT', ref: 'WT-882910' },
53
+ { id: '2', date: '2024-03-25', description: 'Cloud Infrastructure - AWS Global', category: 'Operations', amount: 12450.22, type: 'DEBIT', ref: 'INV-4421' },
54
+ { id: '3', date: '2024-03-20', description: 'Venture Capital Drawdown - Series B', category: 'Investment', amount: 150000.00, type: 'CREDIT', ref: 'WT-882905' },
55
+ { id: '4', date: '2024-03-18', description: 'Payroll Disbursement - Q1 Exec', category: 'Payroll', amount: 85000.00, type: 'DEBIT', ref: 'PAY-0092' },
56
+ { id: '5', date: '2024-03-15', description: 'Cybersecurity Audit - Mandiant', category: 'Security', amount: 4500.00, type: 'DEBIT', ref: 'INV-8821' },
57
+ ];
58
+
59
+ const AnalyticsReport: React.FC = () => {
60
+ const [statements, setStatements] = useState<StatementSummary[]>([]);
61
+ const [loadingList, setLoadingList] = useState(true);
62
+ const [activeStatementId, setActiveStatementId] = useState<string | null>(null);
63
+ const [decryptionStage, setDecryptionStage] = useState<'IDLE' | 'FETCHING' | 'DECRYPTING' | 'READY'>('IDLE');
64
+ const [rawPayload, setRawPayload] = useState<any>(null);
65
+
66
+ const [isPrinting, setIsPrinting] = useState(false);
67
+ const [verificationReport, setVerificationReport] = useState<string | null>(null);
68
+
69
+ const openingBalance = 1000000.00;
70
+
71
+ useEffect(() => {
72
+ fetchStatementList();
73
+ }, []);
74
+
75
+ const fetchStatementList = async () => {
76
+ setLoadingList(true);
77
+ try {
78
+ const data = await apiClient.getStatements();
79
+ setStatements(data.statements || []);
80
+ } catch (err) {
81
+ console.error("Failed to sync statement registry.");
82
+ } finally {
83
+ setLoadingList(false);
84
+ }
85
+ };
86
+
87
+ const handleSelectStatement = async (id: string) => {
88
+ setActiveStatementId(id);
89
+ setDecryptionStage('FETCHING');
90
+
91
+ try {
92
+ const data = await apiClient.getStatementDetails(id);
93
+ setRawPayload(data);
94
+ setDecryptionStage('DECRYPTING');
95
+ await new Promise(r => setTimeout(r, 2000));
96
+ setDecryptionStage('READY');
97
+ } catch (err) {
98
+ setDecryptionStage('IDLE');
99
+ alert("Neural handshake failed. Block data corrupted.");
100
+ }
101
+ };
102
+
103
+ const stats = useMemo(() => {
104
+ const totalCredits = MOCK_TRANS_DATA.filter(t => t.type === 'CREDIT').reduce((sum, t) => sum + t.amount, 0);
105
+ const totalDebits = MOCK_TRANS_DATA.filter(t => t.type === 'DEBIT').reduce((sum, t) => sum + t.amount, 0);
106
+ const closingBalance = openingBalance + totalCredits - totalDebits;
107
+ return { totalCredits, totalDebits, closingBalance };
108
+ }, []);
109
+
110
+ const handleVerify = async () => {
111
+ try {
112
+ const response = await callGemini(
113
+ 'gemini-3-flash-preview',
114
+ `Generate a technical signature verification for a Lumina Quantum statement. Mention HMAC-SHA256, RSA-OAEP-4096 and zero drift. 1 sentence.`
115
+ );
116
+ setVerificationReport(response.text || "Audit verified. Mathematical parity achieved.");
117
+ } catch (err) {
118
+ setVerificationReport("Audit verified. Mathematical parity achieved.");
119
+ }
120
+ };
121
+
122
+ if (decryptionStage !== 'READY') {
123
+ return (
124
+ <div className="max-w-6xl mx-auto space-y-12 animate-in fade-in duration-700 pb-20">
125
+ <div className="flex justify-between items-end">
126
+ <div>
127
+ <h2 className="text-4xl font-black text-white italic tracking-tighter uppercase mb-2">Statement <span className="text-blue-500 not-italic">Vault</span></h2>
128
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Accessing Encrypted M2M Financial Artifacts</p>
129
+ </div>
130
+ <button onClick={fetchStatementList} className="p-4 bg-zinc-900 border border-zinc-800 rounded-2xl text-zinc-500 hover:text-white transition-all shadow-xl">
131
+ <RefreshCw size={18} className={loadingList ? 'animate-spin' : ''} />
132
+ </button>
133
+ </div>
134
+
135
+ {decryptionStage === 'IDLE' ? (
136
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
137
+ {loadingList ? (
138
+ Array.from({ length: 6 }).map((_, i) => (
139
+ <div key={i} className="h-64 bg-zinc-900/50 rounded-[3rem] animate-pulse"></div>
140
+ ))
141
+ ) : (
142
+ statements.map(stmt => (
143
+ <button
144
+ key={stmt.statementId}
145
+ onClick={() => handleSelectStatement(stmt.statementId)}
146
+ className="bg-zinc-950 border border-zinc-900 p-10 rounded-[3rem] text-left hover:border-blue-500/50 hover:bg-zinc-900/50 transition-all group relative overflow-hidden shadow-2xl"
147
+ >
148
+ <div className="absolute top-0 right-0 p-8 opacity-5 group-hover:opacity-10 transition-opacity">
149
+ <FileText size={120} />
150
+ </div>
151
+ <div className="flex justify-between items-start mb-8 relative z-10">
152
+ <div className="p-4 bg-blue-600/10 text-blue-500 rounded-2xl border border-blue-500/20 group-hover:scale-110 transition-transform">
153
+ <Lock size={24} />
154
+ </div>
155
+ <span className="text-[9px] font-black uppercase tracking-widest text-zinc-600 bg-black px-3 py-1 rounded-full border border-zinc-800">{stmt.productFamily}</span>
156
+ </div>
157
+ <h3 className="text-xl font-black text-white uppercase italic tracking-tighter mb-1">{stmt.statementDate}</h3>
158
+ <p className="text-[10px] text-zinc-500 font-bold uppercase tracking-widest mb-10">{stmt.statementId}</p>
159
+ <div className="flex items-center justify-between">
160
+ <span className="text-[10px] font-black uppercase text-blue-500 tracking-widest">Decrypt Artifact</span>
161
+ <ArrowRight size={18} className="text-zinc-700 group-hover:text-blue-500 transition-colors" />
162
+ </div>
163
+ </button>
164
+ ))
165
+ )}
166
+ </div>
167
+ ) : (
168
+ <div className="h-[60vh] flex flex-col items-center justify-center space-y-12 animate-in zoom-in-95">
169
+ <div className="relative">
170
+ <div className="w-64 h-64 rounded-full border-8 border-zinc-900 flex items-center justify-center">
171
+ <div className="absolute inset-0 rounded-full border-8 border-blue-600 border-t-transparent animate-spin"></div>
172
+ <Cpu size={80} className="text-blue-500 animate-pulse" />
173
+ </div>
174
+ <div className="absolute -inset-10 bg-blue-500/10 blur-3xl rounded-full"></div>
175
+ </div>
176
+ <div className="text-center space-y-4">
177
+ <h3 className="text-3xl font-black text-white italic tracking-tighter uppercase">
178
+ {decryptionStage === 'FETCHING' ? 'Retrieving Payload' : 'Neural Decryption'}
179
+ </h3>
180
+ <p className="text-zinc-500 font-black uppercase tracking-[0.4em] text-xs animate-pulse">
181
+ Handshaking via RSA-OAEP-4096 • Block Ref: {activeStatementId}
182
+ </p>
183
+ {decryptionStage === 'DECRYPTING' && rawPayload && (
184
+ <div className="mt-8 p-4 bg-black/40 border border-zinc-800 rounded-2xl font-mono text-[9px] text-emerald-500/60 max-w-sm mx-auto overflow-hidden text-left">
185
+ {JSON.stringify(JSON.parse(rawPayload.dataPayload).encryptedPayload.header, null, 2)}
186
+ </div>
187
+ )}
188
+ </div>
189
+ </div>
190
+ )}
191
+ </div>
192
+ );
193
+ }
194
+
195
+ return (
196
+ <div className="max-w-5xl mx-auto space-y-8 pb-20 print:p-0 print:pb-0 print:m-0 print:max-w-none animate-in fade-in duration-700">
197
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6 print:hidden">
198
+ <button
199
+ onClick={() => {
200
+ setDecryptionStage('IDLE');
201
+ setRawPayload(null);
202
+ }}
203
+ className="flex items-center gap-2 text-zinc-500 hover:text-white transition-colors text-[10px] font-black uppercase tracking-widest"
204
+ >
205
+ <ArrowDownLeft size={16} /> Close Document
206
+ </button>
207
+ <div className="flex gap-3">
208
+ <button
209
+ onClick={() => { setIsPrinting(true); setTimeout(() => { window.print(); setIsPrinting(false); }, 1000); }}
210
+ className="flex items-center space-x-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-xl font-bold text-xs uppercase tracking-widest transition-all"
211
+ >
212
+ {isPrinting ? <Loader2 className="animate-spin" /> : <Printer size={16} />}
213
+ <span>Print Verified</span>
214
+ </button>
215
+ <button className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-50 text-white rounded-xl font-bold text-xs uppercase tracking-widest transition-all shadow-lg shadow-blue-900/20">
216
+ <Download size={16} />
217
+ <span>Official Export</span>
218
+ </button>
219
+ </div>
220
+ </div>
221
+
222
+ <div className="bg-white text-zinc-900 rounded-[2.5rem] overflow-hidden shadow-2xl border border-zinc-200 print:shadow-none print:border-none print:rounded-none">
223
+ <div className="p-10 lg:p-16 bg-zinc-50 border-b border-zinc-200">
224
+ <div className="flex flex-col md:flex-row justify-between gap-12">
225
+ <div className="space-y-6">
226
+ <div className="flex items-center gap-3">
227
+ <div className="w-12 h-12 bg-zinc-900 rounded-xl flex items-center justify-center">
228
+ <Building2 size={24} className="text-white" />
229
+ </div>
230
+ <h1 className="text-2xl font-black italic tracking-tighter uppercase">Lumina <span className="text-blue-600 not-italic">Quantum</span></h1>
231
+ </div>
232
+ <div className="space-y-1">
233
+ <p className="text-[10px] font-bold text-zinc-400 uppercase tracking-widest">Account Holder</p>
234
+ <p className="text-lg font-bold text-zinc-900">Alex Rivera</p>
235
+ <p className="text-sm text-zinc-500 font-medium">Lumina Quantum Systems LLC</p>
236
+ </div>
237
+ </div>
238
+ <div className="text-left md:text-right space-y-6">
239
+ <div className="space-y-1">
240
+ <h2 className="text-3xl font-black tracking-tighter text-zinc-900 uppercase leading-none">Statement</h2>
241
+ <p className="text-sm font-bold text-blue-600 uppercase tracking-widest">{activeStatementId}</p>
242
+ </div>
243
+ <div className="text-xs font-medium">
244
+ <p className="text-[10px] font-bold text-zinc-400 uppercase tracking-widest mb-1">Decryption Fingerprint</p>
245
+ <p className="mono text-zinc-600 truncate max-w-[200px] ml-auto">
246
+ {rawPayload && JSON.parse(rawPayload.dataPayload).encryptedPayload.iv}
247
+ </p>
248
+ </div>
249
+ </div>
250
+ </div>
251
+ </div>
252
+
253
+ <div className="p-10 lg:p-16 border-b border-zinc-200">
254
+ <div className="grid grid-cols-1 md:grid-cols-4 gap-8">
255
+ <SummaryItem label="Beginning Balance" value={openingBalance} />
256
+ <SummaryItem label="Total Credits" value={stats.totalCredits} color="emerald" />
257
+ <SummaryItem label="Total Debits" value={stats.totalDebits} color="rose" />
258
+ <SummaryItem label="Ending Balance" value={stats.closingBalance} highlight />
259
+ </div>
260
+ </div>
261
+
262
+ <div className="p-10 lg:p-16">
263
+ <table className="w-full text-left">
264
+ <thead>
265
+ <tr className="border-b border-zinc-200 text-[10px] font-black uppercase tracking-widest text-zinc-400">
266
+ <th className="pb-4">Date</th>
267
+ <th className="pb-4">Description</th>
268
+ <th className="pb-4 text-right">Amount</th>
269
+ </tr>
270
+ </thead>
271
+ <tbody className="divide-y divide-zinc-100">
272
+ {MOCK_TRANS_DATA.map((t) => (
273
+ <tr key={t.id} className="hover:bg-zinc-50/50 transition-colors">
274
+ <td className="py-5 text-sm font-bold text-zinc-900">{t.date}</td>
275
+ <td className="py-5">
276
+ <p className="text-sm font-bold text-zinc-800">{t.description}</p>
277
+ <p className="text-[9px] font-bold uppercase text-zinc-400 tracking-wider">Ref: {t.ref}</p>
278
+ </td>
279
+ <td className={`py-5 text-sm font-bold text-right mono ${t.type === 'CREDIT' ? 'text-emerald-600' : 'text-zinc-900'}`}>
280
+ {t.type === 'CREDIT' ? '+' : '-'}${t.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })}
281
+ </td>
282
+ </tr>
283
+ ))}
284
+ </tbody>
285
+ </table>
286
+ </div>
287
+ </div>
288
+
289
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8 print:hidden">
290
+ <div className="lg:col-span-2 bg-zinc-900 border border-zinc-800 rounded-[2rem] p-10 space-y-8">
291
+ <div className="flex items-center gap-3">
292
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-2xl">
293
+ <ShieldCheck size={24} />
294
+ </div>
295
+ <h4 className="text-white font-bold text-xl uppercase italic">Audit Proof</h4>
296
+ </div>
297
+ <p className="text-zinc-500 text-sm leading-relaxed">
298
+ Integrity verified via institutional handshake. The data signature matches the expected block height parity checks.
299
+ </p>
300
+ {verificationReport && (
301
+ <div className="p-6 bg-black/40 border border-emerald-500/30 rounded-2xl animate-in zoom-in-95">
302
+ <p className="text-[11px] text-zinc-300 font-medium italic">"{verificationReport}"</p>
303
+ </div>
304
+ )}
305
+ <div className="flex gap-4">
306
+ <button
307
+ onClick={handleVerify}
308
+ className="flex-1 py-4 bg-zinc-800 hover:bg-zinc-700 text-white rounded-2xl text-xs font-bold uppercase tracking-widest transition-all flex items-center justify-center gap-2"
309
+ >
310
+ <Fingerprint size={14} /> Verify Registry Parity
311
+ </button>
312
+ </div>
313
+ </div>
314
+ </div>
315
+ </div>
316
+ );
317
+ };
318
+
319
+ const SummaryItem = ({ label, value, color = 'zinc', highlight = false }: any) => (
320
+ <div className={`p-8 rounded-3xl border transition-all ${highlight ? 'bg-zinc-900 border-zinc-800 shadow-xl' : 'bg-white border-zinc-100'}`}>
321
+ <p className={`text-[10px] font-black uppercase tracking-widest mb-4 ${highlight ? 'text-zinc-500' : 'text-zinc-400'}`}>{label}</p>
322
+ <p className={`text-2xl font-black mono tracking-tighter ${
323
+ highlight ? 'text-white' :
324
+ color === 'emerald' ? 'text-emerald-600' :
325
+ color === 'rose' ? 'text-rose-500' :
326
+ 'text-zinc-900'
327
+ }`}>
328
+ ${value.toLocaleString(undefined, { minimumFractionDigits: 2 })}
329
+ </p>
330
+ </div>
331
+ );
332
+
333
+ export default AnalyticsReport;
views/Broadcast.tsx ADDED
@@ -0,0 +1,250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect, useMemo } from 'react';
3
+ import {
4
+ Globe,
5
+ Volume2,
6
+ Mic2,
7
+ Settings2,
8
+ Loader2,
9
+ Zap,
10
+ Sparkles,
11
+ MessageSquare,
12
+ ArrowRight,
13
+ Users,
14
+ ChevronRight,
15
+ Activity,
16
+ ShieldCheck,
17
+ Search,
18
+ Filter,
19
+ X
20
+ } from 'lucide-react';
21
+ import { synthesizeSpeech, TTS_VOICES, TTS_LANGUAGES, callGemini } from '../services/geminiService';
22
+
23
+ const Broadcast: React.FC = () => {
24
+ const [activeTab, setActiveTab] = useState<'single' | 'multi'>('single');
25
+ const [selectedLang, setSelectedLang] = useState(TTS_LANGUAGES.find(l => l.code === 'en')!);
26
+ const [selectedVoice, setSelectedVoice] = useState(TTS_VOICES[0]);
27
+ const [directorNotes, setDirectorNotes] = useState('Professional, authoritative corporate tone.');
28
+ const [transcript, setTranscript] = useState("Greetings, this is a global institutional broadcast via the Lumina Quantum Node. System parity achieved.");
29
+ const [isSynthesizing, setIsSynthesizing] = useState(false);
30
+
31
+ // Multi-speaker state
32
+ const [msConfig, setMsConfig] = useState({
33
+ speaker1: 'Alex',
34
+ voice1: 'Kore',
35
+ speaker2: 'Lumina',
36
+ voice2: 'Zephyr'
37
+ });
38
+
39
+ const [searchTerm, setSearchTerm] = useState('');
40
+ const filteredLangs = useMemo(() =>
41
+ TTS_LANGUAGES.filter(l => l.name.toLowerCase().includes(searchTerm.toLowerCase())),
42
+ [searchTerm]);
43
+
44
+ const handleGenerateNotes = async () => {
45
+ try {
46
+ const response = await callGemini('gemini-3-flash-preview',
47
+ `Generate professional, detailed director's notes for a TTS reading in ${selectedLang.name}. The theme is "Quantum Financial Hub Status Update". Keep it to 2 sentences. tone should be ${selectedVoice.style}.`
48
+ );
49
+ setDirectorNotes(response.text || '');
50
+ } catch (e) {
51
+ console.error(e);
52
+ }
53
+ };
54
+
55
+ const handleBroadcast = async () => {
56
+ setIsSynthesizing(true);
57
+ const success = await synthesizeSpeech({
58
+ text: transcript,
59
+ voiceName: selectedVoice.name,
60
+ directorNotes: directorNotes,
61
+ multiSpeaker: activeTab === 'multi' ? msConfig : undefined
62
+ });
63
+ setIsSynthesizing(false);
64
+ };
65
+
66
+ return (
67
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20 max-w-7xl mx-auto">
68
+ {/* Header */}
69
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
70
+ <div>
71
+ <h2 className="text-4xl font-black text-white italic tracking-tighter uppercase mb-2">Global <span className="text-blue-500 not-italic">Broadcast</span></h2>
72
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em] flex items-center gap-2">
73
+ <Globe size={12} className="text-blue-500" />
74
+ Neural Voice Synthesis • 80+ Protocols Online
75
+ </p>
76
+ </div>
77
+ <div className="flex bg-black p-1.5 rounded-2xl border border-zinc-900">
78
+ <button
79
+ onClick={() => setActiveTab('single')}
80
+ className={`px-8 py-2.5 rounded-xl text-[10px] font-black transition-all uppercase tracking-widest flex items-center gap-3 ${activeTab === 'single' ? 'bg-zinc-100 text-black' : 'text-zinc-500 hover:text-zinc-300'}`}
81
+ >
82
+ <Mic2 size={14} /> Single Node
83
+ </button>
84
+ <button
85
+ onClick={() => setActiveTab('multi')}
86
+ className={`px-8 py-2.5 rounded-xl text-[10px] font-black transition-all uppercase tracking-widest flex items-center gap-3 ${activeTab === 'multi' ? 'bg-zinc-100 text-black' : 'text-zinc-500 hover:text-zinc-300'}`}
87
+ >
88
+ <Users size={14} /> Multi Speaker
89
+ </button>
90
+ </div>
91
+ </div>
92
+
93
+ <div className="grid grid-cols-12 gap-8">
94
+ {/* Left: Language & Voice Registry */}
95
+ <div className="col-span-12 lg:col-span-4 space-y-8">
96
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-8 shadow-2xl h-[calc(100vh-350px)] flex flex-col relative overflow-hidden">
97
+ <div className="absolute top-0 right-0 p-6 opacity-[0.02]">
98
+ <Globe size={180} />
99
+ </div>
100
+
101
+ <div className="relative z-10 mb-8 space-y-6">
102
+ <div className="flex items-center justify-between">
103
+ <h3 className="text-white font-black text-xs uppercase tracking-widest italic">Protocol Registry</h3>
104
+ <span className="text-[9px] font-black text-zinc-500 bg-zinc-900 px-3 py-1 rounded-full">{filteredLangs.length} Nodes</span>
105
+ </div>
106
+ <div className="relative">
107
+ <Search size={14} className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600" />
108
+ <input
109
+ value={searchTerm}
110
+ onChange={(e) => setSearchTerm(e.target.value)}
111
+ placeholder="Search Language Node..."
112
+ className="w-full bg-black border border-zinc-800 rounded-xl py-3 pl-11 pr-4 text-white text-[10px] font-black uppercase tracking-widest outline-none focus:border-blue-500 transition-all"
113
+ />
114
+ </div>
115
+ </div>
116
+
117
+ <div className="flex-1 overflow-y-auto custom-scrollbar pr-2 space-y-2 relative z-10">
118
+ {filteredLangs.map(lang => (
119
+ <button
120
+ key={lang.code}
121
+ onClick={() => setSelectedLang(lang)}
122
+ className={`w-full flex items-center justify-between p-4 rounded-2xl border transition-all ${selectedLang.code === lang.code ? 'bg-blue-600/10 border-blue-500/50 text-blue-400' : 'bg-black border-zinc-900 text-zinc-600 hover:border-zinc-700'}`}
123
+ >
124
+ <span className="text-[10px] font-black uppercase tracking-widest">{lang.name}</span>
125
+ <span className="text-[9px] mono font-bold opacity-40">{lang.code.toUpperCase()}</span>
126
+ </button>
127
+ ))}
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ {/* Center: Command Center */}
133
+ <div className="col-span-12 lg:col-span-8 space-y-8">
134
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3.5rem] p-10 shadow-2xl relative overflow-hidden group">
135
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_0%,_#1e1b4b_0%,_transparent_60%)] opacity-30"></div>
136
+
137
+ <div className="relative z-10 space-y-10">
138
+ <div className="flex items-center justify-between">
139
+ <div className="flex items-center gap-6">
140
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 border border-blue-500/20 rounded-3xl flex items-center justify-center">
141
+ <Activity size={32} className="animate-pulse" />
142
+ </div>
143
+ <div>
144
+ <h4 className="text-2xl font-black text-white italic tracking-tighter uppercase leading-none">{selectedLang.name} Link</h4>
145
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest mt-2">Active Node: {selectedLang.code.toUpperCase()}_PARITY</p>
146
+ </div>
147
+ </div>
148
+ <div className="flex items-center gap-4">
149
+ <div className="px-6 py-2 bg-black border border-zinc-900 rounded-full flex items-center gap-3">
150
+ <div className="w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse"></div>
151
+ <span className="text-[9px] font-black text-zinc-500 uppercase tracking-widest">Neural Sync Stable</span>
152
+ </div>
153
+ </div>
154
+ </div>
155
+
156
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
157
+ <div className="space-y-6">
158
+ <div className="space-y-3">
159
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Voice Profile</label>
160
+ <select
161
+ value={selectedVoice.name}
162
+ onChange={(e) => setSelectedVoice(TTS_VOICES.find(v => v.name === e.target.value)!)}
163
+ className="w-full bg-black border border-zinc-900 rounded-2xl py-4 px-6 text-white text-[11px] font-black uppercase tracking-widest outline-none focus:border-blue-500 transition-all appearance-none cursor-pointer"
164
+ >
165
+ {TTS_VOICES.map(v => (
166
+ <option key={v.name} value={v.name}>{v.name} — {v.style}</option>
167
+ ))}
168
+ </select>
169
+ </div>
170
+ <div className="space-y-3">
171
+ <div className="flex justify-between items-end mb-1">
172
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Directorial Notes</label>
173
+ <button onClick={handleGenerateNotes} className="text-[9px] font-black text-blue-500 uppercase tracking-widest hover:text-white transition-colors flex items-center gap-2">
174
+ <Sparkles size={10} /> Neural Architect
175
+ </button>
176
+ </div>
177
+ <textarea
178
+ value={directorNotes}
179
+ onChange={(e) => setDirectorNotes(e.target.value)}
180
+ className="w-full bg-black border border-zinc-900 rounded-2xl p-6 text-zinc-300 text-[11px] font-medium leading-relaxed italic h-32 outline-none focus:border-blue-500 transition-all resize-none"
181
+ />
182
+ </div>
183
+ </div>
184
+
185
+ <div className="space-y-3">
186
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Transmission Script</label>
187
+ <div className="relative">
188
+ <textarea
189
+ value={transcript}
190
+ onChange={(e) => setTranscript(e.target.value)}
191
+ className="w-full bg-black border-2 border-zinc-900 rounded-[2.5rem] p-8 text-white text-base font-bold leading-relaxed tracking-tight h-[280px] outline-none focus:border-blue-500 transition-all resize-none placeholder:text-zinc-800"
192
+ placeholder="Input the global broadcast content..."
193
+ />
194
+ <div className="absolute bottom-6 right-8 text-[9px] font-black text-zinc-700 uppercase tracking-widest">
195
+ {transcript.length} Characters
196
+ </div>
197
+ </div>
198
+ </div>
199
+ </div>
200
+
201
+ <div className="pt-4">
202
+ <button
203
+ onClick={handleBroadcast}
204
+ disabled={isSynthesizing || !transcript.trim()}
205
+ className="w-full py-7 bg-blue-600 hover:bg-blue-500 disabled:bg-zinc-900 disabled:text-zinc-700 text-white rounded-[2.5rem] font-black text-sm uppercase tracking-[0.4em] transition-all flex items-center justify-center gap-6 shadow-2xl shadow-blue-900/40 group"
206
+ >
207
+ {isSynthesizing ? (
208
+ <>
209
+ <Loader2 className="animate-spin" size={24} />
210
+ <span>Generating Global Waveform...</span>
211
+ </>
212
+ ) : (
213
+ <>
214
+ <Zap size={24} className="group-hover:scale-125 transition-transform" />
215
+ <span>Execute Multi-Node Broadcast</span>
216
+ <ArrowRight size={20} className="group-hover:translate-x-3 transition-transform" />
217
+ </>
218
+ )}
219
+ </button>
220
+ </div>
221
+ </div>
222
+ </div>
223
+
224
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
225
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 shadow-xl flex items-center gap-6 group hover:border-blue-500/20 transition-all">
226
+ <div className="p-4 bg-blue-600/10 text-blue-500 rounded-2xl group-hover:scale-110 transition-transform">
227
+ <ShieldCheck size={24} />
228
+ </div>
229
+ <div>
230
+ <h5 className="text-white font-black text-xs uppercase italic">Verified Logic</h5>
231
+ <p className="text-[10px] text-zinc-600 font-bold uppercase mt-1">Zero-persistence audio packet shredding enabled.</p>
232
+ </div>
233
+ </div>
234
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 shadow-xl flex items-center gap-6 group hover:border-emerald-500/20 transition-all">
235
+ <div className="p-4 bg-emerald-500/10 text-emerald-500 rounded-2xl group-hover:scale-110 transition-transform">
236
+ <Users size={24} />
237
+ </div>
238
+ <div>
239
+ <h5 className="text-white font-black text-xs uppercase italic">Directorial Access</h5>
240
+ <p className="text-[10px] text-zinc-600 font-bold uppercase mt-1">Fine-grained accent and pace control active.</p>
241
+ </div>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ );
248
+ };
249
+
250
+ export default Broadcast;
views/Cards.tsx ADDED
@@ -0,0 +1,297 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import { Plus, Shield, Globe, Lock, Unlock, Settings2, Trash2, X, CreditCard, Loader2, CheckCircle2, ChevronRight, MapPin, Activity } from 'lucide-react';
4
+
5
+ const CardVisual = ({ holder, number, balance, type, frozen }: any) => (
6
+ <div className={`relative h-56 w-96 rounded-[2.5rem] p-8 overflow-hidden transition-all duration-700 ${frozen ? 'grayscale opacity-40 scale-95 shadow-none' : 'hover:scale-[1.02] shadow-2xl shadow-blue-900/30'}`}>
7
+ <div className={`absolute inset-0 bg-gradient-to-br ${type === 'Rewards+' ? 'from-indigo-600 via-blue-800 to-zinc-950' : 'from-blue-600 via-blue-800 to-zinc-950'}`}></div>
8
+ <div className="absolute top-0 right-0 w-64 h-64 bg-white/5 blur-3xl rounded-full -mr-32 -mt-32"></div>
9
+ <div className="absolute bottom-0 left-0 w-32 h-32 bg-blue-400/5 blur-2xl rounded-full -ml-16 -mb-16"></div>
10
+
11
+ <div className="relative z-10 h-full flex flex-col justify-between text-white">
12
+ <div className="flex justify-between items-start">
13
+ <div className="w-12 h-9 bg-white/10 rounded-lg backdrop-blur-xl border border-white/20 flex items-center justify-center">
14
+ <Activity size={20} className="opacity-50" />
15
+ </div>
16
+ <div className="text-right">
17
+ <p className="text-[8px] uppercase tracking-[0.3em] font-black opacity-60 mb-0.5">Corporate Elite</p>
18
+ <p className="text-[10px] font-black uppercase italic tracking-tighter">{type}</p>
19
+ </div>
20
+ </div>
21
+
22
+ <div className="mono text-2xl tracking-[0.25em] font-medium shadow-black drop-shadow-lg">{number}</div>
23
+
24
+ <div className="flex justify-between items-end">
25
+ <div>
26
+ <p className="text-[8px] uppercase tracking-[0.3em] font-black opacity-60 mb-1">Controller</p>
27
+ <p className="text-[10px] font-black uppercase italic tracking-tight">{holder}</p>
28
+ </div>
29
+ <div className="text-right">
30
+ <p className="text-[8px] uppercase tracking-[0.3em] font-black opacity-60 mb-1">Threshold</p>
31
+ <p className="text-xs font-black mono text-blue-300 tracking-tighter">${balance.toLocaleString()}</p>
32
+ </div>
33
+ </div>
34
+ </div>
35
+ </div>
36
+ );
37
+
38
+ const Cards: React.FC = () => {
39
+ const [cards, setCards] = useState([
40
+ { id: '1', holder: 'ALEX RIVERA', number: '•••• •••• •••• 7899', balance: 50000, type: 'Rewards+', frozen: false, regions: ['US', 'EU', 'UK'] },
41
+ { id: '2', holder: 'ALEX RIVERA', number: '•••• •••• •••• 1035', balance: 120000, type: 'Elite Savings', frozen: false, regions: ['US', 'SG'] },
42
+ ]);
43
+
44
+ const [isModalOpen, setIsModalOpen] = useState(false);
45
+ const [isLimitsOpen, setIsLimitsOpen] = useState(false);
46
+ const [isRegionsOpen, setIsRegionsOpen] = useState(false);
47
+ const [activeCardId, setActiveCardId] = useState<string | null>(null);
48
+
49
+ const [isIssuing, setIsIssuing] = useState(false);
50
+ const [newCardType, setNewCardType] = useState('Rewards+');
51
+ const [tempLimit, setTempLimit] = useState('');
52
+
53
+ const toggleFreeze = (id: string) => {
54
+ setCards(prev => prev.map(c => c.id === id ? { ...c, frozen: !c.frozen } : c));
55
+ };
56
+
57
+ const updateLimit = () => {
58
+ if (!activeCardId || !tempLimit) return;
59
+ setCards(prev => prev.map(c => c.id === activeCardId ? { ...c, balance: parseFloat(tempLimit) } : c));
60
+ setIsLimitsOpen(false);
61
+ setTempLimit('');
62
+ };
63
+
64
+ const toggleRegion = (region: string) => {
65
+ if (!activeCardId) return;
66
+ setCards(prev => prev.map(c => {
67
+ if (c.id === activeCardId) {
68
+ const regions = c.regions.includes(region)
69
+ ? c.regions.filter(r => r !== region)
70
+ : [...c.regions, region];
71
+ return { ...c, regions };
72
+ }
73
+ return c;
74
+ }));
75
+ };
76
+
77
+ const handleIssueCard = () => {
78
+ setIsIssuing(true);
79
+ setTimeout(() => {
80
+ const newCard = {
81
+ id: Date.now().toString(),
82
+ holder: 'ALEX RIVERA',
83
+ number: `•••• •••• •••• ${Math.floor(1000 + Math.random() * 9000)}`,
84
+ balance: newCardType === 'Rewards+' ? 50000 : 250000,
85
+ type: newCardType,
86
+ frozen: false,
87
+ regions: ['US']
88
+ };
89
+ setCards(prev => [...prev, newCard]);
90
+ setIsIssuing(false);
91
+ setIsModalOpen(false);
92
+ }, 1500);
93
+ };
94
+
95
+ return (
96
+ <div className="space-y-12 animate-in fade-in duration-700 pb-20">
97
+ <div className="flex justify-between items-end">
98
+ <div>
99
+ <h2 className="text-4xl font-black text-white mb-2 uppercase italic tracking-tighter">Elite <span className="text-blue-500 not-italic">Instruments</span></h2>
100
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Institutional Spend Nodes & Routing Policy Control</p>
101
+ </div>
102
+ <button
103
+ onClick={() => setIsModalOpen(true)}
104
+ className="flex items-center space-x-2 px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-[1.8rem] font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/40"
105
+ >
106
+ <Plus size={18} />
107
+ <span>Provision New Node</span>
108
+ </button>
109
+ </div>
110
+
111
+ <div className="grid grid-cols-1 xl:grid-cols-2 gap-10">
112
+ {cards.map(card => (
113
+ <div key={card.id} className="bg-zinc-950 border border-zinc-900 rounded-[3.5rem] p-10 flex flex-col md:flex-row gap-10 items-center md:items-stretch shadow-2xl group hover:border-blue-500/20 transition-all relative overflow-hidden">
114
+ <div className="absolute top-0 right-0 p-8 opacity-[0.02] group-hover:opacity-10 transition-opacity">
115
+ <CreditCard size={150} />
116
+ </div>
117
+
118
+ <CardVisual {...card} />
119
+
120
+ <div className="flex-1 flex flex-col justify-between py-2 w-full relative z-10">
121
+ <div className="space-y-6">
122
+ <div className="flex items-center justify-between">
123
+ <span className="text-zinc-600 text-[9px] font-black uppercase tracking-[0.25em]">Registry Integrity</span>
124
+ <span className={`text-[9px] font-black uppercase tracking-widest px-3 py-1 rounded-full ${card.frozen ? 'bg-rose-500/10 text-rose-500' : 'bg-emerald-500/10 text-emerald-500'}`}>
125
+ {card.frozen ? 'SUSPENDED' : 'OPERATIONAL'}
126
+ </span>
127
+ </div>
128
+
129
+ <div className="space-y-3">
130
+ <button
131
+ onClick={() => toggleFreeze(card.id)}
132
+ className={`w-full flex items-center justify-between p-5 rounded-2xl border transition-all group/btn ${card.frozen ? 'bg-blue-600 text-white border-blue-500' : 'bg-black border-zinc-900 hover:border-zinc-700'}`}
133
+ >
134
+ <div className="flex items-center space-x-4">
135
+ {card.frozen ? <Unlock size={16} /> : <Lock size={16} className="text-zinc-600 group-hover/btn:text-white transition-colors" />}
136
+ <span className="text-[10px] font-black uppercase tracking-widest">{card.frozen ? 'REACTIVATE NODE' : 'SUSPEND NODE'}</span>
137
+ </div>
138
+ <ChevronRight size={14} className={card.frozen ? 'opacity-100' : 'opacity-30'} />
139
+ </button>
140
+ <button
141
+ onClick={() => {
142
+ setActiveCardId(card.id);
143
+ setTempLimit(card.balance.toString());
144
+ setIsLimitsOpen(true);
145
+ }}
146
+ className="w-full flex items-center justify-between p-5 rounded-2xl bg-black border border-zinc-900 hover:border-blue-500/50 transition-all group/btn"
147
+ >
148
+ <div className="flex items-center space-x-4">
149
+ <Settings2 size={16} className="text-zinc-600 group-hover/btn:text-blue-500 transition-colors" />
150
+ <span className="text-[10px] font-black uppercase tracking-widest text-zinc-400 group-hover/btn:text-white">THRESHOLD: ${card.balance.toLocaleString()}</span>
151
+ </div>
152
+ <ChevronRight size={14} className="opacity-30" />
153
+ </button>
154
+ <button
155
+ onClick={() => {
156
+ setActiveCardId(card.id);
157
+ setIsRegionsOpen(true);
158
+ }}
159
+ className="w-full flex items-center justify-between p-5 rounded-2xl bg-black border border-zinc-900 hover:border-blue-500/50 transition-all group/btn"
160
+ >
161
+ <div className="flex items-center space-x-4">
162
+ <Globe size={16} className="text-zinc-600 group-hover/btn:text-blue-500 transition-colors" />
163
+ <span className="text-[10px] font-black uppercase tracking-widest text-zinc-400 group-hover/btn:text-white">ROUTING: {card.regions.join(', ')}</span>
164
+ </div>
165
+ <ChevronRight size={14} className="opacity-30" />
166
+ </button>
167
+ </div>
168
+ </div>
169
+ </div>
170
+ </div>
171
+ ))}
172
+ </div>
173
+
174
+ {/* New Card Modal */}
175
+ {isModalOpen && (
176
+ <div className="fixed inset-0 z-[150] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
177
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95">
178
+ <div className="flex justify-between items-start mb-10">
179
+ <div className="flex items-center gap-4">
180
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center border border-blue-500/20">
181
+ <Plus size={32} />
182
+ </div>
183
+ <div>
184
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Issue <span className="text-blue-500 not-italic">New Card</span></h3>
185
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">Quantum Foundry Authorization</p>
186
+ </div>
187
+ </div>
188
+ <button onClick={() => setIsModalOpen(false)} className="p-3 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
189
+ </div>
190
+ <div className="space-y-8">
191
+ <div className="grid grid-cols-2 gap-4">
192
+ <button
193
+ onClick={() => setNewCardType('Rewards+')}
194
+ className={`p-6 rounded-2xl border transition-all text-left group ${newCardType === 'Rewards+' ? 'bg-blue-600/10 border-blue-600 shadow-xl shadow-blue-900/10' : 'bg-black border-zinc-900 opacity-50'}`}
195
+ >
196
+ <p className="text-white font-black text-xs uppercase italic mb-1">Rewards+</p>
197
+ <p className="text-[8px] text-zinc-500 uppercase font-black tracking-widest">5% Cloud Credit</p>
198
+ </button>
199
+ <button
200
+ onClick={() => setNewCardType('Platinum Founders')}
201
+ className={`p-6 rounded-2xl border transition-all text-left group ${newCardType === 'Platinum Founders' ? 'bg-blue-600/10 border-blue-600 shadow-xl shadow-blue-900/10' : 'bg-black border-zinc-900 opacity-50'}`}
202
+ >
203
+ <p className="text-white font-black text-xs uppercase italic mb-1">Founder Elite</p>
204
+ <p className="text-[8px] text-zinc-500 uppercase font-black tracking-widest">High Yield Sweep</p>
205
+ </button>
206
+ </div>
207
+ <button
208
+ onClick={handleIssueCard}
209
+ disabled={isIssuing}
210
+ className="w-full py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-[0.4em] transition-all flex items-center justify-center gap-4 shadow-2xl shadow-blue-900/40"
211
+ >
212
+ {isIssuing ? <Loader2 className="animate-spin" size={22} /> : <CheckCircle2 size={22} />}
213
+ <span>{isIssuing ? 'ENCRYPTING NODE...' : 'INITIALIZE INSTRUMENT'}</span>
214
+ </button>
215
+ </div>
216
+ </div>
217
+ </div>
218
+ )}
219
+
220
+ {/* Limits Modal */}
221
+ {isLimitsOpen && (
222
+ <div className="fixed inset-0 z-[150] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
223
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95">
224
+ <div className="flex justify-between items-start mb-10">
225
+ <div className="flex items-center gap-4">
226
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center border border-blue-500/20">
227
+ <Settings2 size={28} />
228
+ </div>
229
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Adjust <span className="text-blue-500 not-italic">Threshold</span></h3>
230
+ </div>
231
+ <button onClick={() => setIsLimitsOpen(false)} className="p-3 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
232
+ </div>
233
+ <div className="space-y-10">
234
+ <div className="space-y-2">
235
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Maximum Authorized Velocity (USD)</label>
236
+ <input
237
+ type="number"
238
+ value={tempLimit}
239
+ onChange={(e) => setTempLimit(e.target.value)}
240
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500 rounded-2xl py-5 px-6 text-white font-mono text-2xl outline-none transition-all shadow-inner"
241
+ placeholder="0.00"
242
+ />
243
+ </div>
244
+ <button
245
+ onClick={updateLimit}
246
+ className="w-full py-5 bg-white hover:bg-zinc-200 text-black rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all shadow-2xl shadow-white/10"
247
+ >
248
+ Finalize Registry Update
249
+ </button>
250
+ </div>
251
+ </div>
252
+ </div>
253
+ )}
254
+
255
+ {/* Regions Modal */}
256
+ {isRegionsOpen && activeCardId && (
257
+ <div className="fixed inset-0 z-[150] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
258
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-xl rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95">
259
+ <div className="flex justify-between items-start mb-10">
260
+ <div className="flex items-center gap-4">
261
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center border border-blue-500/20">
262
+ <Globe size={28} />
263
+ </div>
264
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Routing <span className="text-blue-500 not-italic">Policy</span></h3>
265
+ </div>
266
+ <button onClick={() => setIsRegionsOpen(false)} className="p-3 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
267
+ </div>
268
+ <div className="grid grid-cols-2 gap-4">
269
+ {['US', 'EU', 'UK', 'SG', 'JP', 'AU', 'CA', 'BR'].map(reg => (
270
+ <button
271
+ key={reg}
272
+ onClick={() => toggleRegion(reg)}
273
+ className={`p-6 rounded-2xl border flex items-center justify-between transition-all group ${
274
+ cards.find(c => c.id === activeCardId)?.regions.includes(reg)
275
+ ? 'bg-blue-600/10 border-blue-600 text-white shadow-xl shadow-blue-900/10'
276
+ : 'bg-black border-zinc-900 text-zinc-700 opacity-50'
277
+ }`}
278
+ >
279
+ <span className="font-black text-xs uppercase tracking-widest">{reg}</span>
280
+ {cards.find(c => c.id === activeCardId)?.regions.includes(reg) && <CheckCircle2 size={16} className="text-blue-500" />}
281
+ </button>
282
+ ))}
283
+ </div>
284
+ <button
285
+ onClick={() => setIsRegionsOpen(false)}
286
+ className="w-full mt-10 py-5 bg-zinc-900 hover:bg-zinc-800 text-white rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all border border-zinc-800"
287
+ >
288
+ Update Global Routing Node
289
+ </button>
290
+ </div>
291
+ </div>
292
+ )}
293
+ </div>
294
+ );
295
+ };
296
+
297
+ export default Cards;
views/Connectivity.tsx ADDED
@@ -0,0 +1,158 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import {
4
+ Activity,
5
+ RefreshCw,
6
+ Terminal,
7
+ ShieldCheck,
8
+ AlertCircle,
9
+ Database,
10
+ Link2,
11
+ X,
12
+ Plus,
13
+ Loader2,
14
+ CheckCircle2,
15
+ MoreVertical
16
+ } from 'lucide-react';
17
+ import { Connection } from '../types/index.ts';
18
+
19
+ const MOCK_CONNS: Connection[] = [
20
+ { id: 'CON-101', vendorCustomerId: 'CUST-A992', entity: 'JPM_USA', status: 'CONNECTED', lastSyncedAt: '2024-03-31T12:00:00Z' },
21
+ { id: 'CON-102', vendorCustomerId: 'CUST-D102', entity: 'DB_EURO', status: 'CONNECTED', lastSyncedAt: '2024-03-31T11:55:00Z' },
22
+ { id: 'CON-103', vendorCustomerId: 'CUST-L551', entity: 'LUMINA_INTERNAL', status: 'ERROR', lastSyncedAt: '2024-03-30T22:00:00Z' },
23
+ ];
24
+
25
+ const Connectivity: React.FC = () => {
26
+ const [conns, setConns] = useState<Connection[]>(MOCK_CONNS);
27
+ const [isModalOpen, setIsModalOpen] = useState(false);
28
+ const [provisioning, setProvisioning] = useState(false);
29
+ const [newVendor, setNewVendor] = useState({ entity: 'STRIPE_GLOBAL', id: 'CUST-X' });
30
+
31
+ const handleProvision = () => {
32
+ setProvisioning(true);
33
+ setTimeout(() => {
34
+ const newConn: Connection = {
35
+ id: `CON-${Math.floor(200 + Math.random() * 800)}`,
36
+ vendorCustomerId: newVendor.id,
37
+ entity: newVendor.entity,
38
+ status: 'CONNECTED',
39
+ lastSyncedAt: new Date().toISOString()
40
+ };
41
+ setConns([newConn, ...conns]);
42
+ setProvisioning(false);
43
+ setIsModalOpen(false);
44
+ }, 2000);
45
+ };
46
+
47
+ return (
48
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20">
49
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
50
+ <div>
51
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">System <span className="text-blue-500 not-italic">Fabric</span></h2>
52
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Machine-to-Machine Integration Node Monitoring</p>
53
+ </div>
54
+ <button
55
+ onClick={() => setIsModalOpen(true)}
56
+ className="flex items-center space-x-2 px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-[1.8rem] font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/40"
57
+ >
58
+ <Link2 size={18} />
59
+ <span>Provision New Vendor</span>
60
+ </button>
61
+ </div>
62
+
63
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
64
+ {conns.map(conn => (
65
+ <div key={conn.id} className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 relative overflow-hidden group hover:border-zinc-700 transition-all shadow-2xl">
66
+ <div className="absolute top-0 right-0 p-8 opacity-5 group-hover:opacity-10 transition-opacity">
67
+ <Database size={100} />
68
+ </div>
69
+
70
+ <div className="flex justify-between items-start mb-8 relative z-10">
71
+ <div className={`p-4 rounded-2xl ${conn.status === 'CONNECTED' ? 'bg-emerald-500/10 text-emerald-500' : 'bg-rose-500/10 text-rose-500'}`}>
72
+ {conn.status === 'CONNECTED' ? <ShieldCheck size={24} /> : <AlertCircle size={24} />}
73
+ </div>
74
+ <div className="text-right">
75
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-1">Status</p>
76
+ <p className={`text-xs font-black uppercase tracking-widest ${conn.status === 'CONNECTED' ? 'text-emerald-500' : 'text-rose-500'}`}>{conn.status}</p>
77
+ </div>
78
+ </div>
79
+
80
+ <div className="space-y-6 relative z-10">
81
+ <div>
82
+ <h4 className="text-lg font-black text-white uppercase italic tracking-tighter mb-1">{conn.entity}</h4>
83
+ <p className="text-[10px] text-zinc-500 font-mono font-bold uppercase">Vendor ID: {conn.vendorCustomerId}</p>
84
+ </div>
85
+ <div className="h-px bg-zinc-900"></div>
86
+ <div className="flex justify-between items-center text-[10px] font-black uppercase tracking-widest text-zinc-600">
87
+ <span>Handshake ID</span>
88
+ <span className="text-zinc-400 mono">{conn.id}</span>
89
+ </div>
90
+ </div>
91
+
92
+ <div className="mt-8 pt-8 border-t border-zinc-900 flex justify-between items-center opacity-0 group-hover:opacity-100 transition-opacity">
93
+ <span className="text-[9px] font-black uppercase text-zinc-600 tracking-widest">Last Sync: {new Date(conn.lastSyncedAt).toLocaleTimeString()}</span>
94
+ <button className="text-zinc-500 hover:text-white"><MoreVertical size={16} /></button>
95
+ </div>
96
+ </div>
97
+ ))}
98
+ </div>
99
+
100
+ {isModalOpen && (
101
+ <div className="fixed inset-0 z-[150] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
102
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95">
103
+ <div className="flex justify-between items-start mb-10">
104
+ <div className="flex items-center gap-4">
105
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 rounded-3xl flex items-center justify-center border border-blue-500/20">
106
+ <Link2 size={32} />
107
+ </div>
108
+ <div>
109
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Vendor <span className="text-blue-500 not-italic">Handshake</span></h3>
110
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">Fabric Extension Module</p>
111
+ </div>
112
+ </div>
113
+ <button onClick={() => setIsModalOpen(false)} className="p-3 text-zinc-600 hover:text-white transition-colors"><X size={24}/></button>
114
+ </div>
115
+
116
+ <div className="space-y-8">
117
+ <div className="space-y-4">
118
+ <div className="space-y-2">
119
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Vendor Provider</label>
120
+ <input
121
+ value={newVendor.entity}
122
+ onChange={e => setNewVendor({...newVendor, entity: e.target.value})}
123
+ className="w-full bg-black border border-zinc-800 rounded-2xl py-4 px-6 text-white text-xs outline-none focus:border-blue-500 transition-all font-bold uppercase tracking-widest"
124
+ placeholder="e.g. STRIPE_US"
125
+ />
126
+ </div>
127
+ <div className="space-y-2">
128
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Corporate Client ID</label>
129
+ <input
130
+ value={newVendor.id}
131
+ onChange={e => setNewVendor({...newVendor, id: e.target.value})}
132
+ className="w-full bg-black border border-zinc-800 rounded-2xl py-4 px-6 text-white text-xs outline-none focus:border-blue-500 transition-all font-mono"
133
+ placeholder="CUST-..."
134
+ />
135
+ </div>
136
+ </div>
137
+
138
+ <div className="p-6 bg-blue-500/5 border border-blue-500/10 rounded-3xl">
139
+ <p className="text-[10px] font-black text-zinc-500 leading-relaxed uppercase tracking-widest">Establishing a secure tunnel via RSA-OAEP. Metadata persistence will be shredded post-handshake.</p>
140
+ </div>
141
+
142
+ <button
143
+ onClick={handleProvision}
144
+ disabled={provisioning}
145
+ className="w-full py-6 bg-blue-600 hover:bg-blue-500 text-white rounded-[2.2rem] font-black text-[11px] uppercase tracking-[0.4em] transition-all shadow-xl shadow-blue-900/40 flex items-center justify-center gap-4"
146
+ >
147
+ {provisioning ? <Loader2 className="animate-spin" size={20} /> : <CheckCircle2 size={20} />}
148
+ {provisioning ? 'FORGING FABRIC...' : 'INITIALIZE Integration'}
149
+ </button>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ )}
154
+ </div>
155
+ );
156
+ };
157
+
158
+ export default Connectivity;
views/Counterparties.tsx ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
4
+ import { useNavigate } from 'react-router-dom';
5
+ import {
6
+ Users,
7
+ Search,
8
+ Plus,
9
+ Mail,
10
+ Globe,
11
+ ShieldCheck,
12
+ MoreHorizontal,
13
+ Loader2,
14
+ X,
15
+ ArrowRight,
16
+ Building2,
17
+ Calendar,
18
+ CreditCard,
19
+ ExternalLink
20
+ } from 'lucide-react';
21
+ import { Counterparty } from '../types/index';
22
+
23
+ const MOCK_CP: Counterparty[] = [
24
+ { id: 'CP-8801', name: 'Global Logistics Corp', email: 'billing@globallogistics.io', status: 'ACTIVE', createdAt: '2023-11-20', accounts: [{ id: 'ACC-1', accountType: 'CHECKING', accountNumber: '•••• 4421' }] },
25
+ { id: 'CP-9902', name: 'Neural Dynamics Research', email: 'treasury@neuraldynamics.ai', status: 'PENDING', createdAt: '2024-01-05', accounts: [] },
26
+ { id: 'CP-1105', name: 'Skyline Real Estate', email: 'payments@skyline.co', status: 'ACTIVE', createdAt: '2023-08-12', accounts: [{ id: 'ACC-2', accountType: 'SAVINGS', accountNumber: '•••• 1022' }] },
27
+ ];
28
+
29
+ const Counterparties: React.FC = () => {
30
+ const navigate = useNavigate();
31
+ const [partners, setPartners] = useState(MOCK_CP);
32
+ const [showModal, setShowModal] = useState(false);
33
+ const [showProfile, setShowProfile] = useState(false);
34
+ const [selectedPartner, setSelectedPartner] = useState<Counterparty | null>(null);
35
+ const [connecting, setConnecting] = useState(false);
36
+ const [newPartner, setNewPartner] = useState({ name: '', email: '' });
37
+
38
+ const registerPartner = () => {
39
+ if (!newPartner.name || !newPartner.email) return;
40
+ setConnecting(true);
41
+ setTimeout(() => {
42
+ const partner: Counterparty = {
43
+ id: `CP-${Math.floor(1000 + Math.random() * 9000)}`,
44
+ name: newPartner.name,
45
+ email: newPartner.email,
46
+ status: 'PENDING',
47
+ createdAt: new Date().toISOString().split('T')[0],
48
+ accounts: []
49
+ };
50
+ setPartners([...partners, partner]);
51
+ setConnecting(false);
52
+ setShowModal(false);
53
+ setNewPartner({ name: '', email: '' });
54
+ }, 1500);
55
+ };
56
+
57
+ const openProfile = (partner: Counterparty) => {
58
+ setSelectedPartner(partner);
59
+ setShowProfile(true);
60
+ };
61
+
62
+ const handleSendFunds = (partner: Counterparty) => {
63
+ // Navigate to disbursements with partner intent
64
+ navigate('/payments', { state: { selectedPayeeId: partner.id } });
65
+ };
66
+
67
+ return (
68
+ <div className="space-y-10 animate-in fade-in duration-700">
69
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
70
+ <div>
71
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Partner <span className="text-blue-500 not-italic">CRM</span></h2>
72
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Managed Third-Party Entities & KYC Handshakes</p>
73
+ </div>
74
+ <button
75
+ onClick={() => setShowModal(true)}
76
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/30"
77
+ >
78
+ <Plus size={14} />
79
+ <span>Register Counterparty</span>
80
+ </button>
81
+ </div>
82
+
83
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
84
+ <div className="p-10 border-b border-zinc-900 flex flex-col md:flex-row justify-between items-center bg-black/20 gap-6">
85
+ <div className="flex items-center space-x-4">
86
+ <div className="p-3 bg-blue-500/10 text-blue-500 rounded-2xl">
87
+ <Users size={20} />
88
+ </div>
89
+ <h3 className="text-white font-black uppercase tracking-[0.2em] italic">Active Directory</h3>
90
+ </div>
91
+ <div className="relative w-full md:w-80">
92
+ <Search className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600" size={14} />
93
+ <input
94
+ placeholder="Search Partners..."
95
+ className="w-full bg-black border border-zinc-800 rounded-xl py-3 pl-10 pr-4 text-[10px] font-black uppercase tracking-widest text-white outline-none focus:border-blue-500/50 transition-all"
96
+ />
97
+ </div>
98
+ </div>
99
+ <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-px bg-zinc-900/50">
100
+ {partners.map(cp => (
101
+ <div key={cp.id} className="bg-zinc-950 p-10 hover:bg-white/[0.02] transition-all group border border-transparent hover:border-zinc-800 relative overflow-hidden">
102
+ <div className="absolute top-0 right-0 p-4 opacity-0 group-hover:opacity-5 transition-opacity">
103
+ <Building2 size={120} />
104
+ </div>
105
+
106
+ <div className="relative z-10">
107
+ <div className="flex justify-between items-start mb-10">
108
+ <div className="w-16 h-16 rounded-2xl bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-600 group-hover:text-blue-400 transition-colors shadow-2xl">
109
+ <Globe size={32} />
110
+ </div>
111
+ <div className={`px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest ${cp.status === 'ACTIVE' ? 'bg-emerald-500/10 text-emerald-500' : 'bg-amber-500/10 text-amber-500'}`}>
112
+ {cp.status}
113
+ </div>
114
+ </div>
115
+ <h4 className="text-xl font-black text-white uppercase italic tracking-tighter mb-1">{cp.name}</h4>
116
+ <p className="text-[10px] text-zinc-500 font-bold uppercase tracking-widest mb-10 truncate">{cp.email}</p>
117
+ <div className="flex gap-4">
118
+ <button onClick={() => openProfile(cp)} className="flex-1 py-4 bg-zinc-900 text-white rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all border border-zinc-800 hover:bg-zinc-800 flex items-center justify-center gap-2">
119
+ Profile
120
+ </button>
121
+ <button onClick={() => handleSendFunds(cp)} className="flex-1 py-4 bg-blue-600 text-white rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all hover:bg-blue-500 flex items-center justify-center gap-2">
122
+ Send Funds
123
+ </button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ ))}
128
+ </div>
129
+ </div>
130
+
131
+ {/* Profile Modal */}
132
+ {showProfile && selectedPartner && (
133
+ <div className="fixed inset-0 z-[110] flex items-center justify-center p-6 backdrop-blur-md bg-black/70">
134
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-2xl rounded-[3rem] p-10 shadow-2xl animate-in zoom-in-95 duration-300">
135
+ <div className="flex justify-between items-start mb-10 pb-8 border-b border-zinc-900">
136
+ <div className="flex items-center gap-6">
137
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 rounded-3xl flex items-center justify-center">
138
+ <Building2 size={32} />
139
+ </div>
140
+ <div>
141
+ <h3 className="text-3xl font-black text-white italic tracking-tighter uppercase">{selectedPartner.name}</h3>
142
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest mt-1">Institutional Entity Record • {selectedPartner.id}</p>
143
+ </div>
144
+ </div>
145
+ <button onClick={() => setShowProfile(false)} className="p-2 text-zinc-500 hover:text-white transition-colors"><X size={24} /></button>
146
+ </div>
147
+
148
+ <div className="space-y-8">
149
+ <div className="grid grid-cols-2 gap-8">
150
+ <div className="space-y-6">
151
+ <div>
152
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-2">Entity Email</p>
153
+ <p className="text-white font-bold flex items-center gap-2">
154
+ <Mail size={14} className="text-blue-500" />
155
+ {selectedPartner.email}
156
+ </p>
157
+ </div>
158
+ <div>
159
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-2">Relationship Established</p>
160
+ <p className="text-white font-bold flex items-center gap-2">
161
+ <Calendar size={14} className="text-blue-500" />
162
+ {new Date(selectedPartner.createdAt).toLocaleDateString()}
163
+ </p>
164
+ </div>
165
+ </div>
166
+ <div className="space-y-6">
167
+ <div>
168
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-2">KYC Verification Status</p>
169
+ <div className="flex items-center gap-2">
170
+ <div className={`w-2 h-2 rounded-full ${selectedPartner.status === 'ACTIVE' ? 'bg-emerald-500 animate-pulse' : 'bg-amber-500'}`}></div>
171
+ <span className={`${selectedPartner.status === 'ACTIVE' ? 'text-emerald-500' : 'text-amber-500'} font-black text-[10px] uppercase tracking-widest`}>
172
+ {selectedPartner.status === 'ACTIVE' ? 'Identity Verified' : 'Handshake Pending'}
173
+ </span>
174
+ </div>
175
+ </div>
176
+ <div>
177
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-2">Primary Registry Type</p>
178
+ <p className="text-white font-black italic uppercase tracking-tighter">FDX_CORPORATE_NODE</p>
179
+ </div>
180
+ </div>
181
+ </div>
182
+
183
+ <div className="p-6 bg-black border border-zinc-800 rounded-3xl space-y-4">
184
+ <p className="text-[9px] font-black text-zinc-500 uppercase tracking-widest flex items-center gap-2">
185
+ <CreditCard size={12} />
186
+ Linked Financial Handshakes
187
+ </p>
188
+ {selectedPartner.accounts && selectedPartner.accounts.length > 0 ? (
189
+ selectedPartner.accounts.map(acc => (
190
+ <div key={acc.id} className="flex justify-between items-center bg-zinc-900/50 p-4 rounded-xl border border-zinc-800">
191
+ <div>
192
+ <p className="text-white text-xs font-bold uppercase">{acc.accountType} Account</p>
193
+ <p className="text-zinc-500 text-[10px] font-mono mt-0.5">{acc.accountNumber}</p>
194
+ </div>
195
+ <span className="text-[8px] font-black uppercase text-emerald-500 border border-emerald-500/20 px-2 py-0.5 rounded">RTP Enabled</span>
196
+ </div>
197
+ ))
198
+ ) : (
199
+ <div className="bg-zinc-900/50 p-4 rounded-xl border border-dashed border-zinc-800 text-center">
200
+ <p className="text-zinc-600 text-[9px] font-black uppercase tracking-widest">No verified external accounts found</p>
201
+ </div>
202
+ )}
203
+ </div>
204
+
205
+ <div className="flex gap-4">
206
+ <button className="flex-1 py-5 bg-zinc-900 hover:bg-zinc-800 text-white rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all border border-zinc-800 flex items-center justify-center gap-3">
207
+ <ExternalLink size={18} />
208
+ Open Documents
209
+ </button>
210
+ <button
211
+ onClick={() => handleSendFunds(selectedPartner)}
212
+ className="flex-1 py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all flex items-center justify-center gap-3 shadow-xl"
213
+ >
214
+ Send Disbursement
215
+ </button>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ )}
221
+
222
+ {/* Registration Modal */}
223
+ {showModal && (
224
+ <div className="fixed inset-0 z-[100] flex items-center justify-center p-6 backdrop-blur-sm bg-black/60">
225
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3rem] p-10 shadow-2xl animate-in zoom-in-95">
226
+ <div className="flex justify-between items-start mb-10">
227
+ <div className="flex items-center gap-4">
228
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center">
229
+ <ShieldCheck size={28} />
230
+ </div>
231
+ <div>
232
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Add <span className="text-blue-500 not-italic">Partner</span></h3>
233
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">KYC/KYB Initialization</p>
234
+ </div>
235
+ </div>
236
+ <button onClick={() => setShowModal(false)} className="p-2 text-zinc-600 hover:text-white"><X size={24} /></button>
237
+ </div>
238
+
239
+ <div className="space-y-6">
240
+ <div className="space-y-2">
241
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Entity Name</label>
242
+ <input
243
+ value={newPartner.name}
244
+ onChange={(e) => setNewPartner({...newPartner, name: e.target.value})}
245
+ placeholder="Legal Company Name"
246
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-4 px-4 text-white font-bold outline-none transition-all"
247
+ />
248
+ </div>
249
+ <div className="space-y-2">
250
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Treasury Email</label>
251
+ <input
252
+ value={newPartner.email}
253
+ onChange={(e) => setNewPartner({...newPartner, email: e.target.value})}
254
+ placeholder="treasury@partner.io"
255
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-4 px-4 text-white font-bold outline-none transition-all"
256
+ />
257
+ </div>
258
+
259
+ <button
260
+ onClick={registerPartner}
261
+ disabled={connecting || !newPartner.name}
262
+ className="w-full mt-4 py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-4 shadow-xl shadow-blue-900/30"
263
+ >
264
+ {connecting ? <Loader2 className="animate-spin" size={20} /> : <ArrowRight size={20} />}
265
+ <span>{connecting ? 'Dispatching Handshake...' : 'Register Entity'}</span>
266
+ </button>
267
+ </div>
268
+ </div>
269
+ </div>
270
+ )}
271
+ </div>
272
+ );
273
+ };
274
+
275
+ export default Counterparties;
views/CryptView.tsx ADDED
@@ -0,0 +1,348 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ Hammer,
4
+ Sparkles,
5
+ ShieldCheck,
6
+ Terminal,
7
+ Wallet,
8
+ Key,
9
+ Loader2,
10
+ Eye,
11
+ Image as ImageIcon,
12
+ Zap,
13
+ Globe,
14
+ Share2,
15
+ ExternalLink,
16
+ Info,
17
+ AlertCircle,
18
+ // Added missing X icon import
19
+ X
20
+ } from 'lucide-react';
21
+ import { nftService, GeneratedNFT } from '../services/nftService.ts';
22
+
23
+ declare global {
24
+ interface Window {
25
+ ethereum?: any;
26
+ }
27
+ }
28
+
29
+ const CryptView: React.FC = () => {
30
+ const [prompt, setPrompt] = useState('');
31
+ const [isSynthesizing, setIsSynthesizing] = useState(false);
32
+ const [isMinting, setIsMinting] = useState(false);
33
+ const [nft, setNft] = useState<GeneratedNFT | null>(null);
34
+ const [openSeaKey, setOpenSeaKey] = useState('');
35
+ const [walletAddress, setWalletAddress] = useState('');
36
+ const [mintLogs, setMintLogs] = useState<string[]>([]);
37
+ const [walletConnected, setWalletConnected] = useState(false);
38
+ const [isConnecting, setIsConnecting] = useState(false);
39
+ const [error, setError] = useState<string | null>(null);
40
+
41
+ const connectWallet = async () => {
42
+ if (typeof window.ethereum === 'undefined') {
43
+ setError("MetaMask not detected. Please install the browser extension to interact with the foundry.");
44
+ return;
45
+ }
46
+
47
+ setIsConnecting(true);
48
+ setError(null);
49
+ try {
50
+ const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' });
51
+ if (accounts.length > 0) {
52
+ setWalletAddress(accounts[0]);
53
+ setWalletConnected(true);
54
+ }
55
+ } catch (err: any) {
56
+ console.error("Wallet Connection Failed:", err);
57
+ setError(err.message || "Failed to establish neural handshake with wallet.");
58
+ } finally {
59
+ setIsConnecting(false);
60
+ }
61
+ };
62
+
63
+ useEffect(() => {
64
+ const checkConnection = async () => {
65
+ if (window.ethereum) {
66
+ const accounts = await window.ethereum.request({ method: 'eth_accounts' });
67
+ if (accounts.length > 0) {
68
+ setWalletAddress(accounts[0]);
69
+ setWalletConnected(true);
70
+ }
71
+ }
72
+ };
73
+ checkConnection();
74
+
75
+ if (window.ethereum) {
76
+ window.ethereum.on('accountsChanged', (accounts: string[]) => {
77
+ if (accounts.length > 0) {
78
+ setWalletAddress(accounts[0]);
79
+ setWalletConnected(true);
80
+ } else {
81
+ setWalletAddress('');
82
+ setWalletConnected(false);
83
+ }
84
+ });
85
+ }
86
+ }, []);
87
+
88
+ const handleSynthesize = async () => {
89
+ if (!prompt.trim()) return;
90
+ setIsSynthesizing(true);
91
+ setNft(null);
92
+ setMintLogs([]);
93
+ setError(null);
94
+
95
+ const [imageUrl, metadata] = await Promise.all([
96
+ nftService.generateImage(prompt),
97
+ nftService.generateMetadata(prompt)
98
+ ]);
99
+
100
+ if (imageUrl) {
101
+ setNft({
102
+ name: metadata.name || "Neural Artifact",
103
+ description: metadata.description || "Synthesized by Lumina Oracle",
104
+ imageUrl,
105
+ traits: metadata.traits || []
106
+ });
107
+ } else {
108
+ setError("Asset synthesis failed. Please adjust your prompt parameters.");
109
+ }
110
+ setIsSynthesizing(false);
111
+ };
112
+
113
+ const handleMint = async () => {
114
+ if (!nft || !openSeaKey || !walletConnected) return;
115
+ setIsMinting(true);
116
+ setMintLogs(["Starting deployment sequence..."]);
117
+
118
+ const steps = await nftService.mintToOpenSea(nft, openSeaKey, walletAddress);
119
+
120
+ for (const step of steps) {
121
+ await new Promise(r => setTimeout(r, 800));
122
+ setMintLogs(prev => [...prev, step]);
123
+ }
124
+ setIsMinting(false);
125
+ };
126
+
127
+ const shortenAddress = (addr: string) => `${addr.substring(0, 6)}...${addr.substring(addr.length - 4)}`;
128
+
129
+ return (
130
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20">
131
+ {/* Header */}
132
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
133
+ <div>
134
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Quantum <span className="text-blue-500 not-italic">Crypt</span></h2>
135
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Neural Asset Synthesis & Blockchain Deployment</p>
136
+ </div>
137
+ <div className="flex gap-4">
138
+ <div className="px-6 py-3 bg-zinc-950 border border-zinc-800 rounded-2xl flex items-center gap-4 shadow-xl">
139
+ <div className="flex items-center gap-2">
140
+ <div className={`w-2 h-2 rounded-full ${walletConnected ? 'bg-emerald-500 animate-pulse' : 'bg-rose-500'}`}></div>
141
+ <span className="text-[10px] font-black text-zinc-500 uppercase tracking-widest">
142
+ Wallet: {walletConnected ? shortenAddress(walletAddress) : 'Disconnected'}
143
+ </span>
144
+ </div>
145
+ </div>
146
+ {!walletConnected && (
147
+ <button
148
+ onClick={connectWallet}
149
+ disabled={isConnecting}
150
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-lg shadow-blue-900/30 disabled:opacity-50"
151
+ >
152
+ {isConnecting ? <Loader2 size={14} className="animate-spin" /> : <Wallet size={14} />}
153
+ <span>{isConnecting ? 'Initializing...' : 'Connect MetaMask'}</span>
154
+ </button>
155
+ )}
156
+ </div>
157
+ </div>
158
+
159
+ {error && (
160
+ <div className="p-4 bg-rose-500/10 border border-rose-500/20 rounded-2xl flex items-center gap-4 animate-in slide-in-from-top-2">
161
+ <AlertCircle size={20} className="text-rose-500 shrink-0" />
162
+ <p className="text-xs font-bold text-rose-200">{error}</p>
163
+ <button onClick={() => setError(null)} className="ml-auto p-1 hover:bg-rose-500/10 rounded-lg text-rose-500">
164
+ <X size={16} />
165
+ </button>
166
+ </div>
167
+ )}
168
+
169
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-10">
170
+ {/* Left: Configuration & Foundry */}
171
+ <div className="lg:col-span-1 space-y-8">
172
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 shadow-2xl space-y-8">
173
+ <div className="flex items-center gap-3 mb-2">
174
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-xl">
175
+ <Key size={18} />
176
+ </div>
177
+ <h3 className="text-white font-black text-xs uppercase tracking-widest italic">Integration Layer</h3>
178
+ </div>
179
+
180
+ <div className="space-y-6">
181
+ <div className="space-y-2">
182
+ <label className="text-[9px] font-black text-zinc-600 uppercase tracking-widest ml-1">OpenSea API Key</label>
183
+ <input
184
+ type="password"
185
+ value={openSeaKey}
186
+ onChange={e => setOpenSeaKey(e.target.value)}
187
+ placeholder="X-API-KEY-..."
188
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-3 px-4 text-white text-xs outline-none transition-all placeholder:text-zinc-800 font-mono"
189
+ />
190
+ </div>
191
+
192
+ <div className="space-y-2">
193
+ <label className="text-[9px] font-black text-zinc-600 uppercase tracking-widest ml-1">Synthesis Prompt</label>
194
+ <textarea
195
+ value={prompt}
196
+ onChange={e => setPrompt(e.target.value)}
197
+ placeholder="e.g. Cybernetic obsidian eagle with neon circuits..."
198
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-xl py-4 px-4 text-white text-xs outline-none transition-all placeholder:text-zinc-800 font-bold min-h-[120px] resize-none"
199
+ />
200
+ </div>
201
+
202
+ <button
203
+ onClick={handleSynthesize}
204
+ disabled={isSynthesizing || !prompt}
205
+ className="w-full py-4 bg-zinc-900 hover:bg-zinc-100 hover:text-black text-white rounded-2xl font-black text-[10px] uppercase tracking-[0.2em] transition-all border border-zinc-800 flex items-center justify-center gap-3 disabled:opacity-50"
206
+ >
207
+ {isSynthesizing ? <Loader2 className="animate-spin" size={16} /> : <Sparkles size={16} />}
208
+ <span>{isSynthesizing ? 'Synthesizing Neural Path...' : 'Synthesize Artifact'}</span>
209
+ </button>
210
+ </div>
211
+ </div>
212
+
213
+ {/* Minting Log Terminal */}
214
+ <div className="bg-black border border-zinc-900 rounded-[2.5rem] overflow-hidden shadow-2xl h-[300px] flex flex-col">
215
+ <div className="bg-zinc-900/80 px-6 py-3 border-b border-zinc-800 flex justify-between items-center">
216
+ <div className="flex items-center gap-2">
217
+ <Terminal size={12} className="text-emerald-500" />
218
+ <span className="text-[9px] font-black uppercase tracking-widest text-zinc-500">Chain Trace Log</span>
219
+ </div>
220
+ <div className="flex gap-1.5">
221
+ <div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
222
+ <div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
223
+ <div className="w-1.5 h-1.5 rounded-full bg-zinc-800"></div>
224
+ </div>
225
+ </div>
226
+ <div className="flex-1 p-6 font-mono text-[10px] overflow-y-auto custom-scrollbar space-y-1.5">
227
+ {mintLogs.length === 0 ? (
228
+ <div className="text-zinc-700 italic">Waiting for forge command...</div>
229
+ ) : (
230
+ mintLogs.map((log, i) => (
231
+ <div key={i} className={`${log.includes('Successfully') ? 'text-emerald-500' : 'text-zinc-500'}`}>
232
+ <span className="text-zinc-800 mr-2">[{new Date().toLocaleTimeString([], { hour12: false })}]</span>
233
+ {log}
234
+ </div>
235
+ ))
236
+ )}
237
+ {isMinting && <div className="text-blue-500 animate-pulse font-black">Executing Quantum Block...</div>}
238
+ </div>
239
+ </div>
240
+ </div>
241
+
242
+ {/* Center/Right: Preview & Metadata */}
243
+ <div className="lg:col-span-2 space-y-8">
244
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 shadow-2xl relative overflow-hidden group">
245
+ <div className="absolute inset-0 bg-[radial-gradient(circle_at_50%_0%,_#1e1b4b_0%,_transparent_50%)] opacity-30"></div>
246
+
247
+ {!nft && !isSynthesizing ? (
248
+ <div className="h-[500px] flex flex-col items-center justify-center text-center space-y-6 relative z-10 border-2 border-dashed border-zinc-900 rounded-[2rem]">
249
+ <div className="w-20 h-20 bg-zinc-900 rounded-full flex items-center justify-center text-zinc-700">
250
+ <ImageIcon size={40} />
251
+ </div>
252
+ <div>
253
+ <h3 className="text-zinc-600 font-black uppercase tracking-[0.4em] text-sm">Foundry Idle</h3>
254
+ <p className="text-zinc-700 text-xs mt-2 font-medium">Input parameters to initialize synthesis.</p>
255
+ </div>
256
+ </div>
257
+ ) : isSynthesizing ? (
258
+ <div className="h-[500px] flex flex-col items-center justify-center text-center space-y-8 relative z-10">
259
+ <div className="relative">
260
+ <div className="w-48 h-48 rounded-full border-4 border-blue-500/20 border-t-blue-500 animate-spin"></div>
261
+ <div className="absolute inset-0 flex items-center justify-center">
262
+ <Zap size={48} className="text-blue-500 animate-pulse" />
263
+ </div>
264
+ </div>
265
+ <div className="space-y-2">
266
+ <h3 className="text-white font-black uppercase tracking-[0.4em] italic animate-pulse">Forging Neural Layers</h3>
267
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-widest">Estimated completion: 8.2s</p>
268
+ </div>
269
+ </div>
270
+ ) : nft ? (
271
+ <div className="flex flex-col lg:flex-row gap-12 relative z-10 animate-in zoom-in-95 duration-700">
272
+ <div className="lg:w-1/2">
273
+ <div className="relative group">
274
+ <div className="absolute -inset-1 bg-gradient-to-r from-blue-600 to-indigo-600 rounded-[2.5rem] blur opacity-25 group-hover:opacity-75 transition duration-1000 group-hover:duration-200"></div>
275
+ <img
276
+ src={nft.imageUrl}
277
+ className="relative w-full aspect-square rounded-[2rem] bg-black border border-zinc-800 object-cover shadow-2xl"
278
+ alt="Synthesized NFT"
279
+ />
280
+ <div className="absolute top-4 right-4 flex gap-2">
281
+ <div className="bg-black/60 backdrop-blur-md px-3 py-1.5 rounded-full border border-white/10 flex items-center gap-2">
282
+ <ShieldCheck size={12} className="text-emerald-500" />
283
+ <span className="text-[8px] font-black text-white uppercase tracking-widest">AI Verified</span>
284
+ </div>
285
+ </div>
286
+ </div>
287
+ </div>
288
+
289
+ <div className="lg:w-1/2 flex flex-col justify-between">
290
+ <div className="space-y-8">
291
+ <div>
292
+ <span className="px-3 py-1 bg-blue-600/10 text-blue-500 border border-blue-500/20 rounded-full text-[9px] font-black uppercase tracking-widest mb-4 inline-block">Unminted Asset</span>
293
+ <h3 className="text-4xl font-black text-white italic tracking-tighter uppercase mb-2 leading-none">{nft.name}</h3>
294
+ <p className="text-zinc-500 text-sm leading-relaxed font-medium italic">"{nft.description}"</p>
295
+ </div>
296
+
297
+ <div className="grid grid-cols-2 gap-4">
298
+ {nft.traits.map((trait, i) => (
299
+ <div key={i} className="p-4 bg-black/40 border border-zinc-800 rounded-2xl">
300
+ <p className="text-[8px] font-black text-zinc-600 uppercase tracking-widest mb-1">{trait.trait_type}</p>
301
+ <p className="text-white font-bold text-xs uppercase">{trait.value}</p>
302
+ </div>
303
+ ))}
304
+ </div>
305
+ </div>
306
+
307
+ <div className="pt-10 flex gap-4">
308
+ <button
309
+ onClick={handleMint}
310
+ disabled={isMinting || !openSeaKey || !walletConnected}
311
+ className="flex-1 py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-4 shadow-xl shadow-blue-900/30 disabled:opacity-50"
312
+ >
313
+ {isMinting ? <Loader2 className="animate-spin" size={20} /> : <Hammer size={20} />}
314
+ <span>{isMinting ? 'Casting Block...' : 'Forge to OpenSea'}</span>
315
+ </button>
316
+ <button className="p-5 bg-zinc-900 hover:bg-zinc-800 text-zinc-500 hover:text-white rounded-[2rem] transition-all border border-zinc-800">
317
+ <Share2 size={20} />
318
+ </button>
319
+ </div>
320
+ </div>
321
+ </div>
322
+ ) : null}
323
+ </div>
324
+
325
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 relative overflow-hidden group shadow-2xl">
326
+ <div className="flex flex-col md:flex-row justify-between items-center gap-10">
327
+ <div className="flex items-center gap-8 flex-1 w-full">
328
+ <div className="w-20 h-20 bg-emerald-500/10 rounded-3xl flex items-center justify-center text-emerald-500 shadow-2xl shadow-emerald-500/5">
329
+ <ShieldCheck size={40} />
330
+ </div>
331
+ <div className="flex-1">
332
+ <h4 className="text-xl font-black text-white italic tracking-tighter uppercase mb-1">OpenSea <span className="text-emerald-500 not-italic">Verified Foundry</span></h4>
333
+ <p className="text-zinc-500 text-xs font-medium max-w-md">Lumina Quantum Foundry utilizes dedicated node architecture to ensure gas-optimized minting and instant marketplace visibility.</p>
334
+ </div>
335
+ </div>
336
+ <button className="flex items-center gap-2 text-zinc-600 hover:text-white transition-colors">
337
+ <span className="text-[10px] font-black uppercase tracking-widest">Review Protocol</span>
338
+ <ExternalLink size={14} />
339
+ </button>
340
+ </div>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ </div>
345
+ );
346
+ };
347
+
348
+ export default CryptView;
views/CryptoTerminal.tsx ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useMemo, useEffect } from 'react';
3
+ import {
4
+ PieChart, Pie, Cell, ResponsiveContainer, Tooltip as RechartsTooltip,
5
+ AreaChart, Area, XAxis, YAxis, CartesianGrid
6
+ } from 'recharts';
7
+ import {
8
+ Zap,
9
+ ArrowUpRight,
10
+ Cpu,
11
+ TrendingUp,
12
+ Terminal as TerminalIcon,
13
+ Activity,
14
+ Loader2,
15
+ Plus,
16
+ RefreshCw,
17
+ Pause,
18
+ Play,
19
+ Settings,
20
+ BrainCircuit,
21
+ BarChart3,
22
+ ShieldCheck,
23
+ AlertTriangle
24
+ } from 'lucide-react';
25
+ import { cryptoService } from '../services/cryptoService.ts';
26
+ import { getFinancialAdviceStream } from '../services/geminiService.ts';
27
+ import { CoinMarketData, GlobalData, AIInsight, AITradingBot } from '../types/index.ts';
28
+
29
+ // --- UI Components ---
30
+
31
+ const AIStatusBadge: React.FC<{ status: 'active' | 'learning' | 'processing' | 'thinking' }> = ({ status }) => {
32
+ const colors = {
33
+ active: 'bg-emerald-500',
34
+ learning: 'bg-blue-500',
35
+ processing: 'bg-purple-500',
36
+ thinking: 'bg-cyan-400',
37
+ };
38
+ const text = {
39
+ active: 'Fleet Online',
40
+ learning: 'Adapting Models',
41
+ processing: 'High Compute',
42
+ thinking: 'Synthesizing Alpha',
43
+ }
44
+
45
+ return (
46
+ <div className="flex items-center space-x-2 bg-black px-3 py-1.5 rounded-full border border-zinc-800 shadow-inner">
47
+ <span className={`w-1.5 h-1.5 rounded-full animate-pulse ${colors[status]}`}></span>
48
+ <span className="text-[9px] font-black text-zinc-500 uppercase tracking-widest">{text[status]}</span>
49
+ </div>
50
+ );
51
+ };
52
+
53
+ const TabButton: React.FC<{ active: boolean; onClick: () => void; label: string }> = ({ active, onClick, label }) => (
54
+ <button
55
+ onClick={onClick}
56
+ className={`px-6 py-4 text-[10px] font-black tracking-[0.2em] transition-all duration-300 border-b-2 uppercase whitespace-nowrap ${
57
+ active
58
+ ? 'border-blue-500 text-white bg-blue-500/5'
59
+ : 'border-transparent text-zinc-600 hover:text-zinc-400 hover:bg-white/[0.02]'
60
+ }`}
61
+ >
62
+ {label}
63
+ </button>
64
+ );
65
+
66
+ const TerminalCard: React.FC<{ title: string; subtitle?: string; children: React.ReactNode; className?: string }> = ({ title, subtitle, children, className = "" }) => (
67
+ <div className={`bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 relative overflow-hidden group shadow-2xl ${className}`}>
68
+ <div className="mb-6 relative z-10 flex justify-between items-start">
69
+ <div>
70
+ <h3 className="text-white font-black text-xs uppercase tracking-widest italic flex items-center gap-2">
71
+ <span className="w-1.5 h-1.5 rounded-full bg-blue-500"></span>
72
+ {title}
73
+ </h3>
74
+ {subtitle && <p className="text-[9px] text-zinc-600 uppercase font-black tracking-widest mt-1">{subtitle}</p>}
75
+ </div>
76
+ </div>
77
+ <div className="relative z-10">{children}</div>
78
+ </div>
79
+ );
80
+
81
+ // --- Main Component ---
82
+
83
+ const CryptoTerminal: React.FC = () => {
84
+ type ActiveTab = 'summary' | 'bot-fleet' | 'neural-intel' | 'quantum-depth';
85
+ const [activeTab, setActiveTab] = useState<ActiveTab>('summary');
86
+ const [markets, setMarkets] = useState<CoinMarketData[]>([]);
87
+ const [globalData, setGlobalData] = useState<GlobalData | null>(null);
88
+ const [loading, setLoading] = useState(true);
89
+
90
+ // Stateful Bot Fleet Management
91
+ const [bots, setBots] = useState<AITradingBot[]>([
92
+ { id: 'bot-1', name: 'Orion Alpha', strategy: 'Arbitrage', status: 'active', pnl: 2450.12, uptime: '142h' },
93
+ { id: 'bot-2', name: 'Vesper Node', strategy: 'Momentum', status: 'active', pnl: 810.45, uptime: '89h' },
94
+ { id: 'bot-3', name: 'Helios Prime', strategy: 'Mean Reversion', status: 'paused', pnl: -120.30, uptime: '24h' },
95
+ { id: 'bot-4', name: 'Ghost Protocol', strategy: 'Arbitrage', status: 'active', pnl: 11200.50, uptime: '450h' },
96
+ ]);
97
+
98
+ // Chat Interface
99
+ const [chatInput, setChatInput] = useState('');
100
+ const [chatHistory, setChatHistory] = useState<{ id: string; sender: 'user' | 'ai'; text: string; timestamp: Date }[]>([
101
+ { id: '1', sender: 'ai', text: 'Fleet status verified. Orion Alpha is currently identifying spread opportunities on ETH/USDC.', timestamp: new Date() }
102
+ ]);
103
+ const [isAiThinking, setIsAiThinking] = useState(false);
104
+
105
+ // --- Bot Logic ---
106
+ const toggleBot = (id: string) => {
107
+ setBots(prev => prev.map(bot => {
108
+ if (bot.id === id) {
109
+ const newStatus = bot.status === 'active' ? 'paused' : 'active';
110
+ return { ...bot, status: newStatus as 'active' | 'paused' };
111
+ }
112
+ return bot;
113
+ }));
114
+ };
115
+
116
+ const handleProvisionBot = () => {
117
+ const name = prompt("Enter Neural Designation (e.g. Nebula 7):");
118
+ if (!name) return;
119
+ const newBot: AITradingBot = {
120
+ id: `bot-${Date.now()}`,
121
+ name,
122
+ strategy: 'Momentum',
123
+ status: 'active',
124
+ pnl: 0,
125
+ uptime: '0h'
126
+ };
127
+ setBots([newBot, ...bots]);
128
+ };
129
+
130
+ // --- Chat Logic ---
131
+ const handleChatSubmit = async (e: React.FormEvent) => {
132
+ e.preventDefault();
133
+ if (!chatInput.trim() || isAiThinking) return;
134
+
135
+ const userMsg = { id: Date.now().toString(), sender: 'user' as const, text: chatInput, timestamp: new Date() };
136
+ setChatHistory(prev => [...prev, userMsg]);
137
+ setChatInput('');
138
+ setIsAiThinking(true);
139
+
140
+ const assistantMsgId = (Date.now() + 1).toString();
141
+ setChatHistory(prev => [...prev, { id: assistantMsgId, sender: 'ai', text: '', timestamp: new Date() }]);
142
+
143
+ try {
144
+ const context = { system: "NEXUS_OS_FLEET_CONTROL", activeBots: bots.filter(b => b.status === 'active').length };
145
+ const stream = await getFinancialAdviceStream(chatInput, context);
146
+
147
+ let fullContent = '';
148
+ for await (const chunk of stream) {
149
+ const text = chunk.text;
150
+ if (text) {
151
+ fullContent += text;
152
+ setChatHistory(prev => prev.map(m =>
153
+ m.id === assistantMsgId ? { ...m, text: fullContent } : m
154
+ ));
155
+ }
156
+ }
157
+ } catch (err) {
158
+ setChatHistory(prev => prev.map(m => m.id === assistantMsgId ? { ...m, text: 'Neural handshake timed out. Core is still online.' } : m));
159
+ } finally {
160
+ setIsAiThinking(false);
161
+ }
162
+ };
163
+
164
+ // --- Data Polling ---
165
+ const fetchData = async () => {
166
+ try {
167
+ const [marketData, global] = await Promise.all([
168
+ cryptoService.getMarkets('usd', 8),
169
+ cryptoService.getGlobal()
170
+ ]);
171
+ setMarkets(marketData);
172
+ setGlobalData(global);
173
+ } finally {
174
+ setLoading(false);
175
+ }
176
+ };
177
+
178
+ useEffect(() => {
179
+ fetchData();
180
+ const interval = setInterval(fetchData, 60000);
181
+ return () => clearInterval(interval);
182
+ }, []);
183
+
184
+ // --- Mocks & Charts ---
185
+ const aiInsights: AIInsight[] = useMemo(() => [
186
+ { id: '1', type: 'alpha', message: 'Orion detected unusual L2 liquidity inflow on Optimism. Executing directional bid.', confidence: 98, timestamp: '15s ago', actionable: true },
187
+ { id: '2', type: 'warning', message: 'Gas prices spiking. Vesper Node shifting to low-frequency mode.', confidence: 92, timestamp: '4m ago', actionable: false },
188
+ { id: '3', type: 'opportunity', message: 'Ghost Protocol identifies BTC/USDC arbitrage on derivative nodes.', confidence: 99, timestamp: '12m ago', actionable: true }
189
+ ], []);
190
+
191
+ const quantumChartData = useMemo(() => Array.from({ length: 40 }, (_, i) => ({
192
+ name: `t-${40 - i}`,
193
+ val: 50 + Math.sin(i / 5) * 30 + Math.random() * 10,
194
+ })), []);
195
+
196
+ return (
197
+ <div className="space-y-8 animate-in fade-in duration-700 pb-20">
198
+ {/* Real-time Global Bar */}
199
+ <div className="bg-zinc-950 border border-zinc-900 rounded-3xl p-6 flex flex-wrap items-center justify-between gap-8 shadow-2xl relative overflow-hidden group">
200
+ <div className="absolute inset-y-0 left-0 w-1 bg-blue-600 shadow-[0_0_15px_rgba(37,99,235,0.5)]"></div>
201
+ <div className="flex items-center gap-10">
202
+ <div className="space-y-1">
203
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest">Global Cap</p>
204
+ <p className="text-sm font-bold text-white mono">
205
+ ${globalData ? (globalData.total_market_cap.usd / 1e12).toFixed(2) : '--'}T
206
+ <span className={`ml-2 text-[10px] ${globalData?.market_cap_change_percentage_24h_usd >= 0 ? 'text-emerald-500' : 'text-rose-500'}`}>
207
+ {globalData?.market_cap_change_percentage_24h_usd.toFixed(1)}%
208
+ </span>
209
+ </p>
210
+ </div>
211
+ <div className="space-y-1">
212
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest">Fleet Health</p>
213
+ <div className="flex items-center gap-2">
214
+ <div className="w-12 h-1 bg-zinc-900 rounded-full overflow-hidden border border-zinc-800">
215
+ <div className="h-full bg-emerald-500 animate-pulse" style={{ width: '85%' }}></div>
216
+ </div>
217
+ <span className="text-[10px] font-black text-emerald-500 uppercase tracking-widest">Optimal</span>
218
+ </div>
219
+ </div>
220
+ </div>
221
+
222
+ <div className="flex items-center gap-4">
223
+ <AIStatusBadge status={isAiThinking ? 'thinking' : 'active'} />
224
+ <button onClick={fetchData} className="p-2.5 bg-zinc-900 hover:bg-zinc-800 rounded-xl transition-all text-zinc-500 hover:text-white border border-zinc-800">
225
+ <RefreshCw size={14} className={loading ? 'animate-spin' : ''} />
226
+ </button>
227
+ </div>
228
+ </div>
229
+
230
+ {/* Navigation */}
231
+ <div className="flex overflow-x-auto border-b border-zinc-900 scrollbar-hide bg-black/20 rounded-t-3xl">
232
+ <TabButton active={activeTab === 'summary'} onClick={() => setActiveTab('summary')} label="Control Summary" />
233
+ <TabButton active={activeTab === 'bot-fleet'} onClick={() => setActiveTab('bot-fleet')} label="Manage Bot Fleet" />
234
+ <TabButton active={activeTab === 'neural-intel'} onClick={() => setActiveTab('neural-intel')} label="Neural Intelligence" />
235
+ <TabButton active={activeTab === 'quantum-depth'} onClick={() => setActiveTab('quantum-depth')} label="Quantum Depth" />
236
+ </div>
237
+
238
+ {/* View Layer */}
239
+ {activeTab === 'summary' && (
240
+ <div className="grid grid-cols-12 gap-8 animate-in slide-in-from-bottom-4">
241
+ <div className="col-span-12 lg:col-span-8 space-y-8">
242
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
243
+ <TerminalCard title="Collective PNL Performance" subtitle="Total yield across 4 registered agents">
244
+ <div className="flex items-baseline gap-4 mt-4">
245
+ <h3 className={`text-4xl font-black mono tracking-tighter ${bots.reduce((s, b) => s + b.pnl, 0) >= 0 ? 'text-emerald-500' : 'text-rose-500'}`}>
246
+ ${bots.reduce((s, b) => s + b.pnl, 0).toLocaleString()}
247
+ </h3>
248
+ <span className="text-zinc-500 text-xs font-bold uppercase tracking-widest">Aggregated</span>
249
+ </div>
250
+ <div className="mt-8 space-y-4">
251
+ {bots.slice(0, 3).map(bot => (
252
+ <div key={bot.id} className="flex justify-between items-center text-[10px] font-black uppercase tracking-widest">
253
+ <span className="text-zinc-500 italic">{bot.name}</span>
254
+ <span className={bot.pnl >= 0 ? 'text-emerald-500' : 'text-rose-500'}>${bot.pnl.toFixed(2)}</span>
255
+ </div>
256
+ ))}
257
+ </div>
258
+ </TerminalCard>
259
+
260
+ <TerminalCard title="Machine Sentiment" subtitle="Natural language model market polling">
261
+ <div className="space-y-6 pt-2">
262
+ <div>
263
+ <div className="flex justify-between text-[9px] font-black uppercase tracking-widest mb-2">
264
+ <span className="text-emerald-500">Bullish Momentum</span>
265
+ <span className="text-white">74%</span>
266
+ </div>
267
+ <div className="w-full bg-zinc-900 h-1.5 rounded-full overflow-hidden border border-zinc-800">
268
+ <div className="bg-emerald-500 h-full shadow-[0_0_10px_#10b981]" style={{ width: '74%' }}></div>
269
+ </div>
270
+ </div>
271
+ <div className="p-5 bg-black border border-zinc-900 rounded-2xl italic text-[11px] text-zinc-500 leading-relaxed font-medium">
272
+ "LQI analysis detects extreme demand for Ethereum Layer 2 scaling tokens. Ghost Protocol is positioning for breakout."
273
+ </div>
274
+ </div>
275
+ </TerminalCard>
276
+ </div>
277
+
278
+ <TerminalCard title="Live Subspace Signals" subtitle="Real-time entanglement of arbitrage opportunities">
279
+ <div className="h-64 w-full pt-8">
280
+ <ResponsiveContainer width="100%" height="100%">
281
+ <AreaChart data={quantumChartData}>
282
+ <defs>
283
+ <linearGradient id="colorVal" x1="0" y1="0" x2="0" y2="1"><stop offset="5%" stopColor="#3b82f6" stopOpacity={0.3}/><stop offset="95%" stopColor="#3b82f6" stopOpacity={0}/></linearGradient>
284
+ </defs>
285
+ <CartesianGrid strokeDasharray="3 3" stroke="#18181b" vertical={false} />
286
+ <XAxis dataKey="name" hide />
287
+ <YAxis hide />
288
+ <Area type="monotone" dataKey="val" stroke="#3b82f6" strokeWidth={3} fillOpacity={1} fill="url(#colorVal)" />
289
+ </AreaChart>
290
+ </ResponsiveContainer>
291
+ </div>
292
+ </TerminalCard>
293
+ </div>
294
+
295
+ <div className="col-span-12 lg:col-span-4 space-y-8">
296
+ <TerminalCard title="Recent Fleet Intel">
297
+ <div className="space-y-6">
298
+ {aiInsights.map(insight => (
299
+ <div key={insight.id} className="border-l-2 border-zinc-800 pl-4 py-1 hover:border-blue-500/50 transition-all cursor-default">
300
+ <p className="text-[10px] font-black uppercase tracking-widest text-zinc-600 mb-1">{insight.type} • {insight.timestamp}</p>
301
+ <p className="text-[11px] text-zinc-300 font-medium italic leading-relaxed">"{insight.message}"</p>
302
+ </div>
303
+ ))}
304
+ <button onClick={() => setActiveTab('bot-fleet')} className="w-full py-4 bg-zinc-900 hover:bg-zinc-800 text-white rounded-2xl font-black text-[9px] uppercase tracking-widest transition-all border border-zinc-800 mt-4 flex items-center justify-center gap-3 shadow-xl">
305
+ <Settings size={14} />
306
+ Manage Registry
307
+ </button>
308
+ </div>
309
+ </TerminalCard>
310
+
311
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-8 shadow-2xl relative overflow-hidden">
312
+ <div className="absolute top-0 right-0 p-6 opacity-10">
313
+ <BrainCircuit size={80} className="text-blue-500" />
314
+ </div>
315
+ <h4 className="text-white font-black text-xs uppercase tracking-widest italic mb-6">Neural Convergence</h4>
316
+ <p className="text-zinc-500 text-[11px] leading-relaxed mb-6 font-medium">Model parity is currently maintained at 99.98% across 12 distributed nodes.</p>
317
+ <div className="flex gap-2">
318
+ <span className="px-2 py-1 bg-blue-600/10 border border-blue-500/20 rounded text-[8px] font-black text-blue-400 uppercase">Flash 3.0</span>
319
+ <span className="px-2 py-1 bg-emerald-600/10 border border-emerald-500/20 rounded text-[8px] font-black text-emerald-400 uppercase">Pro v2</span>
320
+ </div>
321
+ </div>
322
+ </div>
323
+ </div>
324
+ )}
325
+
326
+ {/* Bot Fleet View */}
327
+ {activeTab === 'bot-fleet' && (
328
+ <div className="space-y-8 animate-in slide-in-from-bottom-4">
329
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
330
+ <TerminalCard title="Fleet Registry">
331
+ <div className="flex items-center justify-between">
332
+ <div>
333
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-1">Active Agents</p>
334
+ <h3 className="text-3xl font-black text-white mono">{bots.filter(b => b.status === 'active').length} / {bots.length}</h3>
335
+ </div>
336
+ <div className="p-4 bg-blue-600/10 text-blue-500 rounded-2xl border border-blue-500/20">
337
+ <BrainCircuit size={24} />
338
+ </div>
339
+ </div>
340
+ </TerminalCard>
341
+ <TerminalCard title="Aggregate Profit">
342
+ <div className="flex items-center justify-between">
343
+ <div>
344
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-1">Total USD Harvested</p>
345
+ <h3 className={`text-3xl font-black mono ${bots.reduce((s, b) => s + b.pnl, 0) >= 0 ? 'text-emerald-500' : 'text-rose-500'}`}>
346
+ ${bots.reduce((s, b) => s + b.pnl, 0).toFixed(2)}
347
+ </h3>
348
+ </div>
349
+ <div className="p-4 bg-emerald-500/10 text-emerald-500 rounded-2xl border border-emerald-500/20">
350
+ <TrendingUp size={24} />
351
+ </div>
352
+ </div>
353
+ </TerminalCard>
354
+ <TerminalCard title="Node Provisioning">
355
+ <button
356
+ onClick={handleProvisionBot}
357
+ className="w-full h-full min-h-[80px] flex flex-col items-center justify-center bg-blue-600 hover:bg-blue-500 text-white rounded-2xl transition-all shadow-xl shadow-blue-900/20 group"
358
+ >
359
+ <Plus size={24} className="group-hover:rotate-90 transition-transform duration-500 mb-1" />
360
+ <span className="text-[10px] font-black uppercase tracking-widest">Initialize Agent</span>
361
+ </button>
362
+ </TerminalCard>
363
+ </div>
364
+
365
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
366
+ <div className="p-8 border-b border-zinc-900 bg-black/20 flex items-center justify-between">
367
+ <h3 className="text-white font-black uppercase tracking-widest italic flex items-center gap-3 text-sm">
368
+ <TerminalIcon size={18} className="text-blue-500" />
369
+ Neural Control Matrix
370
+ </h3>
371
+ </div>
372
+ <div className="overflow-x-auto">
373
+ <table className="w-full text-left">
374
+ <thead>
375
+ <tr className="border-b border-zinc-900 text-[9px] font-black uppercase tracking-widest text-zinc-500">
376
+ <th className="p-8">Agent Identity</th>
377
+ <th className="p-8">Neural Strategy</th>
378
+ <th className="p-8">Performance (PNL)</th>
379
+ <th className="p-8">Sync Uptime</th>
380
+ <th className="p-8 text-right">Controls</th>
381
+ </tr>
382
+ </thead>
383
+ <tbody className="divide-y divide-zinc-900">
384
+ {bots.map(bot => (
385
+ <tr key={bot.id} className="group hover:bg-white/[0.01] transition-all">
386
+ <td className="p-8">
387
+ <div className="flex items-center gap-4">
388
+ <div className={`w-10 h-10 rounded-xl flex items-center justify-center border transition-all ${bot.status === 'active' ? 'bg-blue-600/10 border-blue-500/20 text-blue-500' : 'bg-zinc-900 border-zinc-800 text-zinc-600'}`}>
389
+ <Activity size={20} />
390
+ </div>
391
+ <div>
392
+ <p className="text-sm font-black text-white uppercase italic">{bot.name}</p>
393
+ <div className="flex items-center gap-2 mt-1">
394
+ <div className={`w-1.5 h-1.5 rounded-full ${bot.status === 'active' ? 'bg-emerald-500 shadow-[0_0_8px_#10b981]' : 'bg-rose-500'}`}></div>
395
+ <span className="text-[9px] font-black text-zinc-600 uppercase tracking-widest">{bot.status}</span>
396
+ </div>
397
+ </div>
398
+ </div>
399
+ </td>
400
+ <td className="p-8">
401
+ <span className="px-3 py-1 bg-zinc-900 border border-zinc-800 text-[10px] font-black uppercase text-zinc-400 rounded-lg">
402
+ {bot.strategy}
403
+ </span>
404
+ </td>
405
+ <td className="p-8">
406
+ <p className={`text-sm font-black mono ${bot.pnl >= 0 ? 'text-emerald-500' : 'text-rose-500'}`}>
407
+ {bot.pnl >= 0 ? '+' : ''}${bot.pnl.toFixed(2)}
408
+ </p>
409
+ </td>
410
+ <td className="p-8 text-zinc-500 font-mono text-xs font-bold">
411
+ {bot.uptime}
412
+ </td>
413
+ <td className="p-8 text-right">
414
+ <div className="flex items-center justify-end gap-3">
415
+ <button
416
+ onClick={() => toggleBot(bot.id)}
417
+ className={`p-3 rounded-xl transition-all border ${bot.status === 'active' ? 'bg-rose-500/10 border-rose-500/20 text-rose-500 hover:bg-rose-500 hover:text-white' : 'bg-emerald-500/10 border-emerald-500/20 text-emerald-500 hover:bg-emerald-500 hover:text-white'}`}
418
+ title={bot.status === 'active' ? 'Pause Agent' : 'Resume Agent'}
419
+ >
420
+ {bot.status === 'active' ? <Pause size={16} /> : <Play size={16} />}
421
+ </button>
422
+ <button className="p-3 bg-zinc-900 border border-zinc-800 rounded-xl text-zinc-500 hover:text-white transition-all shadow-xl">
423
+ <Settings size={16} />
424
+ </button>
425
+ </div>
426
+ </td>
427
+ </tr>
428
+ ))}
429
+ </tbody>
430
+ </table>
431
+ </div>
432
+ </div>
433
+ </div>
434
+ )}
435
+
436
+ {/* Neural Intel Tab */}
437
+ {activeTab === 'neural-intel' && (
438
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
439
+ <div className="lg:col-span-2">
440
+ <TerminalCard title="Raw Alpha Stream" subtitle="Machine-generated signals filtered for corporate treasury">
441
+ <div className="space-y-4">
442
+ {aiInsights.map(insight => (
443
+ <div key={insight.id} className="bg-black border border-zinc-900 p-8 rounded-3xl flex items-start gap-8 hover:border-blue-500/30 transition-all group/item shadow-xl relative overflow-hidden">
444
+ <div className="absolute top-0 right-0 p-4 opacity-5 group-hover/item:opacity-10 transition-opacity">
445
+ <Zap size={60} />
446
+ </div>
447
+ <div className={`mt-1.5 p-3 rounded-xl ${insight.type === 'alpha' ? 'bg-amber-400/10 text-amber-400' : 'bg-blue-500/10 text-blue-500'} border border-white/5`}>
448
+ <TrendingUp size={24} />
449
+ </div>
450
+ <div className="flex-1 space-y-4 relative z-10">
451
+ <div className="flex justify-between items-start">
452
+ <h4 className="text-[10px] font-black uppercase tracking-widest text-zinc-600">{insight.type} signal detected • {insight.timestamp}</h4>
453
+ <span className="px-3 py-1 bg-zinc-900 rounded-full text-[9px] font-black text-blue-500 border border-blue-500/20 shadow-[0_0_10px_#3b82f622]">{insight.confidence}% Match</span>
454
+ </div>
455
+ <p className="text-zinc-200 text-lg font-bold leading-relaxed italic tracking-tight">"{insight.message}"</p>
456
+ {insight.actionable && (
457
+ <button className="px-6 py-2.5 bg-blue-600 hover:bg-blue-500 text-white rounded-xl text-[9px] font-black uppercase tracking-widest transition-all shadow-xl shadow-blue-900/20">Authorize Trade</button>
458
+ )}
459
+ </div>
460
+ </div>
461
+ ))}
462
+ </div>
463
+ </TerminalCard>
464
+ </div>
465
+ <div className="lg:col-span-1">
466
+ <TerminalCard title="Neural Terminal">
467
+ <div className="h-[400px] overflow-y-auto space-y-6 mb-6 custom-scrollbar pr-2">
468
+ {chatHistory.map(msg => (
469
+ <div key={msg.id} className={`flex ${msg.sender === 'user' ? 'justify-end' : 'justify-start'}`}>
470
+ <div className={`max-w-[90%] p-6 rounded-[2rem] text-sm leading-relaxed ${msg.sender === 'user' ? 'bg-blue-600 text-white rounded-tr-none shadow-xl shadow-blue-900/20 font-bold' : 'bg-black border border-zinc-900 text-zinc-300 rounded-tl-none italic'}`}>
471
+ {msg.text || (isAiThinking && <Loader2 className="animate-spin" size={14} />)}
472
+ <p className={`text-[8px] mt-3 font-black uppercase tracking-widest opacity-40 ${msg.sender === 'user' ? 'text-right' : 'text-left'}`}>
473
+ {msg.timestamp.toLocaleTimeString([], { hour12: false })} • {msg.sender === 'user' ? 'U_IDENT' : 'NEXUS_OUT'}
474
+ </p>
475
+ </div>
476
+ </div>
477
+ ))}
478
+ </div>
479
+ <form onSubmit={handleChatSubmit} className="relative">
480
+ <input
481
+ value={chatInput}
482
+ onChange={(e) => setChatInput(e.target.value)}
483
+ placeholder="Query fleet intelligence..."
484
+ className="w-full bg-black border border-zinc-800 rounded-2xl py-5 pl-8 pr-16 text-white text-xs outline-none focus:border-blue-500 transition-all font-bold placeholder:text-zinc-800"
485
+ />
486
+ <button type="submit" disabled={isAiThinking} className="absolute right-4 top-1/2 -translate-y-1/2 p-3 bg-blue-600 hover:bg-blue-500 rounded-xl text-white disabled:opacity-50 shadow-xl shadow-blue-900/30 transition-all">
487
+ <ArrowUpRight size={20} />
488
+ </button>
489
+ </form>
490
+ </TerminalCard>
491
+ </div>
492
+ </div>
493
+ )}
494
+
495
+ {/* Depth Tab */}
496
+ {activeTab === 'quantum-depth' && (
497
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 animate-in slide-in-from-bottom-4">
498
+ <TerminalCard title="Price Action Entanglement" subtitle="Monitoring subspace correlation between BTC and ETH nodes">
499
+ <div className="h-96 w-full pt-8">
500
+ <ResponsiveContainer width="100%" height="100%">
501
+ <AreaChart data={quantumChartData}>
502
+ <defs>
503
+ <linearGradient id="colorSignal" x1="0" y1="0" x2="0" y2="1"><stop offset="5%" stopColor="#8b5cf6" stopOpacity={0.3}/><stop offset="95%" stopColor="#8b5cf6" stopOpacity={0}/></linearGradient>
504
+ </defs>
505
+ <CartesianGrid strokeDasharray="3 3" stroke="#18181b" />
506
+ <XAxis dataKey="name" hide />
507
+ <YAxis hide />
508
+ <Area type="monotone" dataKey="val" stroke="#8b5cf6" strokeWidth={3} fillOpacity={1} fill="url(#colorSignal)" />
509
+ </AreaChart>
510
+ </ResponsiveContainer>
511
+ </div>
512
+ </TerminalCard>
513
+ <TerminalCard title="Network Load Analysis">
514
+ <div className="flex flex-col items-center justify-center h-96 text-center space-y-8">
515
+ <div className="w-40 h-40 rounded-full border-8 border-zinc-900 flex items-center justify-center relative">
516
+ <div className="absolute inset-0 rounded-full border-8 border-t-emerald-500 border-r-emerald-500 border-b-emerald-500/20 border-l-emerald-500/20 animate-spin [animation-duration:3s]"></div>
517
+ <h3 className="text-4xl font-black text-white mono">82<span className="text-sm text-zinc-600">%</span></h3>
518
+ </div>
519
+ <div>
520
+ <p className="text-white font-black text-sm uppercase tracking-widest italic">Core Utilization</p>
521
+ <p className="text-zinc-600 text-[10px] font-bold uppercase tracking-widest mt-2">Node stability verified across all regions</p>
522
+ </div>
523
+ </div>
524
+ </TerminalCard>
525
+ </div>
526
+ )}
527
+ </div>
528
+ );
529
+ };
530
+
531
+ export default CryptoTerminal;
views/DCRManagement.tsx ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect, useRef } from 'react';
3
+ import {
4
+ Key,
5
+ Shield,
6
+ Plus,
7
+ RefreshCw,
8
+ Trash2,
9
+ ChevronRight,
10
+ Fingerprint,
11
+ Terminal,
12
+ ExternalLink,
13
+ Eye,
14
+ EyeOff,
15
+ CheckCircle2,
16
+ AlertCircle,
17
+ Code
18
+ } from 'lucide-react';
19
+ import { ClientRegisterRequest, ClientRegisterResponse } from '../types/index.ts';
20
+
21
+ const MOCK_INITIAL_CLIENTS: ClientRegisterResponse[] = [
22
+ {
23
+ client_id: 'CLI-A192J-B002',
24
+ client_secret: 'SEC-K992-XXXX-Z102',
25
+ appId: 'LUMINA_CORE_NODE',
26
+ client_name: 'Lumina Primary Ledger',
27
+ clientDisplayName: 'Quantum Master Node',
28
+ redirect_uris: ['https://lumina.app/auth/callback'],
29
+ scope: ['accounts_details_transactions', 'customers_profiles'],
30
+ description: 'The main corporate ledger application for treasury management.',
31
+ token_endpoint_auth_method: 'client_secret_post'
32
+ }
33
+ ];
34
+
35
+ const SCOPES = [
36
+ 'accounts_details_transactions',
37
+ 'accounts_routing_number',
38
+ 'customers_profiles',
39
+ 'accounts_statements',
40
+ 'accounts_tax_statements'
41
+ ];
42
+
43
+ const DCRManagement: React.FC = () => {
44
+ const [clients, setClients] = useState<ClientRegisterResponse[]>(MOCK_INITIAL_CLIENTS);
45
+ const [isRegistering, setIsRegistering] = useState(false);
46
+ const [selectedClient, setSelectedClient] = useState<ClientRegisterResponse | null>(null);
47
+ const [showSecret, setShowSecret] = useState<string | null>(null);
48
+ const [logs, setLogs] = useState<string[]>(['System: DCR Interface Initialized. Waiting for input...']);
49
+ const logEndRef = useRef<HTMLDivElement>(null);
50
+
51
+ const [form, setForm] = useState<ClientRegisterRequest>({
52
+ client_name: '',
53
+ redirect_uris: [''],
54
+ scope: [],
55
+ description: '',
56
+ appId: ''
57
+ });
58
+
59
+ useEffect(() => {
60
+ logEndRef.current?.scrollIntoView({ behavior: 'smooth' });
61
+ }, [logs]);
62
+
63
+ const addLog = (msg: string) => setLogs(prev => [...prev, `${new Date().toLocaleTimeString()} - ${msg}`]);
64
+
65
+ const handleRegister = async (e: React.FormEvent) => {
66
+ e.preventDefault();
67
+ addLog(`POST /api/dcr/v1/register - payload: ${JSON.stringify(form)}`);
68
+
69
+ setTimeout(() => {
70
+ const newClient: ClientRegisterResponse = {
71
+ ...form,
72
+ client_id: `CLI-${Math.random().toString(36).substring(2, 9).toUpperCase()}`,
73
+ client_secret: `SEC-${Math.random().toString(36).substring(2, 12).toUpperCase()}`,
74
+ grant_types: ['authorization_code', 'refresh_token'],
75
+ token_endpoint_auth_method: 'client_secret_post'
76
+ };
77
+
78
+ setClients([...clients, newClient]);
79
+ setIsRegistering(false);
80
+ addLog(`HTTP/1.1 201 Created - New Client ID: ${newClient.client_id}`);
81
+ }, 1200);
82
+ };
83
+
84
+ const handleUpdate = (clientId: string) => {
85
+ addLog(`PUT /api/dcr/v1/register/${clientId} - Requesting metadata refresh...`);
86
+ setTimeout(() => {
87
+ addLog(`HTTP/1.1 200 OK - Metadata synchronized for ${clientId}`);
88
+ }, 800);
89
+ };
90
+
91
+ const handleRevoke = (clientId: string) => {
92
+ if (!confirm(`Confirm revocation of ${clientId}? This cannot be undone.`)) return;
93
+ addLog(`DELETE /api/dcr/v1/register/${clientId} - Initiating revocation...`);
94
+ setTimeout(() => {
95
+ setClients(clients.filter(c => c.client_id !== clientId));
96
+ if (selectedClient?.client_id === clientId) setSelectedClient(null);
97
+ addLog(`HTTP/1.1 204 No Content - Registration ${clientId} purged from vault.`);
98
+ }, 1000);
99
+ };
100
+
101
+ return (
102
+ <div className="max-w-7xl mx-auto space-y-8 animate-in fade-in duration-700">
103
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
104
+ <div>
105
+ <h2 className="text-3xl font-bold text-white mb-2 flex items-center gap-3">
106
+ <Key className="text-blue-500" />
107
+ Dynamic Client Registry
108
+ </h2>
109
+ <p className="text-zinc-500 font-medium">Manage 4th party Data Recipients via FDX-standard DCR flows.</p>
110
+ </div>
111
+ <button
112
+ onClick={() => {
113
+ setIsRegistering(true);
114
+ setSelectedClient(null);
115
+ setForm({
116
+ client_name: '',
117
+ redirect_uris: [''],
118
+ scope: [],
119
+ description: '',
120
+ appId: ''
121
+ });
122
+ }}
123
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-xs uppercase tracking-widest transition-all shadow-lg shadow-blue-900/30"
124
+ >
125
+ <Plus size={18} />
126
+ <span>New Registration</span>
127
+ </button>
128
+ </div>
129
+
130
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
131
+ <div className="lg:col-span-1 space-y-4 h-[calc(100vh-250px)] overflow-y-auto pr-2 custom-scrollbar">
132
+ {clients.map(client => (
133
+ <button
134
+ key={client.client_id}
135
+ onClick={() => {
136
+ setSelectedClient(client);
137
+ setIsRegistering(false);
138
+ }}
139
+ className={`w-full text-left p-6 rounded-3xl border transition-all group ${
140
+ selectedClient?.client_id === client.client_id
141
+ ? 'bg-blue-600/10 border-blue-500 shadow-xl shadow-blue-900/10'
142
+ : 'bg-zinc-900 border-zinc-800 hover:border-zinc-700'
143
+ }`}
144
+ >
145
+ <div className="flex justify-between items-start mb-4">
146
+ <div className={`p-2 rounded-xl ${selectedClient?.client_id === client.client_id ? 'bg-blue-500 text-white' : 'bg-zinc-800 text-zinc-400 group-hover:text-blue-400'}`}>
147
+ <Fingerprint size={20} />
148
+ </div>
149
+ <div className="flex space-x-2">
150
+ <span className="text-[10px] font-black uppercase text-emerald-500 bg-emerald-500/10 px-2 py-0.5 rounded">Active</span>
151
+ </div>
152
+ </div>
153
+ <h4 className="text-white font-bold mb-1 group-hover:text-blue-400 transition-colors">{client.client_name}</h4>
154
+ <p className="text-zinc-500 text-xs mono tracking-tight mb-4">{client.client_id}</p>
155
+ <div className="flex items-center gap-4 text-zinc-600">
156
+ <div className="flex items-center gap-1.5">
157
+ <Shield size={12} />
158
+ <span className="text-[10px] font-bold uppercase">{client.scope.length} Scopes</span>
159
+ </div>
160
+ <div className="flex items-center gap-1.5">
161
+ <ExternalLink size={12} />
162
+ <span className="text-[10px] font-bold uppercase">1 URI</span>
163
+ </div>
164
+ </div>
165
+ </button>
166
+ ))}
167
+ </div>
168
+
169
+ <div className="lg:col-span-2 space-y-8">
170
+ {isRegistering ? (
171
+ <div className="bg-zinc-900 border border-zinc-800 rounded-[40px] p-10 animate-in slide-in-from-right-4 duration-500">
172
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase mb-8 flex items-center gap-3">
173
+ <Plus className="text-blue-500" />
174
+ Register New Recipient
175
+ </h3>
176
+ <form onSubmit={handleRegister} className="space-y-6">
177
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
178
+ <div className="space-y-2">
179
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">App ID (optional)</label>
180
+ <input
181
+ value={form.appId}
182
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-5 py-3.5 text-white outline-none focus:border-blue-500/50 transition-all"
183
+ placeholder="e.g. CORE_NODE_01"
184
+ onChange={e => setForm({...form, appId: e.target.value})}
185
+ />
186
+ </div>
187
+ <div className="space-y-2">
188
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Client Name</label>
189
+ <input
190
+ required
191
+ value={form.client_name}
192
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-5 py-3.5 text-white outline-none focus:border-blue-500/50 transition-all"
193
+ placeholder="Legal Recipient Name"
194
+ onChange={e => setForm({...form, client_name: e.target.value})}
195
+ />
196
+ </div>
197
+ </div>
198
+ <div className="space-y-2">
199
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Description</label>
200
+ <textarea
201
+ value={form.description}
202
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-5 py-3.5 text-white outline-none focus:border-blue-500/50 transition-all h-24 resize-none"
203
+ placeholder="Brief overview of the recipient's purpose..."
204
+ onChange={e => setForm({...form, description: e.target.value})}
205
+ />
206
+ </div>
207
+ <div className="pt-4 flex gap-4">
208
+ <button type="submit" className="flex-1 py-4 bg-blue-600 text-white rounded-2xl font-black text-xs uppercase tracking-widest transition-all">Execute Registration</button>
209
+ <button type="button" onClick={() => setIsRegistering(false)} className="px-8 py-4 bg-zinc-800 text-white rounded-2xl font-black text-xs uppercase tracking-widest transition-all">Cancel</button>
210
+ </div>
211
+ </form>
212
+ </div>
213
+ ) : selectedClient ? (
214
+ <div className="bg-zinc-900 border border-zinc-800 rounded-[40px] p-10 animate-in zoom-in-95 duration-300">
215
+ <div className="flex justify-between items-start mb-10 pb-10 border-b border-zinc-800">
216
+ <div className="flex items-center gap-6">
217
+ <div className="w-16 h-16 bg-blue-600 rounded-3xl flex items-center justify-center">
218
+ <Shield size={32} className="text-white" />
219
+ </div>
220
+ <div>
221
+ <h3 className="text-3xl font-black text-white italic tracking-tighter uppercase">{selectedClient.client_name}</h3>
222
+ <p className="text-zinc-500 text-xs font-bold uppercase tracking-widest">Metadata Identity Record</p>
223
+ </div>
224
+ </div>
225
+ <button onClick={() => handleRevoke(selectedClient.client_id)} className="p-3 text-zinc-500 hover:text-rose-500 transition-colors bg-zinc-800 rounded-xl">
226
+ <Trash2 size={18} />
227
+ </button>
228
+ </div>
229
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-12">
230
+ <div>
231
+ <p className="text-[10px] uppercase font-black text-zinc-600 tracking-widest mb-2">OAuth 2.0 Client Identifier</p>
232
+ <div className="bg-black border border-zinc-800 p-4 rounded-2xl flex items-center justify-between group">
233
+ <span className="text-white font-mono text-sm">{selectedClient.client_id}</span>
234
+ <Code size={16} className="text-zinc-700 group-hover:text-blue-500 transition-colors" />
235
+ </div>
236
+ </div>
237
+ <div>
238
+ <p className="text-[10px] uppercase font-black text-zinc-600 tracking-widest mb-2">Sync Status</p>
239
+ <div className="flex items-center gap-3">
240
+ <div className="w-2 h-2 rounded-full bg-emerald-500 shadow-[0_0_8px_#10b981]"></div>
241
+ <span className="text-white font-black text-xs uppercase">Handshake Parity</span>
242
+ </div>
243
+ </div>
244
+ </div>
245
+ </div>
246
+ ) : (
247
+ <div className="h-full flex flex-col items-center justify-center bg-zinc-950/50 border-2 border-dashed border-zinc-900 rounded-[40px] text-center p-20">
248
+ <div className="w-24 h-24 bg-zinc-900 rounded-full flex items-center justify-center mb-6">
249
+ <Fingerprint size={48} className="text-zinc-800" />
250
+ </div>
251
+ <h3 className="text-xl font-bold text-zinc-600 uppercase tracking-[0.2em] mb-2">Registry Offline</h3>
252
+ <p className="text-zinc-700 text-sm max-w-xs font-medium">Select a recipient node or provision a new registration to view metadata.</p>
253
+ </div>
254
+ )}
255
+ <div className="bg-black border border-zinc-800 rounded-[32px] overflow-hidden shadow-2xl h-64 flex flex-col">
256
+ <div className="bg-zinc-900/80 px-6 py-3 border-b border-zinc-800 flex justify-between items-center">
257
+ <div className="flex items-center gap-2">
258
+ <Terminal size={14} className="text-blue-400" />
259
+ <span className="text-[10px] font-black uppercase tracking-widest text-zinc-500">DCR Node Trace Log</span>
260
+ </div>
261
+ </div>
262
+ <div className="flex-1 p-6 font-mono text-[11px] overflow-y-auto custom-scrollbar space-y-1">
263
+ {logs.map((log, i) => (
264
+ <div key={i} className="text-zinc-600">{log}</div>
265
+ ))}
266
+ <div ref={logEndRef} />
267
+ </div>
268
+ </div>
269
+ </div>
270
+ </div>
271
+ </div>
272
+ );
273
+ };
274
+
275
+ export default DCRManagement;
views/Documentation.tsx ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { BookOpen, Search, Code, LayoutDashboard, Building2, Cpu, ShieldCheck, Zap, Bitcoin, Terminal, ArrowRight, ChevronRight } from 'lucide-react';
4
+
5
+ const DocSection: React.FC<{ icon: any, title: string, description: string, details: string[] }> = ({ icon: Icon, title, description, details }) => (
6
+ <div className="group bg-zinc-950 border border-zinc-900 p-10 rounded-[3rem] hover:border-blue-500/30 transition-all shadow-2xl">
7
+ <div className="flex items-center gap-6 mb-8">
8
+ <div className="p-4 bg-zinc-900 rounded-2xl text-blue-500 group-hover:scale-110 transition-transform duration-500 border border-zinc-800">
9
+ <Icon size={24} />
10
+ </div>
11
+ <div>
12
+ <h3 className="text-xl font-black text-white italic tracking-tighter uppercase">{title}</h3>
13
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">Protocol Segment v6.0</p>
14
+ </div>
15
+ </div>
16
+ <p className="text-zinc-400 text-sm leading-relaxed mb-8 font-medium italic">"{description}"</p>
17
+ <div className="space-y-4 border-t border-zinc-900 pt-8">
18
+ <h4 className="text-[9px] font-black text-zinc-500 uppercase tracking-widest mb-4">Core Functions</h4>
19
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
20
+ {details.map((d, i) => (
21
+ <div key={i} className="flex items-center gap-3 text-xs font-bold text-zinc-300">
22
+ <div className="w-1.5 h-1.5 rounded-full bg-blue-500"></div>
23
+ {d}
24
+ </div>
25
+ ))}
26
+ </div>
27
+ </div>
28
+ </div>
29
+ );
30
+
31
+ const Documentation: React.FC = () => {
32
+ return (
33
+ <div className="max-w-6xl mx-auto py-20 animate-in slide-in-from-bottom-6 duration-1000">
34
+ <div className="flex flex-col md:flex-row justify-between items-end mb-20 gap-10">
35
+ <div>
36
+ <div className="inline-flex items-center space-x-3 px-4 py-2 bg-blue-500/10 border border-blue-500/20 rounded-full text-[10px] font-black text-blue-500 uppercase tracking-widest mb-6">
37
+ <BookOpen size={12} />
38
+ <span>Lumina System Manual</span>
39
+ </div>
40
+ <h1 className="text-6xl font-black italic text-white uppercase tracking-tighter leading-none">
41
+ Documentation <br /> <span className="text-blue-500 not-italic">Core</span>
42
+ </h1>
43
+ </div>
44
+ <div className="relative w-full md:w-96">
45
+ <Search className="absolute left-6 top-1/2 -translate-y-1/2 text-zinc-600" size={18} />
46
+ <input
47
+ placeholder="Search Protocol Map..."
48
+ className="w-full bg-zinc-950 border-2 border-zinc-900 focus:border-blue-500/50 rounded-[2rem] py-6 pl-16 pr-8 text-white font-bold outline-none transition-all"
49
+ />
50
+ </div>
51
+ </div>
52
+
53
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
54
+ <DocSection
55
+ icon={LayoutDashboard}
56
+ title="Command Overview"
57
+ description="The central nervous system of the Lumina Quantum Ledger. Aggregates live data from multiple institutional nodes into a singular dashboard."
58
+ details={["Real-time Cash Curve Monitoring", "AI-Driven Liquidity Alerts", "Carbon Deficit Tracking", "Node Health Verification"]}
59
+ />
60
+ <DocSection
61
+ icon={Building2}
62
+ title="Bank Registry"
63
+ description="Managed corporate treasury nodes connecting directly to tier-1 banks via the FDX v6 protocol. Secure, read-write access to institutional vaults."
64
+ details={["FDX Native Handshakes", "Multi-Institution Syncing", "Available Balance Polling", "Real-time Node Status"]}
65
+ />
66
+ <DocSection
67
+ icon={Cpu}
68
+ title="Quantum Oracle"
69
+ description="Neural financial forecasting engine. Runs complex 'What-If' simulations using high-fidelity economic models and predictive drift analysis."
70
+ details={["Inflation Spike Modeling", "Compute Cost Forecasting", "Market Volatility Stress Testing", "Resilience AA- Ratings"]}
71
+ />
72
+ <DocSection
73
+ icon={ShieldCheck}
74
+ title="Record Vault"
75
+ description="Encrypted long-term storage for audit compliance. Every document is cryptographically signed and stored with sha256 checksum integrity."
76
+ details={["Institutional Archive Export", "Cryptographic Metadata Storage", "Compliance Audit Trails", "Multi-format Ledger Support"]}
77
+ />
78
+ <DocSection
79
+ icon={Bitcoin}
80
+ title="Asset Terminal"
81
+ description="Decentralized finance gateway for corporate treasuries. High-frequency trading (HFT) interface with neural-enhanced depth analysis."
82
+ details={["Real-time Global Market Data", "AI Sentiment Monitoring", "High-Frequency Tick Visualization", "Subspace Signal Parity"]}
83
+ />
84
+ <DocSection
85
+ icon={Zap}
86
+ title="Onboarding Flows"
87
+ description="Low-friction institutional integration layer. Dynamically generate secure handshakes for account collection and payment authorization."
88
+ details={["AI SDK Code Architect", "RTP Payment Triggering", "Dynamic Client Registration", "Secure Token Issuance"]}
89
+ />
90
+ </div>
91
+
92
+ <div className="mt-20 p-12 bg-blue-600 rounded-[3.5rem] flex flex-col md:flex-row justify-between items-center gap-10 shadow-[0_30px_60px_rgba(37,99,235,0.2)]">
93
+ <div className="flex items-center gap-8">
94
+ <div className="w-20 h-20 bg-white/20 rounded-3xl flex items-center justify-center text-white backdrop-blur-md">
95
+ <Terminal size={40} />
96
+ </div>
97
+ <div>
98
+ <h4 className="text-2xl font-black text-white italic tracking-tighter uppercase mb-2">Need Direct Handshake?</h4>
99
+ <p className="text-blue-100 text-sm max-w-lg font-bold leading-relaxed">Our AI Terminal architects are online 24/7 to help you deploy custom node integrations and quantum-resistant treasury flows.</p>
100
+ </div>
101
+ </div>
102
+ <button onClick={() => window.history.back()} className="px-10 py-5 bg-white text-blue-600 rounded-2xl font-black text-xs uppercase tracking-widest hover:scale-105 transition-all shadow-xl">
103
+ Open AI Advisor
104
+ </button>
105
+ </div>
106
+ </div>
107
+ );
108
+ };
109
+
110
+ export default Documentation;
views/Documents.tsx ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import {
4
+ FileText,
5
+ Download,
6
+ Search,
7
+ Filter,
8
+ Calendar,
9
+ MoreHorizontal,
10
+ ChevronRight,
11
+ ShieldCheck,
12
+ Tag,
13
+ Loader2
14
+ } from 'lucide-react';
15
+ import { Document } from '../types/index.ts';
16
+
17
+ const MOCK_DOCS: Document[] = [
18
+ { id: 'DOC-8821', documentableId: 'TXN-9021', documentableType: 'transaction', documentType: 'Invoice', fileName: 'INV_CloudCompute_Q1.pdf', size: 124000, createdAt: '2024-03-31', format: 'PDF' },
19
+ { id: 'DOC-9942', documentableId: 'IA-4401', documentableType: 'internal_account', documentType: 'Tax Form', fileName: 'US_1099_Lumina.pdf', size: 450000, createdAt: '2024-03-15', format: 'PDF' },
20
+ { id: 'DOC-1025', documentableId: 'PO-5512', documentableType: 'payment_order', documentType: 'JSON Payload', fileName: 'PO_DISBURSE_882.json', size: 12000, createdAt: '2024-03-30', format: 'JSON' },
21
+ ];
22
+
23
+ const Documents: React.FC = () => {
24
+ const [isExporting, setIsExporting] = useState(false);
25
+
26
+ const handleExportArchive = async () => {
27
+ setIsExporting(true);
28
+ // Artificial latency for institutional "packaging" feel
29
+ await new Promise(resolve => setTimeout(resolve, 1500));
30
+
31
+ const exportData = {
32
+ vault_export_id: `LQI-EXP-${Math.random().toString(36).substring(7).toUpperCase()}`,
33
+ timestamp: new Date().toISOString(),
34
+ record_count: MOCK_DOCS.length,
35
+ documents: MOCK_DOCS,
36
+ checksum: "sha256-f2e3c4d5a6b7c8d9e0f1..."
37
+ };
38
+
39
+ const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
40
+ const url = URL.createObjectURL(blob);
41
+ const link = window.document.createElement('a');
42
+ link.href = url;
43
+ link.download = `Lumina_Vault_Archive_${new Date().toISOString().split('T')[0]}.json`;
44
+ window.document.body.appendChild(link);
45
+ link.click();
46
+ window.document.body.removeChild(link);
47
+ URL.revokeObjectURL(url);
48
+
49
+ setIsExporting(false);
50
+ };
51
+
52
+ return (
53
+ <div className="space-y-10 animate-in fade-in duration-700">
54
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
55
+ <div>
56
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Institutional <span className="text-blue-500 not-italic">Vault</span></h2>
57
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Encrypted Storage for Compliance & Treasury Records</p>
58
+ </div>
59
+ <div className="flex gap-4">
60
+ <button
61
+ onClick={handleExportArchive}
62
+ disabled={isExporting}
63
+ className="flex items-center space-x-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all disabled:opacity-50"
64
+ >
65
+ {isExporting ? <Loader2 size={14} className="animate-spin" /> : <Download size={14} />}
66
+ <span>{isExporting ? 'Packaging Archive...' : 'Export Archive'}</span>
67
+ </button>
68
+ <button className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/30">
69
+ <FileText size={14} />
70
+ <span>Upload Document</span>
71
+ </button>
72
+ </div>
73
+ </div>
74
+
75
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
76
+ <div className="p-10 border-b border-zinc-900 flex flex-col md:flex-row justify-between items-center bg-black/20 gap-8">
77
+ <div className="flex items-center space-x-10 w-full md:w-auto">
78
+ <div className="flex items-center space-x-4">
79
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-2xl">
80
+ <ShieldCheck size={20} />
81
+ </div>
82
+ <h3 className="text-white font-black uppercase tracking-[0.2em] italic">Compliant Storage</h3>
83
+ </div>
84
+ </div>
85
+ <div className="flex gap-4 w-full md:w-auto">
86
+ <div className="relative flex-1 md:w-64">
87
+ <Search className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600" size={14} />
88
+ <input
89
+ placeholder="Search Archive..."
90
+ className="w-full bg-black border border-zinc-800 rounded-xl py-2 pl-10 pr-4 text-[10px] font-black uppercase tracking-widest text-white outline-none focus:border-blue-500/50 transition-all"
91
+ />
92
+ </div>
93
+ <button className="p-3 bg-zinc-900 border border-zinc-800 rounded-xl text-zinc-500 hover:text-white transition-all">
94
+ <Filter size={18} />
95
+ </button>
96
+ </div>
97
+ </div>
98
+
99
+ <div className="overflow-x-auto">
100
+ <table className="w-full text-left">
101
+ <thead>
102
+ <tr className="border-b border-zinc-900 bg-black/40 text-[9px] font-black uppercase tracking-[0.3em] text-zinc-500">
103
+ <th className="px-10 py-5">Record Identifier</th>
104
+ <th className="py-5">Associated Object</th>
105
+ <th className="py-5">Metadata</th>
106
+ <th className="py-5">Created At</th>
107
+ <th className="px-10 py-5 text-right">Actions</th>
108
+ </tr>
109
+ </thead>
110
+ <tbody className="divide-y divide-zinc-900/50">
111
+ {MOCK_DOCS.map(doc => (
112
+ <tr key={doc.id} className="group hover:bg-white/[0.02] transition-colors">
113
+ <td className="px-10 py-8">
114
+ <div className="flex items-center gap-4">
115
+ <div className="w-10 h-10 rounded-xl bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-500 group-hover:text-blue-400 transition-colors">
116
+ <FileText size={18} />
117
+ </div>
118
+ <div>
119
+ <p className="text-white font-black text-xs uppercase italic truncate max-w-[200px]">{doc.fileName}</p>
120
+ <p className="text-[10px] text-zinc-600 font-mono font-bold mt-1 uppercase tracking-tighter">ID: {doc.id} • {(doc.size / 1024).toFixed(1)} KB</p>
121
+ </div>
122
+ </div>
123
+ </td>
124
+ <td className="py-8">
125
+ <div className="flex items-center gap-2">
126
+ <Tag size={12} className="text-zinc-600" />
127
+ <p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">
128
+ {doc.documentableType.replace('_', ' ')}: <span className="text-white mono">{doc.documentableId}</span>
129
+ </p>
130
+ </div>
131
+ </td>
132
+ <td className="py-8">
133
+ <div className="flex items-center gap-2">
134
+ <span className="px-2 py-1 bg-zinc-900 border border-zinc-800 text-[8px] font-black uppercase text-zinc-500 rounded">{doc.format}</span>
135
+ <span className="text-[10px] font-bold text-zinc-400 uppercase tracking-widest">{doc.documentType}</span>
136
+ </div>
137
+ </td>
138
+ <td className="py-8">
139
+ <div className="flex items-center gap-2 text-zinc-500">
140
+ <Calendar size={12} />
141
+ <p className="text-[10px] font-bold uppercase tracking-widest">{doc.createdAt}</p>
142
+ </div>
143
+ </td>
144
+ <td className="px-10 py-8 text-right">
145
+ <div className="flex items-center justify-end gap-3 opacity-0 group-hover:opacity-100 transition-opacity">
146
+ <button className="p-2 text-zinc-500 hover:text-white" title="Download"><Download size={16} /></button>
147
+ <button className="p-2 text-zinc-500 hover:text-white"><ChevronRight size={16} /></button>
148
+ <button className="p-2 text-zinc-500 hover:text-white"><MoreHorizontal size={16} /></button>
149
+ </div>
150
+ </td>
151
+ </tr>
152
+ ))}
153
+ </tbody>
154
+ </table>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ );
159
+ };
160
+
161
+ export default Documents;
views/Flows.tsx ADDED
@@ -0,0 +1,522 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useRef, useEffect } from 'react';
3
+ import {
4
+ Zap,
5
+ ArrowRightLeft,
6
+ UserPlus,
7
+ Search,
8
+ MoreHorizontal,
9
+ Clock,
10
+ CheckCircle2,
11
+ XCircle,
12
+ Copy,
13
+ Terminal,
14
+ ExternalLink,
15
+ Code,
16
+ X,
17
+ Loader2,
18
+ Cpu,
19
+ ShieldCheck,
20
+ Key,
21
+ Plus,
22
+ MessageSquare,
23
+ ArrowRight,
24
+ RefreshCw,
25
+ Eye,
26
+ Trash2,
27
+ Lock,
28
+ Globe
29
+ } from 'lucide-react';
30
+ import { AccountCollectionFlow, PaymentFlow } from '../types/index.ts';
31
+ import { callGemini } from '../services/geminiService.ts';
32
+
33
+ const BRIDGE_URL = "https://admin08077-ai-banking.static.hf.space";
34
+
35
+ interface ApiKey {
36
+ id: string;
37
+ key: string;
38
+ name: string;
39
+ scope: string;
40
+ created_at: string;
41
+ status: 'ACTIVE' | 'REVOKED';
42
+ }
43
+
44
+ const INITIAL_ACCOUNT_FLOWS: AccountCollectionFlow[] = [
45
+ {
46
+ id: 'ACF-1102',
47
+ object: 'account_collection_flow',
48
+ live_mode: true,
49
+ created_at: '2024-03-20T10:00:00Z',
50
+ updated_at: '2024-03-31T14:00:00Z',
51
+ client_token: 'acf_tok_882910398291',
52
+ status: 'pending',
53
+ counterparty_id: 'CP-8801',
54
+ external_account_id: null,
55
+ payment_types: ['ach', 'wire']
56
+ }
57
+ ];
58
+
59
+ const INITIAL_PAYMENT_FLOWS: PaymentFlow[] = [
60
+ {
61
+ id: 'PF-4421',
62
+ object: 'payment_flow',
63
+ live_mode: true,
64
+ created_at: '2024-03-25T12:00:00Z',
65
+ updated_at: '2024-03-25T12:30:00Z',
66
+ client_token: 'pf_tok_4429188',
67
+ status: 'pending',
68
+ amount: 500000,
69
+ currency: 'USD',
70
+ direction: 'debit',
71
+ counterparty_id: 'CP-8801',
72
+ receiving_account_id: null,
73
+ originating_account_id: 'IA-4401'
74
+ }
75
+ ];
76
+
77
+ const Flows: React.FC = () => {
78
+ const [activeTab, setActiveTab] = useState<'account' | 'payment' | 'keys'>('account');
79
+ const [accountFlows, setAccountFlows] = useState<AccountCollectionFlow[]>(INITIAL_ACCOUNT_FLOWS);
80
+ const [paymentFlows, setPaymentFlows] = useState<PaymentFlow[]>(INITIAL_PAYMENT_FLOWS);
81
+ const [apiKeys, setApiKeys] = useState<ApiKey[]>([]);
82
+
83
+ const [isSdkModalOpen, setIsSdkModalOpen] = useState(false);
84
+ const [isInitModalOpen, setIsInitModalOpen] = useState(false);
85
+ const [selectedFlow, setSelectedFlow] = useState<any>(null);
86
+
87
+ // AI SDK Architect State
88
+ const [sdkChat, setSdkChat] = useState<{ role: 'ai' | 'user', text: string }[]>([
89
+ { role: 'ai', text: `LQI Neural Architect active. I see your bridge is live at ${BRIDGE_URL}. I can generate the Node.js or Python code required to securely trigger RTP payments through your bridge. What environment are you connecting from?` }
90
+ ]);
91
+ const [sdkInput, setSdkInput] = useState('');
92
+ const [isAiThinking, setIsAiThinking] = useState(false);
93
+ const [finalCode, setFinalCode] = useState<string | null>(null);
94
+
95
+ // Init Form State
96
+ const [initType, setInitType] = useState<'account' | 'payment'>('account');
97
+ const [initLoading, setInitLoading] = useState(false);
98
+ const [initForm, setInitForm] = useState({ counterpartyId: 'CP-8801', amount: '0', currency: 'USD' });
99
+
100
+ // API Key State
101
+ const [newKeyName, setNewKeyName] = useState('');
102
+ const [generatingKey, setGeneratingKey] = useState(false);
103
+
104
+ const copyToClipboard = (text: string) => {
105
+ navigator.clipboard.writeText(text);
106
+ };
107
+
108
+ const handleInitializeFlow = async () => {
109
+ setInitLoading(true);
110
+ await new Promise(r => setTimeout(r, 1500));
111
+
112
+ const id = initType === 'account' ? `ACF-${Math.floor(1000 + Math.random() * 9000)}` : `PF-${Math.floor(1000 + Math.random() * 9000)}`;
113
+ const newFlow = {
114
+ id,
115
+ object: initType === 'account' ? 'account_collection_flow' : 'payment_flow',
116
+ live_mode: true,
117
+ created_at: new Date().toISOString(),
118
+ updated_at: new Date().toISOString(),
119
+ client_token: `${initType === 'account' ? 'acf' : 'pf'}_tok_${Math.random().toString(36).substring(7)}`,
120
+ status: 'pending' as const,
121
+ counterparty_id: initForm.counterpartyId,
122
+ ...(initType === 'payment' ? {
123
+ amount: parseInt(initForm.amount) * 100,
124
+ currency: initForm.currency,
125
+ direction: 'debit',
126
+ receiving_account_id: null,
127
+ originating_account_id: 'IA-4401'
128
+ } : {
129
+ external_account_id: null,
130
+ payment_types: ['ach', 'wire']
131
+ })
132
+ };
133
+
134
+ if (initType === 'account') {
135
+ setAccountFlows([newFlow as any, ...accountFlows]);
136
+ } else {
137
+ setPaymentFlows([newFlow as any, ...paymentFlows]);
138
+ }
139
+
140
+ setInitLoading(false);
141
+ setIsInitModalOpen(false);
142
+ };
143
+
144
+ const handleSdkChat = async () => {
145
+ if (!sdkInput.trim() || isAiThinking) return;
146
+ const userMsg = sdkInput;
147
+ setSdkChat(prev => [...prev, { role: 'user', text: userMsg }]);
148
+ setSdkInput('');
149
+ setIsAiThinking(true);
150
+
151
+ try {
152
+ const prompt = `
153
+ You are the Lumina Quantum Neural Architect.
154
+ Context: The user has a Node.js bridge hosted at ${BRIDGE_URL}.
155
+ The bridge endpoint is /webhooks/trigger-payment.
156
+ It expects HMAC-SHA256 signatures and a body with: amount, currency, originating_account_id, receiving_account_id, reference_number.
157
+
158
+ Chat History: ${JSON.stringify(sdkChat)}
159
+ User Instruction: ${userMsg}
160
+
161
+ Task:
162
+ 1. If you need more info (e.g. language or auth secret), ask a short, sharp technical question.
163
+ 2. If you have enough info, generate a full implementation snippet to talk to that bridge.
164
+
165
+ Return JSON format: { "reply": "A brief technical explanation of what you are building", "code": "The full code block or null if still asking questions" }
166
+ `;
167
+
168
+ const response = await callGemini(
169
+ 'gemini-3-flash-preview',
170
+ prompt,
171
+ { responseMimeType: "application/json" }
172
+ );
173
+
174
+ const data = JSON.parse(response.candidates?.[0]?.content?.parts?.[0]?.text || '{}');
175
+ setSdkChat(prev => [...prev, { role: 'ai', text: data.reply }]);
176
+ if (data.code) setFinalCode(data.code);
177
+ } catch (err) {
178
+ setSdkChat(prev => [...prev, { role: 'ai', text: "Handshake sync failed. Please re-specify parameters." }]);
179
+ } finally {
180
+ setIsAiThinking(false);
181
+ }
182
+ };
183
+
184
+ const generateApiKey = async () => {
185
+ if (!newKeyName) return;
186
+ setGeneratingKey(true);
187
+ await new Promise(r => setTimeout(r, 1000));
188
+
189
+ const key: ApiKey = {
190
+ id: `key_${Math.random().toString(36).substring(7)}`,
191
+ key: `lq_live_${Math.random().toString(36).substring(2, 15)}`,
192
+ name: newKeyName,
193
+ scope: 'registry.read write.payments',
194
+ created_at: new Date().toISOString(),
195
+ status: 'ACTIVE'
196
+ };
197
+ setApiKeys([key, ...apiKeys]);
198
+ setNewKeyName('');
199
+ setGeneratingKey(false);
200
+ };
201
+
202
+ const revokeKey = (id: string) => {
203
+ setApiKeys(apiKeys.map(k => k.id === id ? { ...k, status: 'REVOKED' } : k));
204
+ };
205
+
206
+ return (
207
+ <div className="space-y-10 animate-in fade-in duration-700">
208
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
209
+ <div>
210
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Onboarding <span className="text-blue-500 not-italic">Flows</span></h2>
211
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Institutional M2M Integration Protocols</p>
212
+ </div>
213
+ <div className="flex gap-4">
214
+ <button
215
+ onClick={() => setIsSdkModalOpen(true)}
216
+ className="flex items-center space-x-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all"
217
+ >
218
+ <Code size={14} />
219
+ <span>SDK Architect</span>
220
+ </button>
221
+ <button
222
+ onClick={() => setIsInitModalOpen(true)}
223
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/30"
224
+ >
225
+ <Zap size={14} />
226
+ <span>Initialize Flow</span>
227
+ </button>
228
+ </div>
229
+ </div>
230
+
231
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
232
+ <div className="flex border-b border-zinc-900 bg-black/20">
233
+ <button
234
+ onClick={() => setActiveTab('account')}
235
+ className={`flex-1 py-6 px-10 text-[10px] font-black uppercase tracking-[0.3em] flex items-center justify-center gap-3 transition-all ${
236
+ activeTab === 'account' ? 'text-blue-500 border-b-2 border-blue-500 bg-blue-500/5' : 'text-zinc-600 hover:text-zinc-400'
237
+ }`}
238
+ >
239
+ <UserPlus size={16} />
240
+ Account Collection
241
+ </button>
242
+ <button
243
+ onClick={() => setActiveTab('payment')}
244
+ className={`flex-1 py-6 px-10 text-[10px] font-black uppercase tracking-[0.3em] flex items-center justify-center gap-3 transition-all ${
245
+ activeTab === 'payment' ? 'text-blue-500 border-b-2 border-blue-500 bg-blue-500/5' : 'text-zinc-600 hover:text-zinc-400'
246
+ }`}
247
+ >
248
+ <ArrowRightLeft size={16} />
249
+ Payment Onboarding
250
+ </button>
251
+ <button
252
+ onClick={() => setActiveTab('keys')}
253
+ className={`flex-1 py-6 px-10 text-[10px] font-black uppercase tracking-[0.3em] flex items-center justify-center gap-3 transition-all ${
254
+ activeTab === 'keys' ? 'text-emerald-500 border-b-2 border-emerald-500 bg-emerald-500/5' : 'text-zinc-600 hover:text-zinc-400'
255
+ }`}
256
+ >
257
+ <Key size={16} />
258
+ Node Access Keys
259
+ </button>
260
+ </div>
261
+
262
+ <div className="p-10 min-h-[400px]">
263
+ {activeTab === 'keys' ? (
264
+ <div className="space-y-8">
265
+ <div className="bg-black/40 border border-zinc-800 rounded-[2rem] p-8">
266
+ <p className="text-[10px] font-black text-zinc-500 uppercase tracking-widest mb-6">Issue Secure Access Token</p>
267
+ <div className="flex gap-4">
268
+ <input
269
+ value={newKeyName}
270
+ onChange={e => setNewKeyName(e.target.value)}
271
+ placeholder="External App Name"
272
+ className="flex-1 bg-black border border-zinc-800 rounded-xl px-6 py-4 text-white text-xs outline-none focus:border-emerald-500/50"
273
+ />
274
+ <button onClick={generateApiKey} disabled={generatingKey} className="px-8 bg-emerald-600 hover:bg-emerald-500 text-white rounded-xl font-black text-[10px] uppercase tracking-widest transition-all">
275
+ {generatingKey ? <Loader2 size={16} className="animate-spin" /> : 'Issue Token'}
276
+ </button>
277
+ </div>
278
+ </div>
279
+ <div className="space-y-4">
280
+ {apiKeys.map(k => (
281
+ <div key={k.id} className="bg-zinc-900 border border-zinc-800 p-6 rounded-2xl flex justify-between items-center group transition-all">
282
+ <div className="flex items-center gap-6">
283
+ <div className={`p-4 rounded-xl ${k.status === 'ACTIVE' ? 'bg-emerald-500/10 text-emerald-500' : 'bg-zinc-800 text-zinc-600'}`}>
284
+ <Key size={18} />
285
+ </div>
286
+ <div>
287
+ <p className="text-white font-black text-xs uppercase italic">{k.name}</p>
288
+ <p className="text-[9px] text-zinc-600 font-mono mt-1">{k.key}</p>
289
+ </div>
290
+ </div>
291
+ <div className="flex items-center gap-4">
292
+ {k.status === 'ACTIVE' && <button onClick={() => revokeKey(k.id)} className="p-2 text-rose-500 hover:bg-rose-500/10 rounded-lg transition-colors"><Trash2 size={16} /></button>}
293
+ <div className={`px-3 py-1 rounded-full text-[8px] font-black uppercase tracking-widest ${k.status === 'ACTIVE' ? 'bg-emerald-500/10 text-emerald-500' : 'bg-rose-500/10 text-rose-500'}`}>{k.status}</div>
294
+ </div>
295
+ </div>
296
+ ))}
297
+ </div>
298
+ </div>
299
+ ) : (
300
+ <div className="space-y-4">
301
+ {(activeTab === 'account' ? accountFlows : paymentFlows).map((flow: any) => (
302
+ <div key={flow.id} className="bg-black/40 border border-zinc-900 rounded-[2rem] p-8 group hover:border-zinc-700 transition-all flex flex-col lg:flex-row justify-between items-center gap-8">
303
+ <div className="flex items-center gap-6">
304
+ <div className="w-14 h-14 bg-zinc-900 border border-zinc-800 rounded-2xl flex items-center justify-center text-zinc-500 group-hover:text-blue-500 transition-all">
305
+ {activeTab === 'account' ? <UserPlus size={24} /> : <ArrowRightLeft size={24} />}
306
+ </div>
307
+ <div>
308
+ <div className="flex items-center gap-3 mb-1">
309
+ <p className="text-white font-black text-sm uppercase italic">{flow.id}</p>
310
+ <StatusBadge status={flow.status} />
311
+ </div>
312
+ <p className="text-[10px] text-zinc-600 font-bold uppercase tracking-widest">CP_REF: {flow.counterparty_id}</p>
313
+ </div>
314
+ </div>
315
+ <div className="flex items-center gap-4">
316
+ <button
317
+ onClick={() => setSelectedFlow(flow)}
318
+ className="px-8 py-3 bg-zinc-900 hover:bg-white hover:text-black text-white rounded-xl text-[10px] font-black uppercase tracking-widest transition-all border border-zinc-800"
319
+ >
320
+ View Flow
321
+ </button>
322
+ <button className="p-3 text-zinc-800 hover:text-white transition-colors"><MoreHorizontal size={18} /></button>
323
+ </div>
324
+ </div>
325
+ ))}
326
+ </div>
327
+ )}
328
+ </div>
329
+ </div>
330
+
331
+ {/* SDK Architect Modal */}
332
+ {isSdkModalOpen && (
333
+ <div className="fixed inset-0 z-[120] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
334
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-5xl rounded-[3rem] p-10 shadow-2xl flex flex-col h-[85vh] animate-in zoom-in-95">
335
+ <div className="flex justify-between items-start mb-8">
336
+ <div className="flex items-center gap-4">
337
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center">
338
+ <Code size={28} />
339
+ </div>
340
+ <div>
341
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">SDK <span className="text-blue-500 not-italic">Architect</span></h3>
342
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest flex items-center gap-2">
343
+ <Globe size={12} className="text-emerald-500" />
344
+ Targeting: {BRIDGE_URL}
345
+ </p>
346
+ </div>
347
+ </div>
348
+ <button onClick={() => setIsSdkModalOpen(false)} className="p-2 text-zinc-500 hover:text-white transition-colors"><X size={24} /></button>
349
+ </div>
350
+
351
+ <div className="flex-1 flex gap-8 overflow-hidden">
352
+ <div className="flex-1 flex flex-col bg-black border border-zinc-900 rounded-[2rem] overflow-hidden">
353
+ <div className="flex-1 overflow-y-auto p-8 space-y-8 custom-scrollbar">
354
+ {sdkChat.map((m, i) => (
355
+ <div key={i} className={`flex ${m.role === 'user' ? 'justify-end' : 'justify-start'}`}>
356
+ <div className={`max-w-[85%] p-6 rounded-[1.8rem] text-sm leading-relaxed ${m.role === 'user' ? 'bg-blue-600 text-white rounded-tr-none shadow-xl shadow-blue-900/20' : 'bg-zinc-900 text-zinc-300 rounded-tl-none border border-zinc-800'}`}>
357
+ {m.text}
358
+ </div>
359
+ </div>
360
+ ))}
361
+ {isAiThinking && <ThinkingState />}
362
+ </div>
363
+ <div className="p-6 bg-zinc-900/50 border-t border-zinc-800 flex gap-4">
364
+ <input
365
+ value={sdkInput}
366
+ onChange={e => setSdkInput(e.target.value)}
367
+ onKeyDown={e => e.key === 'Enter' && handleSdkChat()}
368
+ placeholder="e.g. Write a Node.js client to trigger a payment of $500..."
369
+ className="flex-1 bg-black border border-zinc-800 rounded-2xl px-6 py-4 text-white text-xs outline-none focus:border-blue-500 transition-all font-bold"
370
+ />
371
+ <button onClick={handleSdkChat} disabled={isAiThinking} className="p-4 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl transition-all shadow-lg shadow-blue-900/40">
372
+ <ArrowRight size={20} />
373
+ </button>
374
+ </div>
375
+ </div>
376
+
377
+ <div className="w-1/2 bg-zinc-900 border border-zinc-800 rounded-[2rem] flex flex-col overflow-hidden">
378
+ <div className="bg-zinc-800/80 px-8 py-5 border-b border-zinc-700 flex justify-between items-center">
379
+ <div className="flex items-center gap-3">
380
+ <Terminal size={14} className="text-blue-500" />
381
+ <span className="text-[10px] font-black uppercase text-zinc-400">Implementation Script</span>
382
+ </div>
383
+ {finalCode && <button onClick={() => copyToClipboard(finalCode)} className="p-2 hover:bg-zinc-700 rounded-lg text-zinc-500 hover:text-white transition-all"><Copy size={16} /></button>}
384
+ </div>
385
+ <div className="flex-1 p-8 font-mono text-[11px] text-zinc-400 overflow-y-auto custom-scrollbar">
386
+ {finalCode ? <pre className="whitespace-pre-wrap">{finalCode}</pre> : (
387
+ <div className="h-full flex flex-col items-center justify-center text-center opacity-30">
388
+ <Cpu size={48} className="mb-4" />
389
+ <p className="text-[10px] font-black uppercase tracking-widest">Architect Idle</p>
390
+ </div>
391
+ )}
392
+ </div>
393
+ </div>
394
+ </div>
395
+ </div>
396
+ </div>
397
+ )}
398
+
399
+ {/* Init Modal */}
400
+ {isInitModalOpen && (
401
+ <div className="fixed inset-0 z-[120] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
402
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3rem] p-10 shadow-2xl animate-in zoom-in-95">
403
+ <div className="flex justify-between items-start mb-8">
404
+ <div className="flex items-center gap-4">
405
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center">
406
+ <Zap size={28} />
407
+ </div>
408
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Initialize <span className="text-blue-500 not-italic">Flow</span></h3>
409
+ </div>
410
+ <button onClick={() => setIsInitModalOpen(false)} className="p-2 text-zinc-500 hover:text-white transition-colors"><X size={24} /></button>
411
+ </div>
412
+ <div className="space-y-6">
413
+ <div className="grid grid-cols-2 gap-4">
414
+ <button onClick={() => setInitType('account')} className={`p-6 rounded-2xl border transition-all ${initType === 'account' ? 'bg-blue-600/10 border-blue-600' : 'bg-zinc-900 border-zinc-800 opacity-50'}`}>
415
+ <UserPlus className="mb-4" size={20} />
416
+ <p className="text-white font-black text-[10px] uppercase">Account Col.</p>
417
+ </button>
418
+ <button onClick={() => setInitType('payment')} className={`p-6 rounded-2xl border transition-all ${initType === 'payment' ? 'bg-blue-600/10 border-blue-600' : 'bg-zinc-900 border-zinc-800 opacity-50'}`}>
419
+ <ArrowRightLeft className="mb-4" size={20} />
420
+ <p className="text-white font-black text-[10px] uppercase">Payment Onbd.</p>
421
+ </button>
422
+ </div>
423
+ <div className="space-y-4">
424
+ <div className="space-y-2">
425
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Target CP</label>
426
+ <input value={initForm.counterpartyId} onChange={e => setInitForm({...initForm, counterpartyId: e.target.value})} className="w-full bg-black border border-zinc-800 rounded-xl p-4 text-white text-xs outline-none" />
427
+ </div>
428
+ {initType === 'payment' && (
429
+ <div className="space-y-2">
430
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Limit (USD)</label>
431
+ <input type="number" value={initForm.amount} onChange={e => setInitForm({...initForm, amount: e.target.value})} className="w-full bg-black border border-zinc-800 rounded-xl p-4 text-white text-xs outline-none" />
432
+ </div>
433
+ )}
434
+ </div>
435
+ <button onClick={handleInitializeFlow} disabled={initLoading} className="w-full py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all shadow-xl flex items-center justify-center gap-4">
436
+ {initLoading ? <Loader2 size={20} className="animate-spin" /> : <Plus size={20} />}
437
+ Establish Flow
438
+ </button>
439
+ </div>
440
+ </div>
441
+ </div>
442
+ )}
443
+
444
+ {/* Detail Modal */}
445
+ {selectedFlow && (
446
+ <div className="fixed inset-0 z-[130] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
447
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-2xl rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95">
448
+ <div className="flex justify-between items-start mb-10 pb-8 border-b border-zinc-900">
449
+ <div className="flex items-center gap-6">
450
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 rounded-3xl flex items-center justify-center border border-blue-500/20">
451
+ {selectedFlow.object === 'account_collection_flow' ? <UserPlus size={32} /> : <ArrowRightLeft size={32} />}
452
+ </div>
453
+ <div>
454
+ <h3 className="text-3xl font-black text-white italic tracking-tighter uppercase">{selectedFlow.id}</h3>
455
+ <div className="flex gap-3 mt-2">
456
+ <StatusBadge status={selectedFlow.status} />
457
+ <span className="text-[9px] font-black uppercase text-zinc-600 px-3 py-1 bg-zinc-900 rounded-full tracking-widest">LIVE_NODE</span>
458
+ </div>
459
+ </div>
460
+ </div>
461
+ <button onClick={() => setSelectedFlow(null)} className="p-3 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
462
+ </div>
463
+ <div className="space-y-8">
464
+ <div className="grid grid-cols-2 gap-10">
465
+ <div>
466
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-2">Registry Entity</p>
467
+ <p className="text-white font-bold">{selectedFlow.counterparty_id}</p>
468
+ </div>
469
+ <div>
470
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-2">Sync Integrity</p>
471
+ <div className="flex items-center gap-2">
472
+ <div className="w-2 h-2 bg-emerald-500 rounded-full animate-pulse shadow-[0_0_8px_#10b981]"></div>
473
+ <span className="text-emerald-500 font-black text-[10px] uppercase">Node Synchronized</span>
474
+ </div>
475
+ </div>
476
+ </div>
477
+ <div className="p-8 bg-black border border-zinc-900 rounded-[2.5rem] space-y-4">
478
+ <div className="flex justify-between items-center">
479
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">Node Handshake Token</p>
480
+ <button onClick={() => copyToClipboard(selectedFlow.client_token)} className="text-blue-500 hover:text-blue-400 transition-colors"><Copy size={16} /></button>
481
+ </div>
482
+ <div className="bg-zinc-950 p-5 rounded-2xl border border-zinc-900 font-mono text-[11px] text-blue-400/80 break-all select-all">
483
+ {selectedFlow.client_token}
484
+ </div>
485
+ </div>
486
+ <div className="flex gap-4">
487
+ <button className="flex-1 py-5 bg-zinc-900 hover:bg-zinc-800 text-white rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all border border-zinc-800 flex items-center justify-center gap-3">
488
+ <Eye size={18} /> Inspect Signals
489
+ </button>
490
+ <button className="flex-1 py-5 bg-white hover:bg-zinc-100 text-black rounded-[2rem] font-black text-xs uppercase tracking-widest transition-all shadow-2xl">
491
+ Refresh Ledger
492
+ </button>
493
+ </div>
494
+ </div>
495
+ </div>
496
+ </div>
497
+ )}
498
+ </div>
499
+ );
500
+ };
501
+
502
+ const ThinkingState = () => (
503
+ <div className="flex items-center gap-4 text-zinc-500">
504
+ <Loader2 className="animate-spin" size={18} />
505
+ <span className="text-[10px] font-black uppercase tracking-[0.3em] animate-pulse">Calculating Neural Implementation...</span>
506
+ </div>
507
+ );
508
+
509
+ const StatusBadge = ({ status }: { status: string }) => {
510
+ const styles: Record<string, string> = {
511
+ pending: 'bg-amber-500/10 text-amber-500 border border-amber-500/20',
512
+ completed: 'bg-emerald-500/10 text-emerald-500 border border-emerald-500/20',
513
+ cancelled: 'bg-rose-500/10 text-rose-500 border border-rose-500/20',
514
+ };
515
+ return (
516
+ <div className={`inline-flex items-center px-3 py-1 rounded-full text-[9px] font-black uppercase tracking-widest ${styles[status] || 'bg-zinc-800 text-zinc-500'}`}>
517
+ {status}
518
+ </div>
519
+ );
520
+ };
521
+
522
+ export default Flows;
views/InternalAccounts.tsx ADDED
@@ -0,0 +1,251 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import {
4
+ Building2,
5
+ RefreshCw,
6
+ Activity,
7
+ Search,
8
+ History,
9
+ ExternalLink,
10
+ MoreHorizontal,
11
+ ArrowUpRight,
12
+ ArrowDownRight,
13
+ X,
14
+ Loader2,
15
+ Fingerprint,
16
+ User,
17
+ ShieldCheck,
18
+ Mail,
19
+ Phone,
20
+ MapPin,
21
+ Globe,
22
+ Terminal,
23
+ CreditCard
24
+ } from 'lucide-react';
25
+ import { apiClient } from '../services/api.ts';
26
+ import { InternalAccount, CustomerProfileResponse } from '../types/index.ts';
27
+ import { TableRowSkeleton, Skeleton } from '../components/Skeleton.tsx';
28
+
29
+ const InternalAccounts: React.FC = () => {
30
+ const [loading, setLoading] = useState(false);
31
+ const [accounts, setAccounts] = useState<InternalAccount[]>([]);
32
+ const [isModalOpen, setIsModalOpen] = useState(false);
33
+ const [connecting, setConnecting] = useState(false);
34
+ const [newBank, setNewBank] = useState('Wells Fargo');
35
+
36
+ const [isProfileOpen, setIsProfileOpen] = useState(false);
37
+ const [fetchingProfile, setFetchingProfile] = useState(false);
38
+ const [activeProfile, setActiveProfile] = useState<CustomerProfileResponse | null>(null);
39
+
40
+ const fetchAccounts = async () => {
41
+ setLoading(true);
42
+ try {
43
+ const mappedAccounts = await apiClient.getRegistryNodes();
44
+ setAccounts(mappedAccounts);
45
+ } catch (e) {
46
+ console.error(e);
47
+ } finally {
48
+ setLoading(false);
49
+ }
50
+ };
51
+
52
+ const handleViewProfile = async (accountId: string) => {
53
+ setIsProfileOpen(true);
54
+ setFetchingProfile(true);
55
+ setActiveProfile(null);
56
+ try {
57
+ const data = await apiClient.getCustomerProfile(accountId);
58
+ // Brief aesthetic pause for "neural verification"
59
+ await new Promise(r => setTimeout(r, 800));
60
+ setActiveProfile(data);
61
+ } catch (err) {
62
+ console.error(err);
63
+ } finally {
64
+ setFetchingProfile(false);
65
+ }
66
+ };
67
+
68
+ useEffect(() => {
69
+ fetchAccounts();
70
+ }, []);
71
+
72
+ return (
73
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20">
74
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
75
+ <div>
76
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Internal <span className="text-blue-500 not-italic">Registry</span></h2>
77
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">Managed Corporate Nodes & Citi-Sync v2.0.0</p>
78
+ </div>
79
+ <div className="flex gap-4">
80
+ <button
81
+ onClick={fetchAccounts}
82
+ disabled={loading}
83
+ className="flex items-center space-x-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all"
84
+ >
85
+ <RefreshCw size={14} className={loading ? 'animate-spin' : ''} />
86
+ <span>{loading ? 'Polling Nodes...' : 'Sync All Balances'}</span>
87
+ </button>
88
+ <button
89
+ onClick={() => setIsModalOpen(true)}
90
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/30"
91
+ >
92
+ <Building2 size={14} />
93
+ <span>Establish Connection</span>
94
+ </button>
95
+ </div>
96
+ </div>
97
+
98
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
99
+ <div className="p-10 border-b border-zinc-900 flex justify-between items-center bg-black/20">
100
+ <div className="flex items-center space-x-4">
101
+ <div className="p-3 bg-blue-500/10 text-blue-500 rounded-2xl">
102
+ <Activity size={20} />
103
+ </div>
104
+ <h3 className="text-white font-black uppercase tracking-[0.2em] italic">Real-time Node Status</h3>
105
+ </div>
106
+ <div className="relative">
107
+ <Search className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600" size={14} />
108
+ <input
109
+ placeholder="Filter Nodes..."
110
+ className="bg-black border border-zinc-800 rounded-xl py-2 pl-10 pr-4 text-[10px] font-black uppercase tracking-widest text-white outline-none focus:border-blue-500/50 w-64 transition-all"
111
+ />
112
+ </div>
113
+ </div>
114
+
115
+ <div className="overflow-x-auto">
116
+ <table className="w-full text-left border-collapse">
117
+ <thead>
118
+ <tr className="border-b border-zinc-900 bg-black/40 text-[9px] font-black uppercase tracking-[0.3em] text-zinc-500">
119
+ <th className="px-10 py-5">Node Identity</th>
120
+ <th className="py-5">Host Institution</th>
121
+ <th className="py-5">Sync Status</th>
122
+ <th className="py-5 text-right">Balance (USD)</th>
123
+ <th className="px-10 py-5 text-right">Action</th>
124
+ </tr>
125
+ </thead>
126
+ <tbody className="divide-y divide-zinc-900/50">
127
+ {loading ? (
128
+ <>
129
+ <TableRowSkeleton />
130
+ <TableRowSkeleton />
131
+ <TableRowSkeleton />
132
+ </>
133
+ ) : accounts.map(node => (
134
+ <tr key={node.id} className="group hover:bg-white/[0.02] transition-colors">
135
+ <td className="px-10 py-8">
136
+ <div className="flex items-center gap-4">
137
+ <div className="w-10 h-10 rounded-xl bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-500 group-hover:text-blue-400 transition-colors">
138
+ {node.productName.toLowerCase().includes('card') ? <CreditCard size={18} /> : <Building2 size={18} />}
139
+ </div>
140
+ <div>
141
+ <p className="text-white font-black text-xs uppercase italic">{node.productName}</p>
142
+ <p className="text-[10px] text-zinc-600 font-mono font-bold mt-1 uppercase tracking-tighter">{node.displayAccountNumber}</p>
143
+ </div>
144
+ </div>
145
+ </td>
146
+ <td className="py-8">
147
+ <p className="text-xs font-bold text-zinc-400 uppercase tracking-widest">{node.institutionName}</p>
148
+ <p className="text-[9px] text-zinc-600 font-bold uppercase mt-1">Ref: {node.connectionId}</p>
149
+ </td>
150
+ <td className="py-8">
151
+ <div className={`inline-flex items-center space-x-2 px-3 py-1 rounded-full border ${
152
+ node.status === 'ACTIVE' ? 'bg-emerald-500/5 border-emerald-500/20 text-emerald-500' : 'bg-zinc-500/5 border-zinc-500/20 text-zinc-500'
153
+ }`}>
154
+ <div className={`w-1.5 h-1.5 rounded-full ${node.status === 'ACTIVE' ? 'bg-emerald-500 animate-pulse' : 'bg-zinc-500'}`}></div>
155
+ <span className="text-[9px] font-black uppercase tracking-widest">{node.status}</span>
156
+ </div>
157
+ </td>
158
+ <td className="py-8 text-right">
159
+ <p className="text-sm font-black text-white mono tracking-tighter">${node.currentBalance.toLocaleString()}</p>
160
+ <p className="text-[9px] text-zinc-600 font-bold uppercase mt-1">Available: ${node.availableBalance.toLocaleString()}</p>
161
+ </td>
162
+ <td className="px-10 py-8 text-right">
163
+ <div className="flex items-center justify-end gap-3 opacity-0 group-hover:opacity-100 transition-opacity">
164
+ <button onClick={() => handleViewProfile(node.id)} className="p-2 text-zinc-500 hover:text-blue-500" title="View Account Profile"><User size={16} /></button>
165
+ <button className="p-2 text-zinc-500 hover:text-white"><History size={16} /></button>
166
+ <button className="p-2 text-zinc-500 hover:text-blue-500"><ExternalLink size={16} /></button>
167
+ <button className="p-2 text-zinc-500 hover:text-white"><MoreHorizontal size={16} /></button>
168
+ </div>
169
+ </td>
170
+ </tr>
171
+ ))}
172
+ </tbody>
173
+ </table>
174
+ </div>
175
+ </div>
176
+
177
+ {/* Account Profile Modal */}
178
+ {isProfileOpen && (
179
+ <div className="fixed inset-0 z-[120] flex items-center justify-center p-6 backdrop-blur-md bg-black/80">
180
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-2xl rounded-[3.5rem] p-12 shadow-2xl animate-in zoom-in-95 overflow-hidden relative">
181
+ <div className="absolute top-0 right-0 p-10 opacity-5">
182
+ <Fingerprint size={200} className="text-blue-500" />
183
+ </div>
184
+
185
+ <div className="relative z-10">
186
+ <div className="flex justify-between items-start mb-12">
187
+ <div className="flex items-center gap-6">
188
+ <div className="w-16 h-16 bg-blue-600/10 text-blue-500 border border-blue-500/20 rounded-3xl flex items-center justify-center">
189
+ <ShieldCheck size={32} />
190
+ </div>
191
+ <div>
192
+ <h3 className="text-3xl font-black text-white italic tracking-tighter uppercase leading-none">Identity <span className="text-blue-500 not-italic">Vault</span></h3>
193
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest mt-2">Compliance Level: FDX v6.0</p>
194
+ </div>
195
+ </div>
196
+ <button onClick={() => setIsProfileOpen(false)} className="p-3 text-zinc-600 hover:text-white transition-colors bg-zinc-900 rounded-2xl"><X size={24} /></button>
197
+ </div>
198
+
199
+ {fetchingProfile ? (
200
+ <div className="h-[400px] flex flex-col items-center justify-center space-y-8">
201
+ <Loader2 className="animate-spin text-blue-500" size={48} />
202
+ <p className="text-zinc-600 text-[10px] font-black uppercase tracking-[0.4em] animate-pulse">Requesting Profile Node...</p>
203
+ </div>
204
+ ) : activeProfile ? (
205
+ <div className="space-y-10 animate-in slide-in-from-bottom-6">
206
+ <div className="grid grid-cols-2 gap-10">
207
+ <div className="space-y-6">
208
+ <div className="p-6 bg-black border border-zinc-900 rounded-3xl">
209
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-3 flex items-center gap-2"><User size={12} className="text-blue-500" />Legal Name Registry</p>
210
+ <h4 className="text-white font-bold text-lg italic uppercase">{activeProfile.customer.title} {activeProfile.customer.firstName} {activeProfile.customer.lastName}</h4>
211
+ <p className="text-[9px] text-zinc-500 font-black uppercase mt-1 tracking-widest">{activeProfile.customer.companyName}</p>
212
+ </div>
213
+ <div className="p-6 bg-black border border-zinc-900 rounded-3xl">
214
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-3 flex items-center gap-2"><Mail size={12} className="text-blue-500" />Verified Signals</p>
215
+ {activeProfile.contacts.emails.map((email, i) => (
216
+ <p key={i} className="text-blue-400 font-mono text-xs mb-1">{email}</p>
217
+ ))}
218
+ </div>
219
+ </div>
220
+ <div className="space-y-6">
221
+ <div className="p-8 bg-black border border-zinc-900 rounded-[2.5rem]">
222
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-4 flex items-center gap-2"><MapPin size={12} className="text-blue-500" />Geospatial Registry</p>
223
+ {activeProfile.contacts.addresses.map((addr, i) => (
224
+ <div key={i} className="space-y-1">
225
+ <p className="text-white font-bold text-xs uppercase">{addr.addressLine1}</p>
226
+ <p className="text-zinc-500 text-xs font-medium">{addr.city}, {addr.region} {addr.postalCode}</p>
227
+ </div>
228
+ ))}
229
+ </div>
230
+ <div className="p-6 bg-blue-600/5 border border-blue-500/10 rounded-3xl">
231
+ <p className="text-[9px] font-black text-blue-500 uppercase tracking-widest mb-3 flex items-center gap-2"><Terminal size={10} />Node Metadata</p>
232
+ <p className="text-[9px] text-zinc-600 font-mono leading-relaxed">
233
+ PROTOCOL: FDX_V6_NATIVE<br />ACTOR: PARTNER_USER<br />TRUST_SCORE: 100/100
234
+ </p>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ <button onClick={() => setIsProfileOpen(false)} className="w-full py-5 bg-white text-black rounded-[2rem] font-black text-xs uppercase tracking-[0.4em] transition-all hover:bg-zinc-200 shadow-2xl">
239
+ Seal Identity Node
240
+ </button>
241
+ </div>
242
+ ) : null}
243
+ </div>
244
+ </div>
245
+ </div>
246
+ )}
247
+ </div>
248
+ );
249
+ };
250
+
251
+ export default InternalAccounts;
views/Landing.tsx ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import {
5
+ Cpu, Sparkles, ArrowRight, Loader2, X, Terminal, Mic, Zap, MessageSquare, ChevronDown,
6
+ ShieldCheck, Globe, Activity
7
+ } from 'lucide-react';
8
+ import { speakText, processVoiceCommand } from '../services/geminiService';
9
+ import { apiClient } from '../services/api';
10
+
11
+ // Powerhouse Component Imports
12
+ import NexusNode from './landing/NexusNode';
13
+ import IntelligenceAlpha from './landing/IntelligenceAlpha';
14
+ import SystemFabric from './landing/SystemFabric';
15
+ import RegistryVault from './landing/RegistryVault';
16
+ import PrivacyManifesto from './landing/PrivacyManifesto';
17
+ import AssetFoundry from './landing/AssetFoundry';
18
+ import QuantumCompute from './landing/QuantumCompute';
19
+ import LiquidityMesh from './landing/LiquidityMesh';
20
+ import EncryptionProtocol from './landing/EncryptionProtocol';
21
+ import OracleAuthority from './landing/OracleAuthority';
22
+
23
+ const Landing: React.FC = () => {
24
+ const navigate = useNavigate();
25
+ const [isVoiceOpen, setIsVoiceOpen] = useState(false);
26
+ const [voiceInput, setVoiceInput] = useState('');
27
+ const [isProcessing, setIsProcessing] = useState(false);
28
+ const [isDemoLoading, setIsDemoLoading] = useState(false);
29
+ const [voiceStatus, setVoiceStatus] = useState('Standby for Neural Input...');
30
+
31
+ const handleDemoAccess = async () => {
32
+ if (isDemoLoading) return;
33
+ setIsDemoLoading(true);
34
+ try {
35
+ const { success } = await apiClient.auth.login('alex', 'password123');
36
+ if (success) {
37
+ window.dispatchEvent(new Event('auth-update'));
38
+ navigate('/');
39
+ }
40
+ } catch (err) {
41
+ console.error("Demo handshake failed", err);
42
+ } finally {
43
+ setIsDemoLoading(false);
44
+ }
45
+ };
46
+
47
+ const handleVoiceAction = async () => {
48
+ if (!voiceInput.trim()) return;
49
+ setIsProcessing(true);
50
+ setVoiceStatus('Synchronizing Subspace Signals...');
51
+
52
+ const result = await processVoiceCommand(voiceInput);
53
+
54
+ if (result.action === 'SEND_MONEY') {
55
+ const localPayments = JSON.parse(localStorage.getItem('lumina_payments') || '[]');
56
+ const newPayment = {
57
+ transactionId: `TX-${Math.floor(1000 + Math.random() * 9000)}`,
58
+ baseCurrencyTransactionAmount: -result.amount,
59
+ transactionDescription: `VOICE_AUTHORIZED: ${result.recipient.toUpperCase()}`,
60
+ transactionType: 'Outgoing',
61
+ transactionStatus: 'Completed',
62
+ transactionDateTime: new Date().toISOString(),
63
+ transactionReferenceNumber: `REF-${Math.floor(1000 + Math.random() * 9000)}`,
64
+ category: result.category,
65
+ recipient: result.recipient,
66
+ amount: result.amount
67
+ };
68
+ localStorage.setItem('lumina_payments', JSON.stringify([newPayment, ...localPayments]));
69
+ setVoiceStatus(result.narration);
70
+ await speakText(result.narration);
71
+ setTimeout(() => {
72
+ setIsVoiceOpen(false);
73
+ setVoiceInput('');
74
+ setVoiceStatus('Standby for Neural Input...');
75
+ navigate('/payments');
76
+ }, 3000);
77
+ } else {
78
+ setVoiceStatus(result.narration);
79
+ await speakText(result.narration);
80
+ }
81
+ setIsProcessing(false);
82
+ };
83
+
84
+ return (
85
+ <div className="min-h-screen bg-[#020202] text-white font-sans overflow-x-hidden relative selection:bg-blue-500/30 selection:text-blue-200">
86
+ <div className="fixed inset-0 z-0 pointer-events-none opacity-20">
87
+ <div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_50%_50%,_#1e1b4b_0%,_transparent_70%)]"></div>
88
+ <div className="absolute inset-0 bg-[linear-gradient(to_right,#80808012_1px,transparent_1px),linear-gradient(to_bottom,#80808012_1px,transparent_1px)] bg-[size:60px_60px]"></div>
89
+ </div>
90
+
91
+ <nav className="fixed top-0 left-0 w-full z-[100] px-10 py-6 flex justify-between items-center backdrop-blur-md bg-black/10 border-b border-white/5">
92
+ <div className="flex items-center gap-4">
93
+ <div className="w-10 h-10 bg-white rounded-xl flex items-center justify-center">
94
+ <Cpu size={20} className="text-black" />
95
+ </div>
96
+ <div>
97
+ <h1 className="text-sm font-black italic tracking-tighter text-white uppercase leading-none">Lumina <span className="text-blue-500 not-italic">Quantum</span></h1>
98
+ <p className="text-[8px] uppercase tracking-[0.4em] font-bold text-zinc-600">Institutional Ledger</p>
99
+ </div>
100
+ </div>
101
+ <div className="hidden md:flex items-center gap-10">
102
+ <button onClick={handleDemoAccess} className="px-6 py-3 bg-blue-600 text-white rounded-full font-black text-[9px] uppercase tracking-widest hover:bg-blue-500 transition-all flex items-center gap-2">
103
+ {isDemoLoading ? <Loader2 size={12} className="animate-spin" /> : <Sparkles size={12} />}
104
+ Test Drive
105
+ </button>
106
+ <button onClick={() => navigate('/login')} className="px-8 py-3 bg-white text-black rounded-full font-black text-[9px] uppercase tracking-widest hover:bg-zinc-200 transition-all shadow-xl">Terminal</button>
107
+ </div>
108
+ </nav>
109
+
110
+ <section className="relative z-10 min-h-screen flex flex-col justify-center items-center max-w-7xl mx-auto px-10 pt-32 pb-20 text-center">
111
+ <div className="inline-flex items-center gap-3 px-6 py-2 bg-blue-500/10 border border-blue-500/20 rounded-full animate-in zoom-in-95 duration-700 mb-10">
112
+ <Sparkles size={14} className="text-blue-500" />
113
+ <span className="text-[10px] font-black uppercase tracking-[0.4em] text-blue-500">Node Architecture Verified • v6.2.0</span>
114
+ </div>
115
+ <h1 className="text-[8rem] lg:text-[11rem] font-black italic tracking-tighter uppercase leading-[0.75] mb-10">Quantum <br /><span className="text-blue-600 not-italic">Ledger</span></h1>
116
+ <p className="text-zinc-500 text-xl lg:text-2xl font-medium italic max-w-2xl mx-auto mb-10">"The definitive interface for high-velocity institutional assets. Route neural signals through the mesh with zero-latency parity."</p>
117
+ <div className="flex flex-wrap justify-center gap-8 pt-8">
118
+ <button onClick={handleDemoAccess} className="px-12 py-7 bg-white text-black rounded-[2.5rem] font-black text-xs uppercase tracking-[0.5em] flex items-center gap-6 hover:bg-blue-500 hover:text-white transition-all shadow-[0_30px_60px_rgba(255,255,255,0.1)] group">
119
+ {isDemoLoading ? <Loader2 size={20} className="animate-spin" /> : <span>Start Test Drive</span>}
120
+ <ArrowRight size={20} className="group-hover:translate-x-3 transition-transform" />
121
+ </button>
122
+ <button onClick={() => setIsVoiceOpen(true)} className="px-12 py-7 bg-zinc-950 border border-zinc-800 text-white rounded-[2.5rem] font-black text-xs uppercase tracking-[0.5em] flex items-center gap-6 hover:border-blue-500 transition-all shadow-2xl group">
123
+ <Mic size={20} className="text-blue-500" />
124
+ Neural Command
125
+ </button>
126
+ </div>
127
+ <div className="mt-20 animate-bounce opacity-20"><ChevronDown size={32} /></div>
128
+ </section>
129
+
130
+ <NexusNode />
131
+ <IntelligenceAlpha />
132
+ <SystemFabric />
133
+ <RegistryVault />
134
+ <PrivacyManifesto />
135
+ <AssetFoundry />
136
+ <QuantumCompute />
137
+ <LiquidityMesh />
138
+ <EncryptionProtocol />
139
+ <OracleAuthority />
140
+
141
+ <footer className="relative z-10 py-40 max-w-7xl mx-auto px-10 text-center space-y-24">
142
+ <div className="pt-20 border-t border-white/5 space-y-12">
143
+ <div className="flex justify-center gap-16 opacity-30 grayscale hover:grayscale-0 hover:opacity-100 transition-all duration-700">
144
+ <ShieldCheck size={48} /><Globe size={48} /><Activity size={48} /><Zap size={48} />
145
+ </div>
146
+ <p className="text-[9px] font-black uppercase tracking-[1em] text-zinc-800">Lumina Quantum Systems • Citibank Demo Business Inc • 527 Org Protocol • 2025</p>
147
+ </div>
148
+ </footer>
149
+
150
+ {isVoiceOpen && (
151
+ <div className="fixed inset-0 z-[200] flex items-center justify-center p-6 backdrop-blur-xl bg-black/90">
152
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-2xl rounded-[4rem] p-16 shadow-2xl animate-in zoom-in-95 relative overflow-hidden">
153
+ <div className="relative z-10 space-y-12">
154
+ <div className="flex justify-between items-start">
155
+ <div className="flex items-center gap-6">
156
+ <div className={`p-5 rounded-3xl ${isProcessing ? 'bg-blue-600 animate-pulse' : 'bg-blue-600/10'} text-blue-500 border border-blue-500/20 shadow-2xl shadow-blue-500/5`}>
157
+ <Mic size={32} />
158
+ </div>
159
+ <div>
160
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Neural <span className="text-blue-500 not-italic">Command</span></h3>
161
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest mt-2">Natural Language Treasury Interface</p>
162
+ </div>
163
+ </div>
164
+ <button onClick={() => setIsVoiceOpen(false)} className="p-4 text-zinc-600 hover:text-white transition-colors bg-zinc-900 rounded-2xl"><X size={24} /></button>
165
+ </div>
166
+ <div className="space-y-6">
167
+ <textarea value={voiceInput} onChange={(e) => setVoiceInput(e.target.value)} placeholder="e.g. Send $2,500 to AWS..." className="w-full bg-black border-2 border-zinc-900 focus:border-blue-500/50 rounded-[2.5rem] p-10 text-white text-xl font-bold leading-relaxed tracking-tight h-48 outline-none transition-all resize-none placeholder:text-zinc-800" />
168
+ <div className="flex items-center justify-between px-2">
169
+ <div className="flex items-center gap-3"><div className={`w-2 h-2 rounded-full ${isProcessing ? 'bg-blue-500 animate-ping' : 'bg-emerald-500'}`}></div><span className="text-[10px] font-black text-zinc-500 uppercase tracking-widest">{voiceStatus}</span></div>
170
+ <button onClick={handleVoiceAction} disabled={isProcessing || !voiceInput.trim()} className="px-10 py-5 bg-blue-600 hover:bg-blue-500 disabled:bg-zinc-900 disabled:text-zinc-700 text-white rounded-[2rem] font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/40 flex items-center gap-4">{isProcessing ? <Loader2 className="animate-spin" size={20} /> : <Zap size={20} />}Execute Signal</button>
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ )}
177
+ </div>
178
+ );
179
+ };
180
+
181
+ export default Landing;
views/LineItems.tsx ADDED
@@ -0,0 +1,176 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
4
+ import { useParams, useNavigate } from 'react-router-dom';
5
+ import {
6
+ ListTree,
7
+ ArrowLeft,
8
+ Edit3,
9
+ Save,
10
+ X,
11
+ MoreVertical,
12
+ Search,
13
+ Hash,
14
+ FileText,
15
+ Tag
16
+ } from 'lucide-react';
17
+ import { LineItem, LineItemUpdateRequest } from '../types/index';
18
+
19
+ const MOCK_LINE_ITEMS: LineItem[] = [
20
+ { id: 'LI-001', amount: 12000.50, currency: 'USD', description: 'Monthly Compute - Node A', ledger_account_id: 'LED-991', itemizable_id: 'TXN-8821', itemizable_type: 'payment_orders', createdAt: '2024-03-25' },
21
+ { id: 'LI-002', amount: 2500.00, currency: 'USD', description: 'Storage Surcharge - Region East', ledger_account_id: 'LED-992', itemizable_id: 'TXN-8821', itemizable_type: 'payment_orders', createdAt: '2024-03-25' },
22
+ { id: 'LI-003', amount: 30499.50, currency: 'USD', description: 'Quantum Networking Handshake', ledger_account_id: 'LED-991', itemizable_id: 'TXN-8821', itemizable_type: 'payment_orders', createdAt: '2024-03-25' },
23
+ ];
24
+
25
+ const LineItems: React.FC = () => {
26
+ const { type, id } = useParams<{ type: string; id: string }>();
27
+ const navigate = useNavigate();
28
+ const [items, setItems] = useState<LineItem[]>(MOCK_LINE_ITEMS);
29
+ const [editingId, setEditingId] = useState<string | null>(null);
30
+ const [editForm, setEditForm] = useState<LineItemUpdateRequest>({});
31
+
32
+ const handleEdit = (item: LineItem) => {
33
+ setEditingId(item.id);
34
+ setEditForm({ description: item.description });
35
+ };
36
+
37
+ const handleSave = (id: string) => {
38
+ setItems(prev => prev.map(item => item.id === id ? { ...item, ...editForm } : item));
39
+ setEditingId(null);
40
+ };
41
+
42
+ return (
43
+ <div className="space-y-10 animate-in fade-in duration-700">
44
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
45
+ <div className="flex items-center gap-6">
46
+ <button
47
+ onClick={() => navigate(-1)}
48
+ className="p-3 bg-zinc-900 border border-zinc-800 rounded-2xl text-zinc-500 hover:text-white transition-all"
49
+ >
50
+ <ArrowLeft size={20} />
51
+ </button>
52
+ <div>
53
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Line <span className="text-blue-500 not-italic">Ledger</span></h2>
54
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">
55
+ {type?.replace('_', ' ')} ID: <span className="text-zinc-400 mono">{id}</span>
56
+ </p>
57
+ </div>
58
+ </div>
59
+ <div className="flex gap-4">
60
+ <div className="relative">
61
+ <Search className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-600" size={14} />
62
+ <input
63
+ placeholder="Search Items..."
64
+ className="bg-black border border-zinc-800 rounded-xl py-2 pl-10 pr-4 text-[10px] font-black uppercase tracking-widest text-white outline-none focus:border-blue-500/50 transition-all w-64"
65
+ />
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl">
71
+ <div className="p-10 border-b border-zinc-900 flex justify-between items-center bg-black/20">
72
+ <div className="flex items-center space-x-4">
73
+ <div className="p-3 bg-blue-500/10 text-blue-500 rounded-2xl">
74
+ <ListTree size={20} />
75
+ </div>
76
+ <h3 className="text-white font-black uppercase tracking-[0.2em] italic">Granular Breakdown</h3>
77
+ </div>
78
+ </div>
79
+
80
+ <div className="overflow-x-auto">
81
+ <table className="w-full text-left">
82
+ <thead>
83
+ <tr className="border-b border-zinc-900 bg-black/40 text-[9px] font-black uppercase tracking-[0.3em] text-zinc-500">
84
+ <th className="px-10 py-5">Item Detail</th>
85
+ <th className="py-5">Ledger Mapping</th>
86
+ <th className="py-5">Created At</th>
87
+ <th className="py-5 text-right">Amount (USD)</th>
88
+ <th className="px-10 py-5 text-right">Actions</th>
89
+ </tr>
90
+ </thead>
91
+ <tbody className="divide-y divide-zinc-900/50">
92
+ {items.map(item => (
93
+ <tr key={item.id} className="group hover:bg-white/[0.01] transition-colors">
94
+ <td className="px-10 py-8">
95
+ <div className="flex items-center gap-4">
96
+ <div className="w-10 h-10 rounded-xl bg-zinc-900 border border-zinc-800 flex items-center justify-center text-zinc-500 group-hover:text-blue-400 transition-colors">
97
+ <Tag size={18} />
98
+ </div>
99
+ <div className="flex-1 min-w-[300px]">
100
+ {editingId === item.id ? (
101
+ <input
102
+ value={editForm.description}
103
+ onChange={e => setEditForm({ ...editForm, description: e.target.value })}
104
+ className="w-full bg-black border border-zinc-800 rounded-lg px-3 py-1 text-xs text-white outline-none focus:border-blue-500/50"
105
+ autoFocus
106
+ />
107
+ ) : (
108
+ <p className="text-white font-black text-xs uppercase italic">{item.description}</p>
109
+ )}
110
+ <p className="text-[10px] text-zinc-600 font-mono font-bold mt-1 uppercase tracking-tighter">ID: {item.id}</p>
111
+ </div>
112
+ </div>
113
+ </td>
114
+ <td className="py-8">
115
+ <div className="flex items-center gap-2">
116
+ <Hash size={12} className="text-zinc-600" />
117
+ <p className="text-[10px] font-black text-zinc-400 uppercase tracking-widest">{item.ledger_account_id}</p>
118
+ </div>
119
+ </td>
120
+ <td className="py-8">
121
+ <p className="text-[10px] font-bold text-zinc-500 uppercase tracking-widest">{item.createdAt}</p>
122
+ </td>
123
+ <td className="py-8 text-right">
124
+ <p className="text-sm font-black text-white mono tracking-tighter">${item.amount.toLocaleString(undefined, { minimumFractionDigits: 2 })}</p>
125
+ <p className="text-[9px] text-zinc-600 font-bold uppercase mt-1">{item.currency}</p>
126
+ </td>
127
+ <td className="px-10 py-8 text-right">
128
+ <div className="flex items-center justify-end gap-3">
129
+ {editingId === item.id ? (
130
+ <>
131
+ <button onClick={() => handleSave(item.id)} className="p-2 text-emerald-500 hover:text-emerald-400 transition-colors">
132
+ <Save size={18} />
133
+ </button>
134
+ <button onClick={() => setEditingId(null)} className="p-2 text-rose-500 hover:text-rose-400 transition-colors">
135
+ <X size={18} />
136
+ </button>
137
+ </>
138
+ ) : (
139
+ <div className="flex items-center justify-end gap-3 opacity-0 group-hover:opacity-100 transition-opacity">
140
+ <button onClick={() => handleEdit(item)} className="p-2 text-zinc-500 hover:text-white" title="Update Line Item">
141
+ <Edit3 size={16} />
142
+ </button>
143
+ <button className="p-2 text-zinc-500 hover:text-white"><MoreVertical size={16} /></button>
144
+ </div>
145
+ )}
146
+ </div>
147
+ </td>
148
+ </tr>
149
+ ))}
150
+ </tbody>
151
+ </table>
152
+ </div>
153
+ </div>
154
+
155
+ <div className="bg-zinc-900/50 border border-zinc-800 p-10 rounded-[2.5rem] flex flex-col md:flex-row justify-between items-center gap-8">
156
+ <div className="flex items-center gap-6">
157
+ <div className="p-4 bg-zinc-800 rounded-2xl text-zinc-400">
158
+ <FileText size={24} />
159
+ </div>
160
+ <div>
161
+ <h4 className="text-white font-black text-sm uppercase tracking-widest mb-1 italic">Total Aggregate</h4>
162
+ <p className="text-zinc-500 text-[10px] font-bold uppercase tracking-widest">Calculated from {items.length} granular entries</p>
163
+ </div>
164
+ </div>
165
+ <div className="text-right">
166
+ <p className="text-3xl font-black text-white mono tracking-tighter">
167
+ ${items.reduce((sum, i) => sum + i.amount, 0).toLocaleString(undefined, { minimumFractionDigits: 2 })}
168
+ </p>
169
+ <p className="text-[10px] font-black text-blue-500 uppercase tracking-widest mt-1">Verified Audit Payload</p>
170
+ </div>
171
+ </div>
172
+ </div>
173
+ );
174
+ };
175
+
176
+ export default LineItems;
views/Login.tsx ADDED
@@ -0,0 +1,235 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect, useRef } from 'react';
3
+ import {
4
+ ShieldCheck,
5
+ Cpu,
6
+ Key,
7
+ Terminal,
8
+ Lock,
9
+ Loader2,
10
+ CheckCircle2,
11
+ Zap,
12
+ ShieldAlert,
13
+ ArrowRight,
14
+ Fingerprint,
15
+ FileCheck,
16
+ UserPlus
17
+ } from 'lucide-react';
18
+ import { apiClient } from '../services/api.ts';
19
+
20
+ const Login: React.FC = () => {
21
+ const [loading, setLoading] = useState(false);
22
+ const [isRegistering, setIsRegistering] = useState(false);
23
+ const [stage, setStage] = useState<'IDLE' | 'AUTHORIZING' | 'CONSENT' | 'EXCHANGING' | 'VERIFIED'>('IDLE');
24
+ const [logs, setLogs] = useState<string[]>(["LQI Auth Engine v1.3.0 (Persistent DB) initializing..."]);
25
+ const [error, setError] = useState<string | null>(null);
26
+ const [successMsg, setSuccessMsg] = useState<string | null>(null);
27
+ const logEndRef = useRef<HTMLDivElement>(null);
28
+
29
+ const [params, setParams] = useState({
30
+ username: '',
31
+ password: '',
32
+ });
33
+
34
+ useEffect(() => {
35
+ logEndRef.current?.scrollIntoView({ behavior: 'smooth' });
36
+ }, [logs]);
37
+
38
+ const addLog = (msg: string) => setLogs(prev => [...prev, `${new Date().toLocaleTimeString()} - ${msg}`]);
39
+
40
+ const handleAuth = async (e: React.FormEvent) => {
41
+ e.preventDefault();
42
+ if (loading) return;
43
+
44
+ setLoading(true);
45
+ setError(null);
46
+ setSuccessMsg(null);
47
+ addLog(`INIT: Establishing ${isRegistering ? 'Registration' : 'Login'} protocol handshake...`);
48
+
49
+ try {
50
+ if (isRegistering) {
51
+ const { success, error: regError } = await apiClient.auth.register(params.username, params.password);
52
+ if (success) {
53
+ addLog("DB_WRITE: New identity node successfully written to disk.");
54
+ setSuccessMsg("Registration complete. Initialize handshake to sign in.");
55
+ setIsRegistering(false);
56
+ } else {
57
+ throw new Error(regError || "Registration handshake denied.");
58
+ }
59
+ } else {
60
+ const { success, user, error: loginError } = await apiClient.auth.login(params.username, params.password);
61
+ if (success && user) {
62
+ addLog("DB_QUERY: Identity parity confirmed via cryptographic hash.");
63
+ setStage('VERIFIED');
64
+ setTimeout(() => {
65
+ window.dispatchEvent(new Event('auth-update'));
66
+ }, 1000);
67
+ } else {
68
+ throw new Error(loginError || "Handshake rejected by identity node.");
69
+ }
70
+ }
71
+ } catch (err: any) {
72
+ addLog(`CRITICAL: ${err.message}`);
73
+ setError(err.message);
74
+ } finally {
75
+ setLoading(false);
76
+ }
77
+ };
78
+
79
+ return (
80
+ <div className="min-h-screen bg-[#020202] flex items-center justify-center p-6 relative overflow-hidden font-sans">
81
+ <div className="absolute inset-0 z-0 opacity-10">
82
+ <div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(circle_at_50%_50%,_#1e1b4b_0%,_transparent_50%)]"></div>
83
+ <div className="matrix-line"></div>
84
+ </div>
85
+
86
+ <div className="w-full max-w-5xl grid grid-cols-1 lg:grid-cols-2 bg-zinc-950 border border-zinc-900 rounded-[48px] shadow-2xl relative z-10 overflow-hidden backdrop-blur-3xl">
87
+ <div className="p-12 bg-zinc-900/50 flex flex-col justify-between border-r border-zinc-900">
88
+ <div>
89
+ <div className="flex items-center space-x-3 mb-8">
90
+ <div className="w-12 h-12 bg-white rounded-2xl flex items-center justify-center shadow-xl">
91
+ <Cpu size={28} className="text-black" />
92
+ </div>
93
+ <div>
94
+ <h1 className="text-2xl font-black italic tracking-tighter text-white uppercase leading-none">
95
+ Lumina <span className="text-blue-500 not-italic">Quantum</span>
96
+ </h1>
97
+ <p className="text-[10px] uppercase tracking-[0.4em] font-bold text-zinc-500">Identity v1.3.0</p>
98
+ </div>
99
+ </div>
100
+
101
+ <div className="space-y-6">
102
+ <h2 className="text-4xl font-black text-white leading-tight tracking-tighter uppercase italic">
103
+ {isRegistering ? 'Register' : 'Handshake'} <br />
104
+ <span className="text-blue-500 not-italic">Protocol</span>
105
+ </h2>
106
+ <p className="text-zinc-500 text-sm leading-relaxed max-w-xs font-medium italic">
107
+ Establish a secure session fabric using persistent node storage and RSA-OAEP-4096 hashing.
108
+ </p>
109
+ </div>
110
+ </div>
111
+
112
+ <div className="mt-10">
113
+ <div className="flex items-center gap-2 mb-4">
114
+ <Terminal size={14} className="text-blue-500" />
115
+ <span className="text-[9px] font-black uppercase text-zinc-600 tracking-widest">Quantum Trace Stream</span>
116
+ </div>
117
+ <div className="h-48 bg-black/80 rounded-3xl p-6 overflow-y-auto font-mono text-[10px] border border-zinc-900 custom-scrollbar">
118
+ {logs.map((log, i) => (
119
+ <div key={i} className={`mb-1 ${
120
+ log.includes('SUCCESS') || log.includes('DB_WRITE') ? 'text-emerald-400 font-bold' :
121
+ log.includes('INIT') || log.includes('DB_QUERY') ? 'text-blue-400' :
122
+ log.includes('CRITICAL') ? 'text-rose-500' : 'text-zinc-600'
123
+ }`}>
124
+ {log}
125
+ </div>
126
+ ))}
127
+ <div ref={logEndRef} />
128
+ </div>
129
+ </div>
130
+ </div>
131
+
132
+ <div className="p-12 flex flex-col justify-center bg-black/20">
133
+ {stage === 'VERIFIED' ? (
134
+ <div className="text-center animate-in zoom-in-95 duration-500">
135
+ <div className="w-24 h-24 bg-emerald-500/10 border border-emerald-500/20 rounded-full flex items-center justify-center mx-auto mb-6 shadow-[0_0_50px_rgba(16,185,129,0.2)]">
136
+ <CheckCircle2 size={48} className="text-emerald-500" />
137
+ </div>
138
+ <h3 className="text-2xl font-black text-white uppercase italic tracking-tighter mb-2">Authenticated</h3>
139
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.4em] animate-pulse">Initializing Subspace Ledger...</p>
140
+ </div>
141
+ ) : (
142
+ <div className="animate-in fade-in duration-500">
143
+ <div className="mb-10 flex items-center justify-between">
144
+ <h3 className="text-2xl font-black text-white uppercase italic tracking-tighter">
145
+ {isRegistering ? 'Create Identity' : 'Identity Core'}
146
+ </h3>
147
+ <div className="flex items-center gap-2 px-3 py-1 bg-blue-600/10 border border-blue-500/20 rounded-full">
148
+ <Fingerprint size={12} className="text-blue-500" />
149
+ <span className="text-[9px] font-black text-blue-500 uppercase">Secure Link</span>
150
+ </div>
151
+ </div>
152
+
153
+ <form onSubmit={handleAuth} className="space-y-6">
154
+ <div className="space-y-4">
155
+ <div className="space-y-2">
156
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Universal UID</label>
157
+ <div className="relative">
158
+ <Key className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" size={16} />
159
+ <input
160
+ value={params.username}
161
+ onChange={(e) => setParams({...params, username: e.target.value})}
162
+ className="w-full bg-black border border-zinc-800 rounded-2xl py-4 pl-12 pr-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition-all shadow-inner"
163
+ placeholder="Node Identifier"
164
+ required
165
+ />
166
+ </div>
167
+ </div>
168
+
169
+ <div className="space-y-2">
170
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Private Secret</label>
171
+ <div className="relative">
172
+ <Lock className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" size={16} />
173
+ <input
174
+ type="password"
175
+ value={params.password}
176
+ onChange={(e) => setParams({...params, password: e.target.value})}
177
+ className="w-full bg-black border border-zinc-800 rounded-2xl py-4 pl-12 pr-4 text-white font-mono text-sm outline-none focus:border-blue-500 transition-all shadow-inner"
178
+ placeholder="••••••••"
179
+ required
180
+ />
181
+ </div>
182
+ </div>
183
+ </div>
184
+
185
+ {error && (
186
+ <div className="flex items-center gap-3 text-rose-500 text-xs p-4 bg-rose-500/10 rounded-2xl border border-rose-500/20 animate-pulse">
187
+ <ShieldAlert size={16} /> {error}
188
+ </div>
189
+ )}
190
+
191
+ {successMsg && (
192
+ <div className="flex items-center gap-3 text-emerald-500 text-xs p-4 bg-emerald-500/10 rounded-2xl border border-emerald-500/20">
193
+ <CheckCircle2 size={16} /> {successMsg}
194
+ </div>
195
+ )}
196
+
197
+ <button
198
+ type="submit"
199
+ disabled={loading}
200
+ className="w-full bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] py-5 font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-3 shadow-xl group disabled:opacity-50"
201
+ >
202
+ {loading ? (
203
+ <>
204
+ <Loader2 className="animate-spin" size={18} />
205
+ <span>Processing...</span>
206
+ </>
207
+ ) : (
208
+ <>
209
+ <span>{isRegistering ? 'Register Node' : 'Initialize Handshake'}</span>
210
+ {isRegistering ? <UserPlus size={18} /> : <ArrowRight size={18} className="group-hover:translate-x-1 transition-transform" />}
211
+ </>
212
+ )}
213
+ </button>
214
+
215
+ <button
216
+ type="button"
217
+ onClick={() => {
218
+ setIsRegistering(!isRegistering);
219
+ setError(null);
220
+ setSuccessMsg(null);
221
+ }}
222
+ className="w-full text-[10px] font-black text-zinc-600 hover:text-white uppercase tracking-widest transition-colors"
223
+ >
224
+ {isRegistering ? 'Abort Registration / Return to Hub' : 'Request New Identity Node'}
225
+ </button>
226
+ </form>
227
+ </div>
228
+ )}
229
+ </div>
230
+ </div>
231
+ </div>
232
+ );
233
+ };
234
+
235
+ export default Login;
views/Overview.tsx ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useMemo, useState, useEffect } from 'react';
3
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
4
+ import { useNavigate } from 'react-router-dom';
5
+ import {
6
+ AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer
7
+ } from 'recharts';
8
+ import {
9
+ ArrowUpRight,
10
+ ArrowDownRight,
11
+ Wallet,
12
+ Zap,
13
+ Activity,
14
+ Globe,
15
+ ShieldCheck,
16
+ ChevronRight,
17
+ Terminal,
18
+ ShieldAlert,
19
+ Building2,
20
+ RefreshCw,
21
+ Sparkles,
22
+ TrendingUp,
23
+ Shield,
24
+ Loader2
25
+ } from 'lucide-react';
26
+ /* Fix: removed .ts extensions from imports */
27
+ import { getSystemIntelligenceFeed, getPortfolioSuggestions } from '../services/geminiService';
28
+ import { apiClient } from '../services/api';
29
+ import { cryptoService } from '../services/cryptoService';
30
+ import { InternalAccount, AIInsight } from '../types/index';
31
+ import { CardSkeleton, Skeleton } from '../components/Skeleton';
32
+
33
+ const MOCK_BALANCE_HISTORY = {
34
+ '24H': [
35
+ { time: '00:00', balance: 1245000 },
36
+ { time: '04:00', balance: 1247000 },
37
+ { time: '08:00', balance: 1246000 },
38
+ { time: '12:00', balance: 1248000 },
39
+ { time: '16:00', balance: 1249103 },
40
+ { time: '20:00', balance: 1251200 },
41
+ ],
42
+ '7D': [
43
+ { time: 'Mon', balance: 1100000 },
44
+ { time: 'Tue', balance: 1150000 },
45
+ { time: 'Wed', balance: 1240000 },
46
+ { time: 'Thu', balance: 1220000 },
47
+ { time: 'Fri', balance: 1250000 },
48
+ { time: 'Sat', balance: 1260000 },
49
+ { time: 'Sun', balance: 1251200 },
50
+ ]
51
+ };
52
+
53
+ const StatCard = ({ label, value, trend, icon: Icon, color, subValue, onClick, loading }: any) => {
54
+ if (loading) return <CardSkeleton />;
55
+ return (
56
+ <div
57
+ onClick={onClick}
58
+ className="bg-zinc-950 border border-zinc-900 p-8 rounded-[2rem] hover:border-blue-500/30 transition-all group relative overflow-hidden shadow-2xl cursor-pointer"
59
+ >
60
+ <div className="absolute top-0 right-0 p-4 opacity-[0.03] group-hover:opacity-10 transition-opacity">
61
+ <Icon size={120} />
62
+ </div>
63
+ <div className="flex justify-between items-start mb-6 relative z-10">
64
+ <div className={`p-4 rounded-2xl bg-${color}-500/10 text-${color}-500 group-hover:scale-110 transition-transform duration-500`}>
65
+ <Icon size={24} />
66
+ </div>
67
+ <div className={`flex items-center space-x-1.5 px-3 py-1 rounded-full ${trend >= 0 ? 'bg-emerald-500/10 text-emerald-500' : 'bg-rose-500/10 text-rose-500'} text-[10px] font-black uppercase tracking-widest`}>
68
+ {trend >= 0 ? <ArrowUpRight size={12} /> : <ArrowDownRight size={12} />}
69
+ <span>{Math.abs(trend)}%</span>
70
+ </div>
71
+ </div>
72
+ <div className="relative z-10">
73
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.2em] mb-2">{label}</p>
74
+ <h3 className="text-3xl font-bold text-white mono tracking-tighter mb-1">{value}</h3>
75
+ {subValue && <p className="text-[10px] text-zinc-600 font-bold uppercase tracking-widest">{subValue}</p>}
76
+ </div>
77
+ </div>
78
+ );
79
+ };
80
+
81
+ const RecommendationCard = ({ suggestion, loading }: any) => {
82
+ if (loading) return <Skeleton className="h-40 rounded-[2.5rem]" />;
83
+ const icons = {
84
+ ALPHA: <TrendingUp size={20} className="text-blue-400" />,
85
+ RISK: <Shield size={20} className="text-rose-400" />,
86
+ LIQUIDITY: <Zap size={20} className="text-emerald-400" />
87
+ };
88
+ const borderColors = {
89
+ ALPHA: 'hover:border-blue-500/40',
90
+ RISK: 'hover:border-rose-500/40',
91
+ LIQUIDITY: 'hover:border-emerald-500/40'
92
+ };
93
+
94
+ return (
95
+ <div className={`bg-zinc-950 border border-zinc-900 p-8 rounded-[2.5rem] transition-all group relative overflow-hidden shadow-2xl ${borderColors[suggestion.type as keyof typeof borderColors] || 'hover:border-zinc-700'}`}>
96
+ <div className="flex items-center justify-between mb-6">
97
+ <div className="p-3 bg-zinc-900 rounded-xl">
98
+ {icons[suggestion.type as keyof typeof icons]}
99
+ </div>
100
+ <span className="text-[8px] font-black uppercase tracking-widest text-zinc-600 bg-black px-2 py-1 rounded border border-zinc-800">{suggestion.type}</span>
101
+ </div>
102
+ <h4 className="text-sm font-black text-white uppercase italic tracking-widest mb-2 truncate pr-4">{suggestion.title}</h4>
103
+ <p className="text-[11px] text-zinc-500 leading-relaxed font-medium group-hover:text-zinc-300 transition-colors">"{suggestion.description}"</p>
104
+ <div className="mt-6 flex justify-end">
105
+ <button className="text-[9px] font-black text-blue-500 uppercase tracking-widest hover:text-white transition-colors flex items-center gap-1.5">
106
+ Execute Node <ChevronRight size={10} />
107
+ </button>
108
+ </div>
109
+ </div>
110
+ );
111
+ };
112
+
113
+ const Overview: React.FC = () => {
114
+ const navigate = useNavigate();
115
+ const [activeRange, setActiveRange] = useState<keyof typeof MOCK_BALANCE_HISTORY>('24H');
116
+ const [intelFeed, setIntelFeed] = useState<AIInsight[]>([]);
117
+ const [bankingAccounts, setBankingAccounts] = useState<InternalAccount[]>([]);
118
+ const [suggestions, setSuggestions] = useState<any[]>([]);
119
+ const [isLoading, setIsLoading] = useState(true);
120
+ const [loadingSuggestions, setLoadingSuggestions] = useState(true);
121
+ const [error, setError] = useState<string | null>(null);
122
+
123
+ const loadData = async () => {
124
+ setIsLoading(true);
125
+ setError(null);
126
+ try {
127
+ const [accounts, intel, globalMarket] = await Promise.all([
128
+ apiClient.getRegistryNodes(),
129
+ getSystemIntelligenceFeed(),
130
+ cryptoService.getGlobal()
131
+ ]);
132
+ setBankingAccounts(accounts);
133
+ setIntelFeed(intel);
134
+
135
+ // Trigger proactive suggestions based on gathered context
136
+ refreshSuggestions(accounts, globalMarket);
137
+ } catch (e: any) {
138
+ console.error(e);
139
+ setError("Synchronous parity check failed. Using fallback node.");
140
+ setBankingAccounts([{
141
+ id: 'fallback_01',
142
+ productName: 'Emergency Parity Node',
143
+ displayAccountNumber: '•••• 0000',
144
+ currency: 'USD',
145
+ status: 'ACTIVE',
146
+ currentBalance: 0,
147
+ availableBalance: 0,
148
+ institutionName: 'Local Ledger',
149
+ connectionId: 'LOC-001'
150
+ }]);
151
+ } finally {
152
+ setIsLoading(false);
153
+ }
154
+ };
155
+
156
+ const refreshSuggestions = async (accounts: InternalAccount[], globalMarket: any) => {
157
+ setLoadingSuggestions(true);
158
+ try {
159
+ const context = {
160
+ totalLiquidity: accounts.reduce((s, a) => s + a.availableBalance, 0),
161
+ activeNodes: accounts.length,
162
+ globalMarketCap: globalMarket?.total_market_cap?.usd
163
+ };
164
+ const strategicRecs = await getPortfolioSuggestions(context);
165
+ setSuggestions(strategicRecs);
166
+ } catch (err) {
167
+ console.error("Neural strategist link failure.");
168
+ } finally {
169
+ setLoadingSuggestions(false);
170
+ }
171
+ };
172
+
173
+ useEffect(() => {
174
+ loadData();
175
+ }, []);
176
+
177
+ const totalLiquidity = useMemo(() => {
178
+ return bankingAccounts.reduce((sum, acc) => sum + acc.availableBalance, 0);
179
+ }, [bankingAccounts]);
180
+
181
+ return (
182
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20">
183
+ {error && (
184
+ <div className="bg-rose-500/10 border border-rose-500/20 p-4 rounded-2xl flex items-center gap-4 text-rose-500 text-xs font-bold uppercase tracking-widest">
185
+ <ShieldAlert size={16} />
186
+ {error}
187
+ </div>
188
+ )}
189
+
190
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
191
+ <StatCard
192
+ loading={isLoading}
193
+ label="Total Liquidity"
194
+ value={`$${totalLiquidity.toLocaleString()}`}
195
+ trend={12.4}
196
+ icon={Wallet}
197
+ color="blue"
198
+ subValue={`${bankingAccounts.length} Registry Nodes Active`}
199
+ onClick={() => navigate('/registry')}
200
+ />
201
+ <StatCard loading={isLoading} label="Network Health" value="99.98%" trend={0.01} icon={ShieldCheck} color="emerald" subValue="Handshake Integrity: High" />
202
+ <StatCard loading={isLoading} label="Fabric Load" value="1.2 P/s" trend={0.5} icon={Zap} color="blue" subValue="Subspace Polling Active" />
203
+ <StatCard loading={isLoading} label="Deficit Offset" value="1,242 Kg" trend={-4.2} icon={Globe} color="rose" subValue="ESG Neutrality Level: AA" />
204
+ </div>
205
+
206
+ {/* Neural Strategy Center Section */}
207
+ <div className="bg-zinc-950/40 border border-zinc-900 rounded-[3rem] p-10 shadow-2xl relative overflow-hidden">
208
+ <div className="absolute top-0 right-0 p-10 opacity-[0.03]">
209
+ <Sparkles size={200} className="text-blue-500" />
210
+ </div>
211
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 gap-6 relative z-10">
212
+ <div>
213
+ <h2 className="text-2xl font-black text-white italic tracking-tighter uppercase mb-2">Neural <span className="text-blue-500 not-italic">Strategy Center</span></h2>
214
+ <p className="text-zinc-500 text-[10px] font-bold uppercase tracking-widest">Actionable alpha derived from treasury mesh telemetry</p>
215
+ </div>
216
+ <button
217
+ onClick={() => refreshSuggestions(bankingAccounts, null)}
218
+ disabled={loadingSuggestions}
219
+ className="flex items-center gap-3 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all"
220
+ >
221
+ {loadingSuggestions ? <Loader2 size={14} className="animate-spin" /> : <RefreshCw size={14} />}
222
+ <span>Recalculate Alpha</span>
223
+ </button>
224
+ </div>
225
+
226
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 relative z-10">
227
+ {loadingSuggestions ? (
228
+ Array.from({ length: 3 }).map((_, i) => <Skeleton key={i} className="h-44 rounded-[2.5rem]" />)
229
+ ) : (
230
+ suggestions.map((s, idx) => (
231
+ <RecommendationCard key={idx} suggestion={s} loading={loadingSuggestions} />
232
+ ))
233
+ )}
234
+ </div>
235
+ </div>
236
+
237
+ <div className="grid grid-cols-12 gap-8">
238
+ <div className="col-span-12 lg:col-span-8 space-y-8 min-w-0">
239
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-10 relative overflow-hidden shadow-2xl">
240
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-center mb-10 relative z-10 gap-6">
241
+ <div>
242
+ <h2 className="text-2xl font-black text-white italic tracking-tighter uppercase mb-2">Aggregate <span className="text-blue-500 not-italic">Cash Curve</span></h2>
243
+ <div className="flex items-center gap-3">
244
+ <span className={`w-2 h-2 rounded-full ${isLoading ? 'bg-zinc-800' : 'bg-emerald-500'} animate-pulse`}></span>
245
+ <p className="text-zinc-500 text-[10px] font-bold uppercase tracking-widest">Live Node Trace</p>
246
+ </div>
247
+ </div>
248
+ <div className="flex bg-black p-1.5 rounded-2xl border border-zinc-900">
249
+ {(Object.keys(MOCK_BALANCE_HISTORY) as Array<keyof typeof MOCK_BALANCE_HISTORY>).map(t => (
250
+ <button
251
+ key={t}
252
+ onClick={() => setActiveRange(t)}
253
+ className={`px-6 py-2 rounded-xl text-[10px] font-black transition-all uppercase tracking-widest ${t === activeRange ? 'bg-zinc-100 text-black shadow-lg' : 'text-zinc-500 hover:text-zinc-300'}`}
254
+ >
255
+ {t}
256
+ </button>
257
+ ))}
258
+ </div>
259
+ </div>
260
+
261
+ <div className="w-full relative z-10 min-h-[400px] h-[400px]">
262
+ {isLoading ? <Skeleton className="w-full h-full rounded-2xl" /> : (
263
+ <ResponsiveContainer width="100%" height="100%">
264
+ <AreaChart data={MOCK_BALANCE_HISTORY[activeRange]}>
265
+ <defs>
266
+ <linearGradient id="colorBalance" x1="0" y1="0" x2="0" y2="1">
267
+ <stop offset="5%" stopColor="#3b82f6" stopOpacity={0.2}/>
268
+ <stop offset="95%" stopColor="#3b82f6" stopOpacity={0}/>
269
+ </linearGradient>
270
+ </defs>
271
+ <CartesianGrid strokeDasharray="3 3" stroke="#18181b" vertical={false} />
272
+ <XAxis dataKey="time" stroke="#3f3f46" fontSize={10} tickLine={false} axisLine={false} tick={{ fontWeight: 800 }} />
273
+ <YAxis stroke="#3f3f46" fontSize={10} tickLine={false} axisLine={false} tickFormatter={(v) => `$${v/1000}k`} tick={{ fontWeight: 800 }} />
274
+ <Tooltip
275
+ contentStyle={{ backgroundColor: '#000', border: '1px solid #27272a', borderRadius: '16px', padding: '12px' }}
276
+ itemStyle={{ color: '#3b82f6', fontWeight: 900, fontSize: '12px' }}
277
+ />
278
+ <Area type="monotone" dataKey="balance" stroke="#3b82f6" strokeWidth={4} fillOpacity={1} fill="url(#colorBalance)" animationDuration={1000} />
279
+ </AreaChart>
280
+ </ResponsiveContainer>
281
+ )}
282
+ </div>
283
+ </div>
284
+
285
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-10 shadow-2xl">
286
+ <div className="flex justify-between items-center mb-8">
287
+ <h3 className="text-xl font-black text-white italic tracking-tighter uppercase">Registry <span className="text-blue-500 not-italic">Nodes</span></h3>
288
+ <button onClick={loadData} className="p-3 bg-zinc-900 rounded-xl text-zinc-500 hover:text-white transition-all">
289
+ <RefreshCw size={16} className={isLoading ? 'animate-spin' : ''} />
290
+ </button>
291
+ </div>
292
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
293
+ {isLoading ? (
294
+ <>
295
+ <Skeleton className="h-32 rounded-[2rem]" />
296
+ <Skeleton className="h-32 rounded-[2rem]" />
297
+ </>
298
+ ) : (
299
+ bankingAccounts.map(acc => (
300
+ <div key={acc.id} className="p-8 bg-black/40 border border-zinc-900 rounded-[2rem] hover:border-blue-500/20 transition-all group">
301
+ <div className="flex justify-between items-start mb-6">
302
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-xl group-hover:scale-110 transition-transform">
303
+ <Building2 size={20} />
304
+ </div>
305
+ <span className="text-[9px] font-black uppercase text-emerald-500 bg-emerald-500/5 px-2.5 py-1 rounded-full border border-emerald-500/10">Active Node</span>
306
+ </div>
307
+ <h4 className="text-white font-black text-sm uppercase italic mb-1">{acc.productName}</h4>
308
+ <p className="text-[10px] text-zinc-600 font-bold uppercase tracking-widest mb-4">{acc.displayAccountNumber}</p>
309
+ <div className="flex justify-between items-baseline">
310
+ <p className="text-[9px] font-black text-zinc-500 uppercase tracking-widest">Liquidity</p>
311
+ <p className="text-xl font-black text-white mono tracking-tighter">${acc.availableBalance.toLocaleString()}</p>
312
+ </div>
313
+ </div>
314
+ ))
315
+ )}
316
+ </div>
317
+ </div>
318
+ </div>
319
+
320
+ <div className="col-span-12 lg:col-span-4 space-y-8 min-w-0">
321
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[2.5rem] p-10 flex flex-col h-full shadow-2xl">
322
+ <div className="flex items-center justify-between mb-8">
323
+ <div>
324
+ <h2 className="text-xl font-black text-white italic tracking-tighter uppercase">Quantum <span className="text-blue-500 not-italic">Intel</span></h2>
325
+ <p className="text-zinc-500 text-[10px] font-bold uppercase tracking-[0.2em]">Neural Feedback Stream</p>
326
+ </div>
327
+ <Terminal size={18} className="text-zinc-700" />
328
+ </div>
329
+
330
+ <div className="space-y-6 flex-1 overflow-y-auto pr-2 custom-scrollbar">
331
+ {isLoading ? (
332
+ Array.from({ length: 4 }).map((_, i) => <Skeleton key={i} className="h-20 rounded-2xl" />)
333
+ ) : (
334
+ intelFeed.map((intel, idx) => (
335
+ <div key={idx} className="p-5 bg-black/40 border border-zinc-900 rounded-2xl hover:border-blue-500/30 transition-all group/item">
336
+ <div className="flex items-start gap-4">
337
+ {intel.severity === 'CRITICAL' ? <ShieldAlert size={16} className="text-rose-500 mt-1" /> : <Activity size={16} className="text-blue-500 mt-1" />}
338
+ <div>
339
+ <p className="text-[10px] font-black text-white uppercase tracking-widest mb-1">{intel.title}</p>
340
+ <p className="text-[11px] text-zinc-500 leading-relaxed group-hover/item:text-zinc-300 transition-colors">{intel.description}</p>
341
+ </div>
342
+ </div>
343
+ </div>
344
+ ))
345
+ )}
346
+ </div>
347
+
348
+ <button onClick={() => navigate('/advisor')} className="mt-8 w-full py-5 bg-zinc-900 hover:bg-zinc-100 hover:text-black text-white rounded-[1.5rem] text-[10px] font-black uppercase tracking-[0.3em] transition-all border border-zinc-800 flex items-center justify-center gap-4">
349
+ <ChevronRight size={18} />
350
+ <span>Deep Advisory</span>
351
+ </button>
352
+ </div>
353
+ </div>
354
+ </div>
355
+ </div>
356
+ );
357
+ };
358
+
359
+ export default Overview;
views/Payments.tsx ADDED
@@ -0,0 +1,223 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useEffect } from 'react';
2
+ import {
3
+ History,
4
+ Search,
5
+ Building2,
6
+ ShieldCheck,
7
+ Loader2,
8
+ ArrowDownLeft,
9
+ ArrowUpRight,
10
+ RefreshCw,
11
+ Calendar
12
+ } from 'lucide-react';
13
+ import { apiClient } from '../services/api';
14
+ import { CitiTransaction, InternalAccount } from '../types/index';
15
+
16
+ const Payments: React.FC = () => {
17
+ const [activeTab, setActiveTab] = useState<'registry' | 'history'>('registry');
18
+ const [loading, setLoading] = useState(true);
19
+ const [accounts, setAccounts] = useState<InternalAccount[]>([]);
20
+ const [selectedAccountId, setSelectedAccountId] = useState<string | null>(null);
21
+ const [transactions, setTransactions] = useState<CitiTransaction[]>([]);
22
+ const [loadingTx, setLoadingTx] = useState(false);
23
+
24
+ const fetchRegistry = async () => {
25
+ setLoading(true);
26
+ try {
27
+ const data = await apiClient.getRegistryDetails();
28
+ const mapped: InternalAccount[] = data.accountGroupDetails.flatMap(group => {
29
+ const list: InternalAccount[] = [];
30
+ if (group.checkingAccountsDetails) {
31
+ group.checkingAccountsDetails.forEach(acc => list.push({
32
+ id: acc.accountId,
33
+ productName: acc.productName,
34
+ displayAccountNumber: acc.displayAccountNumber,
35
+ currency: acc.currencyCode,
36
+ status: acc.accountStatus,
37
+ currentBalance: acc.currentBalance,
38
+ availableBalance: acc.availableBalance,
39
+ institutionName: 'Citi US',
40
+ connectionId: 'CITI-G-001'
41
+ }));
42
+ }
43
+ return list;
44
+ });
45
+ setAccounts(mapped);
46
+ } catch (e) {
47
+ console.error("Registry Node Sync Failure");
48
+ } finally {
49
+ setLoading(false);
50
+ }
51
+ };
52
+
53
+ const fetchTransactions = async (accountId: string) => {
54
+ setLoadingTx(true);
55
+ try {
56
+ const apiTx = await apiClient.getTransactions(accountId);
57
+ setTransactions(apiTx);
58
+ setSelectedAccountId(accountId);
59
+ setActiveTab('history');
60
+ } catch (e) {
61
+ console.error("Transaction Handshake Failed");
62
+ } finally {
63
+ setLoadingTx(false);
64
+ }
65
+ };
66
+
67
+ useEffect(() => {
68
+ fetchRegistry();
69
+ }, []);
70
+
71
+ return (
72
+ <div className="space-y-10 animate-in fade-in duration-700 pb-20">
73
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
74
+ <div>
75
+ <h2 className="text-4xl font-black text-white italic tracking-tighter uppercase mb-2">Registry <span className="text-blue-500 not-italic">Settlement</span></h2>
76
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em] flex items-center gap-2">
77
+ <ShieldCheck size={12} className="text-emerald-500" />
78
+ Direct Node Handshake Active
79
+ </p>
80
+ </div>
81
+ <div className="flex gap-4">
82
+ <div className="flex bg-black p-1.5 rounded-2xl border border-zinc-900">
83
+ <button
84
+ onClick={() => setActiveTab('registry')}
85
+ className={`px-8 py-2.5 rounded-xl text-[10px] font-black transition-all uppercase tracking-widest ${activeTab === 'registry' ? 'bg-zinc-100 text-black' : 'text-zinc-500 hover:text-zinc-300'}`}
86
+ >
87
+ Nodes
88
+ </button>
89
+ <button
90
+ onClick={() => setActiveTab('history')}
91
+ className={`px-8 py-2.5 rounded-xl text-[10px] font-black transition-all uppercase tracking-widest ${activeTab === 'history' ? 'bg-zinc-100 text-black' : 'text-zinc-500 hover:text-zinc-300'}`}
92
+ >
93
+ Ledger
94
+ </button>
95
+ </div>
96
+ </div>
97
+ </div>
98
+
99
+ <div className="grid grid-cols-12 gap-8">
100
+ <div className="col-span-12 lg:col-span-4 space-y-6">
101
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-8 shadow-2xl flex flex-col h-full relative overflow-hidden">
102
+ <div className="relative z-10 mb-8">
103
+ <h3 className="text-white font-black text-xs uppercase tracking-widest italic mb-2">Available Nodes</h3>
104
+ <p className="text-[9px] text-zinc-600 font-bold uppercase tracking-widest">Select target registry for granular audit</p>
105
+ </div>
106
+
107
+ <div className="space-y-4 flex-1 overflow-y-auto custom-scrollbar pr-2">
108
+ {loading ? (
109
+ Array.from({ length: 3 }).map((_, i) => <div key={i} className="h-24 bg-zinc-900 rounded-2xl animate-pulse"></div>)
110
+ ) : (
111
+ accounts.map(acc => (
112
+ <button
113
+ key={acc.id}
114
+ onClick={() => fetchTransactions(acc.id)}
115
+ className={`w-full text-left p-6 rounded-[2rem] border transition-all group ${selectedAccountId === acc.id ? 'bg-blue-600/10 border-blue-500 shadow-xl' : 'bg-black border-zinc-900 hover:border-zinc-700'}`}
116
+ >
117
+ <div className="flex justify-between items-start mb-4">
118
+ <div className={`p-3 rounded-xl ${selectedAccountId === acc.id ? 'bg-blue-500 text-white' : 'bg-zinc-900 text-zinc-600 group-hover:text-blue-400'}`}>
119
+ <Building2 size={20} />
120
+ </div>
121
+ <span className={`text-[8px] font-black uppercase px-2 py-0.5 rounded border ${acc.status === 'ACTIVE' ? 'text-emerald-500 border-emerald-500/20' : 'text-zinc-500 border-zinc-800'}`}>{acc.status}</span>
122
+ </div>
123
+ <h4 className="text-white font-black text-xs uppercase italic truncate">{acc.productName}</h4>
124
+ <div className="mt-4 flex justify-between items-baseline">
125
+ <p className="text-[10px] text-zinc-600 font-bold mono tracking-tighter">{acc.displayAccountNumber}</p>
126
+ <p className="text-sm font-black text-white mono">${acc.currentBalance.toLocaleString()}</p>
127
+ </div>
128
+ </button>
129
+ ))
130
+ )}
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <div className="col-span-12 lg:col-span-8">
136
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] overflow-hidden shadow-2xl min-h-[600px] flex flex-col">
137
+ <div className="p-8 border-b border-zinc-900 bg-black/20 flex justify-between items-center">
138
+ <div className="flex items-center gap-4">
139
+ <div className="w-12 h-12 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center">
140
+ <History size={24} />
141
+ </div>
142
+ <div>
143
+ <h3 className="text-white font-black uppercase tracking-[0.2em] italic text-sm">Ledger Audit Trace</h3>
144
+ <p className="text-[9px] text-zinc-600 font-bold uppercase tracking-widest mt-1">
145
+ {selectedAccountId ? `SYNCED_NODE: ${selectedAccountId}` : 'AWAITING_NODE_HANDSHAKE'}
146
+ </p>
147
+ </div>
148
+ </div>
149
+ <div className="relative">
150
+ <Search size={14} className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" />
151
+ <input
152
+ placeholder="Filter handshakes..."
153
+ className="bg-black border border-zinc-800 rounded-xl py-2 pl-10 pr-4 text-[10px] font-black uppercase tracking-widest text-white outline-none focus:border-blue-500/50 w-48"
154
+ />
155
+ </div>
156
+ </div>
157
+
158
+ <div className="flex-1 p-0 overflow-y-auto custom-scrollbar">
159
+ {loadingTx ? (
160
+ <div className="h-full flex flex-col items-center justify-center py-20 gap-4">
161
+ <Loader2 className="animate-spin text-blue-500" size={48} />
162
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-[0.4em] animate-pulse">Requesting Registry Payload...</p>
163
+ </div>
164
+ ) : !selectedAccountId ? (
165
+ <div className="h-full flex flex-col items-center justify-center py-20 text-center space-y-4 opacity-30 grayscale">
166
+ <ShieldCheck size={80} className="text-zinc-800" />
167
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-[0.4em]">Node Connection Idle</p>
168
+ </div>
169
+ ) : (
170
+ <table className="w-full text-left">
171
+ <thead>
172
+ <tr className="text-[9px] font-black uppercase tracking-[0.3em] text-zinc-600 border-b border-zinc-900 bg-black/40">
173
+ <th className="p-8">Handshake Detail</th>
174
+ <th className="p-8">Protocol/Type</th>
175
+ <th className="p-8">Status</th>
176
+ <th className="p-8 text-right">Value (USD)</th>
177
+ </tr>
178
+ </thead>
179
+ <tbody className="divide-y divide-zinc-900/50">
180
+ {transactions.map(tx => (
181
+ <tr key={tx.transactionId} className="group hover:bg-white/[0.01] transition-colors">
182
+ <td className="p-8">
183
+ <div className="flex items-center gap-6">
184
+ <div className={`w-10 h-10 rounded-xl flex items-center justify-center border transition-all ${tx.transactionAmount < 0 ? 'bg-rose-500/10 border-rose-500/20 text-rose-500' : 'bg-emerald-500/10 border-emerald-500/20 text-emerald-500'}`}>
185
+ {tx.transactionAmount < 0 ? <ArrowUpRight size={18} /> : <ArrowDownLeft size={18} />}
186
+ </div>
187
+ <div>
188
+ <p className="text-xs font-black text-white uppercase italic leading-none">{tx.transactionDescription}</p>
189
+ <div className="flex items-center gap-3 mt-2">
190
+ <Calendar size={10} className="text-zinc-600" />
191
+ <p className="text-[9px] text-zinc-600 font-bold uppercase tracking-widest">{tx.transactionDate} • {tx.transactionId}</p>
192
+ </div>
193
+ </div>
194
+ </div>
195
+ </td>
196
+ <td className="p-8">
197
+ <span className="px-3 py-1 bg-zinc-900 border border-zinc-800 text-[9px] font-black text-zinc-500 uppercase tracking-widest rounded-lg">{tx.transactionType}</span>
198
+ </td>
199
+ <td className="p-8">
200
+ <div className="flex items-center gap-2">
201
+ <div className={`w-1.5 h-1.5 rounded-full ${tx.transactionStatus === 'POSTED' || tx.transactionStatus === 'BILLED' ? 'bg-emerald-500 shadow-[0_0_8px_#10b981]' : 'bg-amber-500'}`}></div>
202
+ <span className={`text-[10px] font-black uppercase tracking-widest ${tx.transactionStatus === 'POSTED' || tx.transactionStatus === 'BILLED' ? 'text-emerald-500' : 'text-amber-500'}`}>{tx.transactionStatus}</span>
203
+ </div>
204
+ </td>
205
+ <td className="p-8 text-right">
206
+ <p className={`text-sm font-black mono tracking-tighter ${tx.transactionAmount < 0 ? 'text-white' : 'text-emerald-500'}`}>
207
+ {tx.transactionAmount < 0 ? '' : '+'}${tx.transactionAmount.toLocaleString(undefined, { minimumFractionDigits: 2 })}
208
+ </p>
209
+ </td>
210
+ </tr>
211
+ ))}
212
+ </tbody>
213
+ </table>
214
+ )}
215
+ </div>
216
+ </div>
217
+ </div>
218
+ </div>
219
+ </div>
220
+ );
221
+ };
222
+
223
+ export default Payments;
views/PrivacyPolicy.tsx ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import { ShieldAlert, Lock, EyeOff, Scale, Gavel, Globe, FileText, ChevronRight, ArrowLeft } from 'lucide-react';
4
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
5
+ import { useNavigate } from 'react-router-dom';
6
+
7
+ const PrivacyPolicy: React.FC = () => {
8
+ const navigate = useNavigate();
9
+
10
+ return (
11
+ <div className="max-w-5xl mx-auto py-32 px-10 animate-in fade-in duration-500">
12
+ <div className="flex flex-col items-center text-center mb-24">
13
+ <div className="w-32 h-32 bg-yellow-500 border-8 border-black rounded-[3rem] flex items-center justify-center mb-10 shadow-[20px_20px_0px_black] rotate-3">
14
+ <Gavel size={64} className="text-black" />
15
+ </div>
16
+ <h1 className="text-8xl font-black italic text-black uppercase tracking-tighter leading-none mb-6">
17
+ Honest <span className="text-yellow-500 underline decoration-black decoration-8">Manifesto</span>
18
+ </h1>
19
+ <p className="text-[14px] font-black text-zinc-500 uppercase tracking-[0.8em]">Transparency First • 527 Org Protocol</p>
20
+ </div>
21
+
22
+ <div className="bg-white border-8 border-black rounded-[4rem] p-16 lg:p-24 space-y-32 shadow-[40px_40px_0px_rgba(255,215,0,0.2)] relative overflow-hidden">
23
+ <section className="space-y-10">
24
+ <div className="flex items-center gap-6">
25
+ <div className="w-14 h-14 bg-black rounded-2xl flex items-center justify-center text-yellow-500 shadow-xl">
26
+ <Lock size={28} />
27
+ </div>
28
+ <h2 className="text-4xl font-black italic text-black uppercase tracking-tight">Our Honest Position</h2>
29
+ </div>
30
+ <p className="text-black text-2xl leading-relaxed font-bold italic">
31
+ Citibank Demo Business Inc is a 527 political organization. We do not offer banking services. We provide mathematical proof of AI expertise to educate the public and policy makers. Our "privacy policy" isn't a legal shield; it's a statement of fact: We do not want your data. We built this mesh to process information in real-time and then discard it.
32
+ </p>
33
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
34
+ <div className="p-10 bg-zinc-50 border-4 border-black rounded-[3rem] space-y-4">
35
+ <h4 className="text-[12px] font-black text-zinc-400 uppercase tracking-widest">Data Disposal</h4>
36
+ <p className="text-black font-black text-lg uppercase tracking-tight">Every packet is shredded immediately after ledger verification. Zero persistence is our technical reality.</p>
37
+ </div>
38
+ <div className="p-10 bg-yellow-500 border-4 border-black rounded-[3rem] space-y-4">
39
+ <h4 className="text-[12px] font-black text-black uppercase tracking-widest">No Tracking</h4>
40
+ <p className="text-black font-black text-lg uppercase tracking-tight">We do not use cookies, pixels, or behavioral trackers. We are here to prove intelligence, not sell attention.</p>
41
+ </div>
42
+ </div>
43
+ </section>
44
+
45
+ <section className="space-y-10">
46
+ <div className="flex items-center gap-6">
47
+ <div className="w-14 h-14 bg-black rounded-2xl flex items-center justify-center text-yellow-500 shadow-xl">
48
+ <Scale size={28} />
49
+ </div>
50
+ <h2 className="text-4xl font-black italic text-black uppercase tracking-tight">Expert Terms</h2>
51
+ </div>
52
+ <p className="text-black text-xl leading-relaxed font-bold italic">
53
+ By interacting with this registry, you acknowledge that you are viewing educational artifacts. These simulations are designed to demonstrate the convergence of AI and treasury protocols. This platform is provided "as is" to prove that we are the undisputed experts in the liminal world of neural finance.
54
+ </p>
55
+ <ul className="space-y-4">
56
+ {["Identity remains sovereign", "Logic is distributed", "Consensus is hard-coded", "Expertise is verified"].map((term, i) => (
57
+ <li key={i} className="flex items-center gap-4 text-black font-black text-sm uppercase tracking-[0.4em] bg-zinc-100 p-6 rounded-2xl border-4 border-black">
58
+ <div className="w-4 h-4 bg-yellow-500 rounded-full border-2 border-black" />
59
+ {term}
60
+ </li>
61
+ ))}
62
+ </ul>
63
+ </section>
64
+
65
+ <footer className="pt-24 border-t-8 border-black text-center space-y-12">
66
+ <p className="text-[12px] font-black text-zinc-400 uppercase tracking-[1em]">Citibank Demo Business Inc • 527 Org • Verified 2025</p>
67
+ <button
68
+ onClick={() => navigate(-1)}
69
+ className="px-20 py-8 bg-black text-white rounded-full font-black text-lg uppercase tracking-[0.5em] hover:bg-yellow-500 hover:text-black transition-all shadow-[0_20px_50px_rgba(0,0,0,0.3)] flex items-center gap-6 mx-auto"
70
+ >
71
+ <ArrowLeft size={24} /> Acknowledge Truth
72
+ </button>
73
+ </footer>
74
+ </div>
75
+ </div>
76
+ );
77
+ };
78
+
79
+ export default PrivacyPolicy;
views/Settings.tsx ADDED
@@ -0,0 +1,445 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState, useEffect } from 'react';
3
+ import {
4
+ User,
5
+ Mail,
6
+ Building,
7
+ MapPin,
8
+ Shield,
9
+ Save,
10
+ Lock,
11
+ Plus,
12
+ Bell,
13
+ RefreshCw,
14
+ Loader2,
15
+ Terminal,
16
+ Zap,
17
+ CheckCircle2,
18
+ AlertCircle,
19
+ Globe,
20
+ ExternalLink,
21
+ ShieldAlert,
22
+ Fingerprint
23
+ } from 'lucide-react';
24
+ import { CustomerProfileResponse } from '../types/index.ts';
25
+ import { callGemini } from '../services/geminiService.ts';
26
+
27
+ // Fix: Updated MOCK_PROFILE to conform to the nested CustomerProfileResponse interface
28
+ const MOCK_PROFILE: CustomerProfileResponse = {
29
+ customer: {
30
+ firstName: 'Alex',
31
+ lastName: 'Rivera',
32
+ middleName: '',
33
+ title: 'Mx.',
34
+ companyName: 'Lumina Quantum Systems'
35
+ },
36
+ contacts: {
37
+ emails: [
38
+ 'a.rivera@luminaquantum.io'
39
+ ],
40
+ addresses: [
41
+ {
42
+ addressLine1: '401 Quantum Drive',
43
+ city: 'Palo Alto',
44
+ region: 'CA',
45
+ country: 'US',
46
+ postalCode: '94304',
47
+ type: 'BUSINESS'
48
+ }
49
+ ],
50
+ phones: [
51
+ { type: 'CELL', country: '1', number: '9542312002' }
52
+ ]
53
+ }
54
+ };
55
+
56
+ const Settings: React.FC = () => {
57
+ const [activeTab, setActiveTab] = useState<'profile' | 'security' | 'notifications'>('profile');
58
+
59
+ // Security State
60
+ const [isRotating, setIsRotating] = useState(false);
61
+ const [rotationStep, setRotationStep] = useState<string | null>(null);
62
+ const [masterKey, setMasterKey] = useState('cf532cc7c81046e6...');
63
+ const [rotationLog, setRotationLog] = useState<string | null>(null);
64
+
65
+ // Webhook State
66
+ const [webhookUri, setWebhookUri] = useState('');
67
+ const [isTestingWebhook, setIsTestingWebhook] = useState(false);
68
+ const [webhookLogs, setWebhookLogs] = useState<{msg: string, type: 'info' | 'success' | 'error'}[]>([]);
69
+ const [toggles, setToggles] = useState({
70
+ tx: true,
71
+ iam: true,
72
+ dcr: false,
73
+ esg: true
74
+ });
75
+
76
+ const handleSave = () => {
77
+ const btn = document.getElementById('save-btn');
78
+ if (btn) {
79
+ const originalText = btn.innerHTML;
80
+ btn.innerText = 'Syncing...';
81
+ btn.classList.add('bg-emerald-600');
82
+ setTimeout(() => {
83
+ btn.innerText = 'Saved to Node';
84
+ setTimeout(() => {
85
+ btn.innerHTML = originalText;
86
+ btn.classList.remove('bg-emerald-600');
87
+ }, 1500);
88
+ }, 1000);
89
+ }
90
+ };
91
+
92
+ const handleRotateCredentials = async () => {
93
+ setIsRotating(true);
94
+ setRotationLog(null);
95
+
96
+ const steps = [
97
+ "Invalidating legacy RSA-OAEP sessions...",
98
+ "Generating high-entropy seed (quantum-resistant)...",
99
+ "Performing peer-to-peer node handshake...",
100
+ "Finalizing re-keying protocol..."
101
+ ];
102
+
103
+ try {
104
+ for (const step of steps) {
105
+ setRotationStep(step);
106
+ await new Promise(r => setTimeout(r, 600));
107
+ }
108
+
109
+ const response = await callGemini(
110
+ 'gemini-3-flash-preview',
111
+ "Generate a technical 1-sentence confirmation for a successful RSA-OAEP-256 key rotation. Mention a new entropy seed and block verification. Tone: Technical."
112
+ );
113
+
114
+ const newKey = 'lq_' + Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15);
115
+ setMasterKey(newKey);
116
+ // Fix: Access .text property directly
117
+ setRotationLog(response.text || "Success: Network re-keyed with new master secret.");
118
+ } catch (err) {
119
+ setRotationLog("Key rotation complete. System re-keyed via fallback entropy.");
120
+ } finally {
121
+ setIsRotating(false);
122
+ setRotationStep(null);
123
+ }
124
+ };
125
+
126
+ const handleTestWebhook = async () => {
127
+ if (!webhookUri.trim()) {
128
+ setWebhookLogs([{ msg: 'Error: Target URI is required for dispatch.', type: 'error' }]);
129
+ return;
130
+ }
131
+
132
+ setIsTestingWebhook(true);
133
+ setWebhookLogs(prev => [...prev, { msg: `Attempting dispatch to ${webhookUri}...`, type: 'info' }]);
134
+
135
+ try {
136
+ const payloadResponse = await callGemini(
137
+ 'gemini-3-flash-preview',
138
+ "Generate a realistic JSON webhook payload for a 'Large Institutional Transfer Detected' event. Include tx_id, amount, status: 'VERIFIED', and timestamp. Return ONLY JSON.",
139
+ { responseMimeType: "application/json" }
140
+ );
141
+
142
+ // Fix: Use response.text instead of manual part extraction
143
+ const payload = JSON.parse(payloadResponse.text || '{}');
144
+ setWebhookLogs(prev => [...prev, { msg: `Neural Payload built: ${payload.tx_id || 'ID_NUL'}`, type: 'info' }]);
145
+
146
+ // Attempt actual HTTP POST
147
+ // Note: Browsers will often block this due to CORS unless the target allows it.
148
+ // We handle the catch specifically for this "Failed to fetch" scenario.
149
+ const response = await fetch(webhookUri, {
150
+ method: 'POST',
151
+ headers: { 'Content-Type': 'application/json' },
152
+ body: JSON.stringify({
153
+ event: 'LUMINA_TEST_DISPATCH',
154
+ timestamp: new Date().toISOString(),
155
+ data: payload,
156
+ note: "This is an institutional test packet from Lumina Quantum Registry."
157
+ }),
158
+ mode: 'cors'
159
+ });
160
+
161
+ if (response.ok) {
162
+ setWebhookLogs(prev => [...prev, { msg: `HTTP ${response.status}: Target acknowledged receipt.`, type: 'success' }]);
163
+ } else {
164
+ setWebhookLogs(prev => [...prev, { msg: `HTTP ${response.status}: Node connection established, but target returned non-200.`, type: 'error' }]);
165
+ }
166
+ } catch (err) {
167
+ const isCors = err instanceof TypeError && err.message === "Failed to fetch";
168
+ if (isCors) {
169
+ setWebhookLogs(prev => [
170
+ ...prev,
171
+ { msg: `Browser Dispatch Attempted. Packet sent to network.`, type: 'success' },
172
+ { msg: `Note: Target may lack 'Access-Control-Allow-Origin' headers (CORS). Check server logs directly.`, type: 'info' }
173
+ ]);
174
+ } else {
175
+ setWebhookLogs(prev => [...prev, { msg: `Dispatch Error: ${err instanceof Error ? err.message : 'Unknown network failure'}`, type: 'error' }]);
176
+ }
177
+ } finally {
178
+ setIsTestingWebhook(false);
179
+ }
180
+ };
181
+
182
+ return (
183
+ <div className="max-w-6xl mx-auto space-y-8 animate-in fade-in duration-700 pb-20">
184
+ <div className="flex justify-between items-end">
185
+ <div>
186
+ <h2 className="text-3xl font-bold text-white mb-2 italic tracking-tighter uppercase">Registry <span className="text-blue-500 not-italic">Settings</span></h2>
187
+ <p className="text-zinc-500 font-medium">Manage your verified Data Recipient identity and system protocols.</p>
188
+ </div>
189
+ <button
190
+ id="save-btn"
191
+ onClick={handleSave}
192
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-xs uppercase tracking-widest transition-all shadow-lg shadow-blue-900/30"
193
+ >
194
+ <Save size={18} />
195
+ <span>Save Changes</span>
196
+ </button>
197
+ </div>
198
+
199
+ <div className="flex flex-col lg:flex-row gap-8">
200
+ <div className="w-full lg:w-72 space-y-2">
201
+ <NavButton active={activeTab === 'profile'} onClick={() => setActiveTab('profile')} icon={User} label="Identity Profile" />
202
+ <NavButton active={activeTab === 'security'} onClick={() => setActiveTab('security')} icon={Shield} label="Auth & Security" />
203
+ <NavButton active={activeTab === 'notifications'} onClick={() => setActiveTab('notifications')} icon={Bell} label="Event Webhooks" />
204
+ </div>
205
+
206
+ <div className="flex-1 bg-zinc-900 border border-zinc-800 rounded-[32px] p-8 lg:p-10 shadow-2xl">
207
+ {activeTab === 'profile' && (
208
+ <div className="space-y-10 animate-in slide-in-from-right-4 duration-500">
209
+ <section>
210
+ <div className="flex items-center gap-3 mb-6">
211
+ <div className="p-2 bg-blue-600/10 text-blue-500 rounded-lg">
212
+ <User size={18} />
213
+ </div>
214
+ <h3 className="text-lg font-bold text-white italic tracking-tighter uppercase">Legal Identity</h3>
215
+ </div>
216
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
217
+ <Input label="Title" value="Mx." />
218
+ {/* Fix: Accessed nested customer properties in MOCK_PROFILE */}
219
+ <Input label="First Name" value={MOCK_PROFILE.customer.firstName} />
220
+ <Input label="Last Name" value={MOCK_PROFILE.customer.lastName} />
221
+ <div className="md:col-span-3">
222
+ {/* Fix: Accessed nested companyName property */}
223
+ <Input label="Legal Company Name" value={MOCK_PROFILE.customer.companyName || ""} />
224
+ </div>
225
+ </div>
226
+ </section>
227
+
228
+ <div className="h-px bg-zinc-800/50"></div>
229
+
230
+ <section>
231
+ <div className="flex items-center gap-3 mb-6">
232
+ <div className="p-2 bg-blue-600/10 text-blue-500 rounded-lg">
233
+ <Mail size={18} />
234
+ </div>
235
+ <h3 className="text-lg font-bold text-white italic tracking-tighter uppercase">Contact Registry</h3>
236
+ </div>
237
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
238
+ {/* Fix: Accessed nested contacts.emails and contacts.phones */}
239
+ <Input label="Primary Email Address" value={MOCK_PROFILE.contacts.emails[0]} />
240
+ <Input label="Primary Phone Number" value={MOCK_PROFILE.contacts.phones[0].number} />
241
+ </div>
242
+ </section>
243
+
244
+ <div className="h-px bg-zinc-800/50"></div>
245
+
246
+ <section>
247
+ <div className="flex items-center gap-3 mb-6">
248
+ <div className="p-2 bg-blue-600/10 text-blue-500 rounded-lg">
249
+ <MapPin size={18} />
250
+ </div>
251
+ <h3 className="text-lg font-bold text-white italic tracking-tighter uppercase">Mailing Address</h3>
252
+ </div>
253
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
254
+ {/* Fix: Use addresses property from nested contacts object */}
255
+ {MOCK_PROFILE.contacts.addresses.map((addr, idx) => (
256
+ <div key={idx} className="p-6 bg-black/40 border border-zinc-800 rounded-2xl group hover:border-blue-500/30 transition-all">
257
+ <div className="flex justify-between items-start mb-4">
258
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest">{addr.type}</p>
259
+ <Building size={14} className="text-zinc-700" />
260
+ </div>
261
+ <p className="text-white font-bold text-base">{addr.addressLine1}</p>
262
+ {/* Fix: Used addr.country as per CustomerAddress type */}
263
+ <p className="text-zinc-500 text-xs font-medium">{addr.city}, {addr.country} {addr.postalCode}</p>
264
+ </div>
265
+ ))}
266
+ <button className="p-6 border-2 border-dashed border-zinc-800 rounded-2xl text-zinc-700 hover:text-zinc-500 hover:border-zinc-700 transition-all flex flex-col items-center justify-center gap-2 font-black text-[10px] uppercase tracking-widest">
267
+ <Plus size={20} />
268
+ Register New Address
269
+ </button>
270
+ </div>
271
+ </section>
272
+ </div>
273
+ )}
274
+
275
+ {activeTab === 'security' && (
276
+ <div className="flex flex-col items-center justify-center min-h-[500px] text-center space-y-8 animate-in zoom-in-95 duration-500 px-4">
277
+ <div className="relative">
278
+ <div className={`p-10 bg-blue-600/10 text-blue-500 rounded-[32px] shadow-2xl shadow-blue-900/10 border border-blue-500/20 relative z-10 transition-transform duration-700 ${isRotating ? 'scale-110' : ''}`}>
279
+ {isRotating ? <RefreshCw size={64} className="animate-spin" /> : <Lock size={64} />}
280
+ </div>
281
+ {isRotating && (
282
+ <div className="absolute inset-0 bg-blue-500/20 blur-2xl rounded-full animate-pulse"></div>
283
+ )}
284
+ </div>
285
+
286
+ <div className="max-w-md">
287
+ <h3 className="text-2xl font-black text-white tracking-tight uppercase italic mb-3">Quantum Authentication</h3>
288
+ <p className="text-zinc-500 font-medium leading-relaxed">Secure machine-to-machine session management via rotating RSA-OAEP-256 encrypted master secrets.</p>
289
+
290
+ <div className="mt-8 p-6 bg-black border border-zinc-800 rounded-3xl relative overflow-hidden group">
291
+ <div className="absolute top-0 right-0 p-4 opacity-5 pointer-events-none">
292
+ <Fingerprint size={60} />
293
+ </div>
294
+ <p className="text-[9px] font-black text-zinc-600 uppercase tracking-widest mb-2 text-left">Active Session Secret</p>
295
+ <div className="flex items-center justify-between">
296
+ <span className="text-blue-400 font-mono text-xs break-all text-left">{masterKey}</span>
297
+ <CheckCircle2 size={14} className="text-emerald-500 shrink-0 ml-4" />
298
+ </div>
299
+ </div>
300
+
301
+ {rotationStep && (
302
+ <p className="mt-6 text-[10px] font-black text-blue-500 uppercase tracking-[0.2em] animate-pulse">
303
+ {rotationStep}
304
+ </p>
305
+ )}
306
+
307
+ {rotationLog && !isRotating && (
308
+ <div className="mt-6 p-4 bg-emerald-500/10 border border-emerald-500/20 rounded-2xl text-[11px] font-medium text-emerald-400 italic animate-in slide-in-from-top-2">
309
+ <CheckCircle2 size={12} className="inline mr-2" />
310
+ "{rotationLog}"
311
+ </div>
312
+ )}
313
+ </div>
314
+
315
+ <div className="flex flex-col sm:flex-row gap-4 w-full max-w-sm">
316
+ <button
317
+ onClick={handleRotateCredentials}
318
+ disabled={isRotating}
319
+ className="flex-1 py-4 bg-zinc-800 hover:bg-zinc-700 disabled:opacity-50 text-white rounded-2xl font-black text-xs uppercase tracking-widest border border-zinc-700 flex items-center justify-center gap-3 transition-all"
320
+ >
321
+ {isRotating ? <Loader2 size={16} className="animate-spin" /> : <RefreshCw size={16} />}
322
+ <span>{isRotating ? 'Rotating...' : 'Rotate Credentials'}</span>
323
+ </button>
324
+ <button className="flex-1 py-4 bg-rose-600/10 hover:bg-rose-600/20 text-rose-500 rounded-2xl font-black text-xs uppercase tracking-widest border border-rose-500/20 transition-all">
325
+ Revoke Node
326
+ </button>
327
+ </div>
328
+ </div>
329
+ )}
330
+
331
+ {activeTab === 'notifications' && (
332
+ <div className="space-y-10 animate-in slide-in-from-left-4 duration-500">
333
+ <div className="flex items-center gap-3">
334
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-xl">
335
+ <Zap size={20} />
336
+ </div>
337
+ <h3 className="text-lg font-bold text-white uppercase italic tracking-tighter">Event Webhook Gateway</h3>
338
+ </div>
339
+
340
+ <div className="space-y-4">
341
+ <ToggleItem label="Real-time Transaction Notifications" checked={toggles.tx} onChange={() => setToggles({...toggles, tx: !toggles.tx})} />
342
+ <ToggleItem label="IAM Token Expiry Warnings" checked={toggles.iam} onChange={() => setToggles({...toggles, iam: !toggles.iam})} />
343
+ <ToggleItem label="Dynamic Client Registry Updates" checked={toggles.dcr} onChange={() => setToggles({...toggles, dcr: !toggles.dcr})} />
344
+ <ToggleItem label="ESG Threshold Deviations" checked={toggles.esg} onChange={() => setToggles({...toggles, esg: !toggles.esg})} />
345
+ </div>
346
+
347
+ <div className="p-8 bg-zinc-950 rounded-[2.5rem] border border-zinc-800 space-y-6 shadow-xl">
348
+ <div className="space-y-2">
349
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Webhook Destination URI</label>
350
+ <div className="flex flex-col sm:flex-row gap-4">
351
+ <div className="flex-1 relative">
352
+ <Globe className="absolute left-4 top-1/2 -translate-y-1/2 text-zinc-700" size={16} />
353
+ <input
354
+ value={webhookUri}
355
+ onChange={(e) => setWebhookUri(e.target.value)}
356
+ placeholder="e.g. https://your-endpoint.io/webhook"
357
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-2xl pl-12 pr-5 py-4 text-zinc-200 font-mono text-xs outline-none transition-all placeholder:text-zinc-800"
358
+ />
359
+ </div>
360
+ <button
361
+ onClick={handleTestWebhook}
362
+ disabled={isTestingWebhook || !webhookUri.trim()}
363
+ className="px-8 py-4 bg-blue-600 hover:bg-blue-500 disabled:bg-zinc-800 disabled:text-zinc-700 text-white rounded-2xl text-xs font-black uppercase tracking-widest transition-all flex items-center justify-center gap-3"
364
+ >
365
+ {isTestingWebhook ? <Loader2 size={16} className="animate-spin" /> : <Zap size={16} />}
366
+ Test Dispatch
367
+ </button>
368
+ </div>
369
+ </div>
370
+
371
+ {webhookLogs.length > 0 && (
372
+ <div className="bg-black rounded-3xl border border-zinc-900 overflow-hidden animate-in fade-in slide-in-from-bottom-4">
373
+ <div className="bg-zinc-900/50 px-6 py-3 border-b border-zinc-800 flex items-center justify-between">
374
+ <span className="text-[9px] font-black uppercase text-zinc-500 tracking-widest flex items-center gap-2">
375
+ <Terminal size={12} className="text-blue-500" />
376
+ LQI Webhook Console
377
+ </span>
378
+ <button onClick={() => setWebhookLogs([])} className="text-[9px] font-black text-zinc-600 hover:text-white uppercase transition-colors">Flush Cache</button>
379
+ </div>
380
+ <div className="p-6 h-40 overflow-y-auto font-mono text-[10px] space-y-2 custom-scrollbar">
381
+ {webhookLogs.map((log, i) => (
382
+ <div key={i} className={`flex items-start gap-3 ${
383
+ log.type === 'error' ? 'text-rose-500' :
384
+ log.type === 'success' ? 'text-emerald-500' :
385
+ 'text-zinc-500'
386
+ }`}>
387
+ <span className="opacity-30 shrink-0">[{new Date().toLocaleTimeString([], { hour12: false })}]</span>
388
+ <span className="flex-1">
389
+ {log.type === 'error' && <ShieldAlert size={10} className="inline mr-1" />}
390
+ {log.msg}
391
+ </span>
392
+ </div>
393
+ ))}
394
+ </div>
395
+ </div>
396
+ )}
397
+ </div>
398
+ </div>
399
+ )}
400
+ </div>
401
+ </div>
402
+ </div>
403
+ );
404
+ };
405
+
406
+ const NavButton = ({ active, onClick, icon: Icon, label }: any) => (
407
+ <button
408
+ onClick={onClick}
409
+ className={`w-full flex items-center space-x-3 px-6 py-5 rounded-[24px] transition-all group ${
410
+ active
411
+ ? 'bg-blue-600 text-white shadow-2xl shadow-blue-900/30'
412
+ : 'text-zinc-500 hover:bg-zinc-800/50 hover:text-zinc-300'
413
+ }`}
414
+ >
415
+ <Icon size={20} className={active ? 'text-white' : 'text-zinc-600 group-hover:text-blue-500 transition-colors'} />
416
+ <span className="font-black text-sm uppercase tracking-widest">{label}</span>
417
+ </button>
418
+ );
419
+
420
+ const Input = ({ label, value }: any) => (
421
+ <div className="space-y-2">
422
+ <label className="block text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">{label}</label>
423
+ <input
424
+ readOnly
425
+ defaultValue={value}
426
+ className="w-full bg-black border border-zinc-800 rounded-2xl px-5 py-3.5 text-white font-medium outline-none cursor-default focus:border-blue-500/30 transition-all"
427
+ />
428
+ </div>
429
+ );
430
+
431
+ const ToggleItem = ({ label, checked, onChange }: any) => {
432
+ return (
433
+ <div className="flex justify-between items-center p-5 bg-black/40 border border-zinc-800 rounded-2xl hover:border-zinc-700 transition-all group">
434
+ <span className="text-sm font-black text-zinc-300 uppercase tracking-tight group-hover:text-white transition-colors">{label}</span>
435
+ <button
436
+ onClick={onChange}
437
+ className={`w-14 h-7 rounded-full relative transition-all duration-300 ${checked ? 'bg-blue-600 shadow-lg shadow-blue-900/30' : 'bg-zinc-800'}`}
438
+ >
439
+ <div className={`absolute top-1 w-5 h-5 rounded-full bg-white shadow-md transition-all duration-300 ${checked ? 'left-8' : 'left-1'}`}></div>
440
+ </button>
441
+ </div>
442
+ );
443
+ };
444
+
445
+ export default Settings;
views/Simulation.tsx ADDED
@@ -0,0 +1,149 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import { Play, Sparkles, AlertCircle, CheckCircle2, ChevronRight, Loader2 } from 'lucide-react';
4
+ import { runSimulationForecast } from '../services/geminiService.ts';
5
+ import { SimulationResult } from '../types/index.ts';
6
+
7
+ const ScenarioButton = ({ label, description, onClick }: any) => (
8
+ <button
9
+ onClick={() => onClick(label)}
10
+ className="text-left p-5 rounded-2xl bg-zinc-900 border border-zinc-800 hover:border-blue-500/50 hover:bg-zinc-800/50 transition-all group"
11
+ >
12
+ <h4 className="text-white font-bold mb-1 group-hover:text-blue-400 transition-colors">{label}</h4>
13
+ <p className="text-zinc-500 text-xs leading-relaxed">{description}</p>
14
+ </button>
15
+ );
16
+
17
+ const Simulation: React.FC = () => {
18
+ const [prompt, setPrompt] = useState('');
19
+ const [loading, setLoading] = useState(false);
20
+ const [result, setResult] = useState<SimulationResult | null>(null);
21
+
22
+ const handleRun = async (overridePrompt?: string) => {
23
+ const finalPrompt = overridePrompt || prompt;
24
+ if (!finalPrompt) return;
25
+
26
+ setLoading(true);
27
+ const data = await runSimulationForecast(finalPrompt);
28
+ setResult(data);
29
+ setLoading(false);
30
+ };
31
+
32
+ return (
33
+ <div className="max-w-5xl mx-auto space-y-8 animate-in slide-in-from-bottom-4 duration-500">
34
+ <div className="bg-gradient-to-br from-indigo-900/20 via-zinc-900 to-zinc-900 border border-zinc-800 rounded-3xl p-10 overflow-hidden relative">
35
+ <div className="absolute top-0 right-0 -mt-10 -mr-10 w-64 h-64 bg-indigo-600/10 blur-[80px] rounded-full"></div>
36
+ <div className="relative z-10">
37
+ <div className="flex items-center space-x-3 mb-4">
38
+ <div className="p-2 rounded-lg bg-indigo-500/20 text-indigo-400">
39
+ <Sparkles size={24} />
40
+ </div>
41
+ <h2 className="text-3xl font-bold text-white tracking-tight">Gemini Oracle Simulation</h2>
42
+ </div>
43
+ <p className="text-zinc-400 text-lg mb-8 max-w-2xl">
44
+ Simulate complex market shifts, regulatory shocks, or internal expenditure reallocations using neural financial forecasting.
45
+ </p>
46
+
47
+ <div className="flex flex-col space-y-4">
48
+ <div className="relative">
49
+ <textarea
50
+ value={prompt}
51
+ onChange={(e) => setPrompt(e.target.value)}
52
+ placeholder="e.g., 'Simulate a 5% inflation spike over 12 months with a focus on cloud compute costs'"
53
+ className="w-full bg-black border-2 border-zinc-800 focus:border-indigo-500/50 rounded-2xl p-6 text-white text-lg min-h-[160px] outline-none transition-all resize-none placeholder:text-zinc-700"
54
+ ></textarea>
55
+ <button
56
+ onClick={() => handleRun()}
57
+ disabled={loading || !prompt}
58
+ className="absolute bottom-4 right-4 px-8 py-3 bg-indigo-600 hover:bg-indigo-500 disabled:bg-zinc-800 disabled:text-zinc-500 text-white rounded-xl font-bold flex items-center space-x-2 transition-all shadow-lg shadow-indigo-900/20"
59
+ >
60
+ {loading ? <Loader2 className="animate-spin" size={20} /> : <Play size={20} fill="currentColor" />}
61
+ <span>{loading ? 'Thinking...' : 'Run Simulation'}</span>
62
+ </button>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+
68
+ <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
69
+ <ScenarioButton
70
+ label="Market Crash"
71
+ description="Simulate 2008-style housing collapse on commercial real estate portfolio."
72
+ onClick={handleRun}
73
+ />
74
+ <ScenarioButton
75
+ label="Hyperinflation"
76
+ description="Model assets against 15%+ annual CPI growth over a 3-year horizon."
77
+ onClick={handleRun}
78
+ />
79
+ <ScenarioButton
80
+ label="AI Expansion"
81
+ description="Forecast ROI on $2M compute spend increase vs reduced dev headcount."
82
+ onClick={handleRun}
83
+ />
84
+ </div>
85
+
86
+ {result && (
87
+ <div className="bg-zinc-900 border border-zinc-800 rounded-3xl p-8 animate-in zoom-in-95 duration-300">
88
+ <div className="flex justify-between items-start mb-8 pb-8 border-b border-zinc-800">
89
+ <div className="flex items-center space-x-4">
90
+ <div className="w-12 h-12 rounded-2xl bg-emerald-500/10 text-emerald-500 flex items-center justify-center">
91
+ <CheckCircle2 size={28} />
92
+ </div>
93
+ <div>
94
+ <h3 className="text-xl font-bold text-white">Simulation Results</h3>
95
+ <p className="text-zinc-500 text-sm mono">REF: {result.simulationId}</p>
96
+ </div>
97
+ </div>
98
+ <div className="text-right">
99
+ <p className="text-[10px] uppercase tracking-widest text-zinc-500 font-bold mb-1">Confidence Score</p>
100
+ <div className="flex items-center space-x-2">
101
+ <div className="h-2 w-32 bg-zinc-800 rounded-full overflow-hidden">
102
+ <div className="h-full bg-emerald-500" style={{ width: `${result.confidenceScore * 100}%` }}></div>
103
+ </div>
104
+ <span className="text-sm font-bold text-zinc-300">{Math.round(result.confidenceScore * 100)}%</span>
105
+ </div>
106
+ </div>
107
+ </div>
108
+
109
+ <div className="grid grid-cols-1 lg:grid-cols-2 gap-12">
110
+ <div>
111
+ <h4 className="text-zinc-400 font-bold uppercase text-[10px] tracking-widest mb-4">Neural Narrative</h4>
112
+ <p className="text-zinc-200 leading-relaxed italic border-l-2 border-blue-500/30 pl-6 py-2">
113
+ "{result.outcomeNarrative}"
114
+ </p>
115
+ </div>
116
+ <div className="bg-black/40 rounded-2xl p-8 border border-zinc-800">
117
+ <div className="space-y-6">
118
+ <div>
119
+ <p className="text-zinc-500 text-xs mb-1">Projected Portfolio Value (12M)</p>
120
+ <p className="text-4xl font-bold text-white mono tracking-tighter">
121
+ ${result.projectedValue?.toLocaleString()}
122
+ </p>
123
+ </div>
124
+ <div className="flex space-x-4">
125
+ <div className="flex-1 p-4 rounded-xl bg-zinc-800/50 border border-zinc-700">
126
+ <p className="text-zinc-500 text-[10px] font-bold uppercase mb-1">Risk Exposure</p>
127
+ <div className="flex items-center space-x-2 text-rose-500">
128
+ <AlertCircle size={16} />
129
+ <span className="font-bold">Elevated</span>
130
+ </div>
131
+ </div>
132
+ <div className="flex-1 p-4 rounded-xl bg-zinc-800/50 border border-zinc-700">
133
+ <p className="text-zinc-500 text-[10px] font-bold uppercase mb-1">Resilience Rating</p>
134
+ <div className="flex items-center space-x-2 text-blue-400">
135
+ <ChevronRight size={16} />
136
+ <span className="font-bold">AA-</span>
137
+ </div>
138
+ </div>
139
+ </div>
140
+ </div>
141
+ </div>
142
+ </div>
143
+ </div>
144
+ )}
145
+ </div>
146
+ );
147
+ };
148
+
149
+ export default Simulation;
views/Sustainability.tsx ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import { Leaf, Wind, Droplets, Factory, Info, ExternalLink, CheckCircle2, Loader2 } from 'lucide-react';
4
+ import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts';
5
+ /* Fix: react-router-dom exports may be flaky in this environment, using standard v6 imports */
6
+ import { useNavigate } from 'react-router-dom';
7
+
8
+ const MOCK_DATA = [
9
+ { name: 'Data Centers', value: 45, color: '#3b82f6' },
10
+ { name: 'Supply Chain', value: 25, color: '#10b981' },
11
+ { name: 'Office Logistics', value: 15, color: '#8b5cf6' },
12
+ { name: 'Business Travel', value: 15, color: '#f59e0b' },
13
+ ];
14
+
15
+ const Sustainability: React.FC = () => {
16
+ const [buying, setBuying] = useState(false);
17
+ const isInitiallyNeutral = localStorage.getItem('esg_neutral') === 'true';
18
+ const [success, setSuccess] = useState(isInitiallyNeutral);
19
+ const navigate = useNavigate();
20
+
21
+ const handleBuyCredits = () => {
22
+ setBuying(true);
23
+ setTimeout(() => {
24
+ setBuying(false);
25
+ setSuccess(true);
26
+ localStorage.setItem('esg_neutral', 'true');
27
+ window.dispatchEvent(new Event('esg-update'));
28
+ }, 2000);
29
+ };
30
+
31
+ return (
32
+ <div className="space-y-8 animate-in fade-in slide-in-from-right-4 duration-700">
33
+ <div className="bg-zinc-900 border border-zinc-800 rounded-[32px] overflow-hidden">
34
+ <div className="grid grid-cols-1 lg:grid-cols-2">
35
+ <div className="p-10 border-r border-zinc-800 min-w-0">
36
+ <div className="flex items-center space-x-3 mb-6">
37
+ <div className={`p-3 rounded-2xl ${success ? 'bg-emerald-500/10 text-emerald-500' : 'bg-blue-500/10 text-blue-500'} transition-colors`}>
38
+ <Leaf size={32} />
39
+ </div>
40
+ <h2 className="text-3xl font-bold text-white">Carbon Ledger</h2>
41
+ </div>
42
+
43
+ <div className="mb-10">
44
+ <p className="text-zinc-500 text-sm font-medium mb-2 uppercase tracking-widest">Aggregate Carbon Intensity</p>
45
+ <div className="flex items-baseline space-x-3">
46
+ <h3 className={`text-6xl font-bold tracking-tighter mono transition-colors ${success ? 'text-emerald-500' : 'text-white'}`}>
47
+ {success ? '0' : '1,242'}
48
+ </h3>
49
+ <span className="text-zinc-400 font-bold text-xl uppercase">Kg CO2e</span>
50
+ </div>
51
+ {success && <p className="text-emerald-500 text-xs font-bold mt-2 uppercase tracking-[0.2em]">Net Zero Achieved</p>}
52
+ </div>
53
+
54
+ <div className="space-y-4">
55
+ <div className="flex items-center justify-between p-4 bg-black/50 rounded-2xl border border-zinc-800">
56
+ <div className="flex items-center space-x-4">
57
+ <div className="p-2 bg-blue-500/10 text-blue-500 rounded-lg"><Wind size={20} /></div>
58
+ <span className="font-semibold text-zinc-300">Renewable Energy</span>
59
+ </div>
60
+ <span className="text-emerald-500 font-bold">100% Offset</span>
61
+ </div>
62
+ <div className="flex items-center justify-between p-4 bg-black/50 rounded-2xl border border-zinc-800">
63
+ <div className="flex items-center space-x-4">
64
+ <div className="p-2 bg-amber-500/10 text-amber-500 rounded-lg"><Factory size={20} /></div>
65
+ <span className="font-semibold text-zinc-300">Verified Projects</span>
66
+ </div>
67
+ <span className="text-zinc-400 font-bold">{success ? '4 Active' : 'None'}</span>
68
+ </div>
69
+ </div>
70
+ </div>
71
+
72
+ <div className="p-10 bg-zinc-950/50 flex flex-col min-w-0">
73
+ <h4 className="text-white font-bold text-xl mb-2">Emission Matrix</h4>
74
+ <p className="text-zinc-500 text-sm mb-8">Quantum audit of environmental externalities</p>
75
+
76
+ {/* Explicit container fix for Recharts dimension warning */}
77
+ <div className="flex-1 relative min-h-[300px] h-[300px]">
78
+ <ResponsiveContainer width="100%" height="100%">
79
+ <PieChart>
80
+ <Pie
81
+ data={MOCK_DATA}
82
+ cx="50%"
83
+ cy="50%"
84
+ innerRadius={60}
85
+ outerRadius={80}
86
+ paddingAngle={8}
87
+ dataKey="value"
88
+ >
89
+ {MOCK_DATA.map((entry, index) => (
90
+ <Cell key={`cell-${index}`} fill={success ? '#10b981' : entry.color} stroke="none" />
91
+ ))}
92
+ </Pie>
93
+ <Tooltip
94
+ contentStyle={{ backgroundColor: '#18181b', border: 'none', borderRadius: '12px', fontSize: '12px' }}
95
+ itemStyle={{ color: '#fff' }}
96
+ />
97
+ </PieChart>
98
+ </ResponsiveContainer>
99
+ <div className="absolute inset-0 flex items-center justify-center pointer-events-none">
100
+ <div className="text-center">
101
+ <p className="text-[10px] text-zinc-500 uppercase font-bold tracking-widest">ESG Rating</p>
102
+ <p className="text-emerald-500 font-bold text-sm">{success ? 'AAA' : 'AA-'}</p>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
108
+ </div>
109
+
110
+ <div className={`transition-all duration-500 ${success ? 'bg-emerald-600/10 border-emerald-500/30' : 'bg-blue-600/10 border-blue-500/20'} border rounded-[32px] p-10`}>
111
+ <div className="flex items-start space-x-6">
112
+ <div className={`p-4 ${success ? 'bg-emerald-600 shadow-emerald-900/40' : 'bg-blue-600 shadow-blue-900/40'} text-white rounded-2xl shadow-xl transition-all`}>
113
+ {success ? <CheckCircle2 size={32} /> : <Info size={32} />}
114
+ </div>
115
+ <div className="flex-1">
116
+ <h3 className="text-white font-bold text-2xl mb-2 tracking-tight">
117
+ {success ? 'Portfolio Neutrality Confirmed' : 'Action Required: Q3 Carbon Deficit'}
118
+ </h3>
119
+ <p className={`${success ? 'text-emerald-100/70' : 'text-blue-100/70'} text-lg leading-relaxed mb-8`}>
120
+ {success
121
+ ? "Your corporate ledger is now verified Carbon Neutral. 25 Carbon Credits have been successfully retired. Certificate #QC-8841-B is available in your Audit Report."
122
+ : "Quantum analysis identifies 1,242Kg of unmitigated emissions. Purchase 25 verified credits to achieve neutrality."}
123
+ </p>
124
+ <div className="flex gap-4">
125
+ {!success ? (
126
+ <button
127
+ onClick={handleBuyCredits}
128
+ disabled={buying}
129
+ className="px-8 py-3.5 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-bold transition-all flex items-center gap-3 shadow-xl shadow-blue-900/30"
130
+ >
131
+ {buying ? <Loader2 size={20} className="animate-spin" /> : <Leaf size={20} />}
132
+ <span>{buying ? 'Executing Quantum Offset...' : 'Buy Carbon Credits'}</span>
133
+ </button>
134
+ ) : (
135
+ <button
136
+ onClick={() => navigate('/analytics')}
137
+ className="px-8 py-3.5 bg-emerald-600 hover:bg-emerald-500 text-white rounded-2xl font-bold transition-all shadow-xl shadow-emerald-900/30"
138
+ >
139
+ View ESG Audit Report
140
+ </button>
141
+ )}
142
+ <button
143
+ onClick={() => navigate('/analytics')}
144
+ className="px-8 py-3.5 bg-zinc-800 hover:bg-zinc-700 text-zinc-200 rounded-2xl font-bold transition-all flex items-center gap-2"
145
+ >
146
+ <span>Full ESG Transparency</span>
147
+ <ExternalLink size={18} />
148
+ </button>
149
+ </div>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ </div>
154
+ );
155
+ };
156
+
157
+ export default Sustainability;
views/Validations.tsx ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React, { useState } from 'react';
3
+ import {
4
+ ShieldCheck,
5
+ Search,
6
+ Building2,
7
+ Globe,
8
+ AlertCircle,
9
+ CheckCircle2,
10
+ Loader2,
11
+ Terminal,
12
+ ArrowRight,
13
+ Info,
14
+ Key,
15
+ MapPin,
16
+ ShieldAlert,
17
+ Zap
18
+ } from 'lucide-react';
19
+
20
+ const Validations: React.FC = () => {
21
+ const [routingNumber, setRoutingNumber] = useState('');
22
+ const [basicToken, setBasicToken] = useState('');
23
+ const [loading, setLoading] = useState(false);
24
+ const [result, setResult] = useState<any>(null);
25
+ const [error, setError] = useState<string | null>(null);
26
+
27
+ const handleValidate = async (e: React.FormEvent) => {
28
+ e.preventDefault();
29
+ if (!routingNumber || !basicToken) {
30
+ setError("Routing number and Basic Token are required.");
31
+ return;
32
+ }
33
+
34
+ setLoading(true);
35
+ setError(null);
36
+ setResult(null);
37
+
38
+ try {
39
+ const response = await fetch(`https://try.readme.io/https://app.moderntreasury.com/api/validations/routing_numbers?routing_number=${routingNumber}&routing_number_type=aba`, {
40
+ method: "GET",
41
+ headers: {
42
+ "accept": "application/json",
43
+ "accept-language": "en-US,en;q=0.9",
44
+ "authorization": `Basic ${basicToken}`,
45
+ "cache-control": "no-cache",
46
+ "pragma": "no-cache",
47
+ "sec-fetch-dest": "empty",
48
+ "sec-fetch-mode": "cors",
49
+ "sec-fetch-site": "cross-site",
50
+ "x-readme-api-explorer": "5.590.0"
51
+ },
52
+ mode: "cors",
53
+ credentials: "include"
54
+ });
55
+
56
+ if (!response.ok) {
57
+ const errData = await response.json().catch(() => ({}));
58
+ throw new Error(errData?.errors?.[0]?.message || `HTTP ${response.status}: API Handshake Failed`);
59
+ }
60
+
61
+ const data = await response.json();
62
+ setResult(data);
63
+ } catch (err: any) {
64
+ setError(err.message || "Network Error: Failed to synchronize with validation node.");
65
+ } finally {
66
+ setLoading(false);
67
+ }
68
+ };
69
+
70
+ return (
71
+ <div className="max-w-5xl mx-auto space-y-10 animate-in slide-in-from-bottom-6 duration-700">
72
+ <div className="text-center space-y-4">
73
+ <div className="w-20 h-20 bg-blue-600/10 text-blue-500 rounded-3xl flex items-center justify-center mx-auto shadow-2xl">
74
+ <ShieldCheck size={40} />
75
+ </div>
76
+ <h2 className="text-4xl font-black text-white italic tracking-tighter uppercase">Registry <span className="text-blue-500 not-italic">Validator</span></h2>
77
+ <p className="text-zinc-500 text-sm max-w-md mx-auto font-medium">Modern Treasury Bridge: Live Routing Integrity Screening</p>
78
+ </div>
79
+
80
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-12 shadow-2xl relative overflow-hidden group">
81
+ <div className="absolute top-0 right-0 p-10 opacity-5 group-hover:opacity-10 transition-opacity">
82
+ <Terminal size={140} />
83
+ </div>
84
+
85
+ <form onSubmit={handleValidate} className="relative z-10 space-y-6">
86
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
87
+ <div className="space-y-2">
88
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">ABA Routing Number</label>
89
+ <div className="relative">
90
+ <Search className="absolute left-6 top-1/2 -translate-y-1/2 text-zinc-600" size={18} />
91
+ <input
92
+ value={routingNumber}
93
+ onChange={e => setRoutingNumber(e.target.value)}
94
+ placeholder="e.g. 021000021"
95
+ className="w-full bg-black border-2 border-zinc-900 focus:border-blue-500/50 rounded-2xl py-5 pl-16 pr-6 text-white text-sm outline-none transition-all placeholder:text-zinc-800 font-bold"
96
+ />
97
+ </div>
98
+ </div>
99
+ <div className="space-y-2">
100
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Basic Auth Token</label>
101
+ <div className="relative">
102
+ <Key className="absolute left-6 top-1/2 -translate-y-1/2 text-zinc-600" size={18} />
103
+ <input
104
+ type="password"
105
+ value={basicToken}
106
+ onChange={e => setBasicToken(e.target.value)}
107
+ placeholder="Basic {token}"
108
+ className="w-full bg-black border-2 border-zinc-900 focus:border-blue-500/50 rounded-2xl py-5 pl-16 pr-6 text-white text-sm outline-none transition-all placeholder:text-zinc-800 font-mono"
109
+ />
110
+ </div>
111
+ </div>
112
+ </div>
113
+ <button
114
+ disabled={loading || !routingNumber || !basicToken}
115
+ className="w-full py-6 bg-blue-600 hover:bg-blue-500 disabled:bg-zinc-900 disabled:text-zinc-700 text-white rounded-2xl font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-3 shadow-xl shadow-blue-900/30"
116
+ >
117
+ {loading ? <Loader2 className="animate-spin" size={20} /> : <Zap size={20} />}
118
+ <span>{loading ? 'Performing Handshake...' : 'Synchronize Validator'}</span>
119
+ </button>
120
+ </form>
121
+ </div>
122
+
123
+ {error && (
124
+ <div className="p-8 bg-rose-600/10 border border-rose-500/20 rounded-[2rem] flex items-center gap-6 animate-in zoom-in-95">
125
+ <ShieldAlert className="text-rose-500 shrink-0" size={32} />
126
+ <div>
127
+ <h4 className="text-rose-500 font-black uppercase text-xs tracking-widest mb-1">Handshake Interrupted</h4>
128
+ <p className="text-zinc-400 text-sm font-medium">{error}</p>
129
+ </div>
130
+ </div>
131
+ )}
132
+
133
+ {result && (
134
+ <div className="grid grid-cols-1 lg:grid-cols-3 gap-8 animate-in slide-in-from-top-6 duration-700">
135
+ <div className="lg:col-span-1 bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 space-y-8 shadow-2xl relative overflow-hidden">
136
+ <div className="absolute top-0 right-0 p-8 opacity-5">
137
+ <Building2 size={80} />
138
+ </div>
139
+ <div className="flex items-center gap-4">
140
+ <div className="w-12 h-12 bg-emerald-500/10 text-emerald-500 rounded-2xl flex items-center justify-center">
141
+ <CheckCircle2 size={24} />
142
+ </div>
143
+ <div>
144
+ <h3 className="text-white font-black text-lg uppercase italic tracking-tighter">Verified Node</h3>
145
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">{result.routing_number}</p>
146
+ </div>
147
+ </div>
148
+ <div className="space-y-6">
149
+ <div className="p-6 bg-black rounded-2xl border border-zinc-900">
150
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-2">Legal Entity</p>
151
+ <p className="text-white font-bold text-sm uppercase leading-relaxed">{result.bank_name}</p>
152
+ </div>
153
+ <div className="p-6 bg-black rounded-2xl border border-zinc-900">
154
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-2">Node Type</p>
155
+ <p className="text-blue-500 font-black uppercase text-sm mono">{result.routing_number_type}</p>
156
+ </div>
157
+ </div>
158
+ </div>
159
+
160
+ <div className="lg:col-span-2 bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 space-y-8 shadow-2xl relative overflow-hidden group">
161
+ <div className="absolute top-0 right-0 p-10 opacity-5 group-hover:scale-110 transition-transform duration-1000">
162
+ <MapPin size={120} className="text-blue-500" />
163
+ </div>
164
+ <div className="flex items-center gap-4">
165
+ <div className="p-3 bg-blue-600/10 text-blue-500 rounded-xl">
166
+ <Globe size={24} />
167
+ </div>
168
+ <h3 className="text-white font-black text-lg uppercase italic tracking-tighter">Geospatial Metadata</h3>
169
+ </div>
170
+
171
+ <div className="grid grid-cols-1 md:grid-cols-2 gap-8">
172
+ <div className="space-y-6">
173
+ <div className="p-6 bg-black rounded-2xl border border-zinc-900">
174
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-3">Host Address</p>
175
+ <p className="text-zinc-200 text-sm font-medium leading-relaxed">
176
+ {result.bank_address?.line1}<br />
177
+ {result.bank_address?.locality}, {result.bank_address?.region} {result.bank_address?.postal_code}<br />
178
+ {result.bank_address?.country}
179
+ </p>
180
+ </div>
181
+ </div>
182
+ <div className="space-y-6">
183
+ <div className="p-6 bg-black rounded-2xl border border-zinc-900">
184
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-3">Supported Protocols</p>
185
+ <div className="flex flex-wrap gap-2">
186
+ {result.supported_payment_types?.map((t: string) => (
187
+ <span key={t} className="px-3 py-1 bg-blue-500/10 border border-blue-500/20 rounded-full text-[9px] font-black uppercase text-blue-500">
188
+ {t} Optimized
189
+ </span>
190
+ ))}
191
+ </div>
192
+ </div>
193
+ <div className="p-6 bg-black rounded-2xl border border-zinc-900">
194
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-2">Sanctions Status</p>
195
+ <div className="flex items-center gap-2">
196
+ <CheckCircle2 size={12} className="text-emerald-500" />
197
+ <span className="text-emerald-500 font-black uppercase text-[10px]">Zero Deviations</span>
198
+ </div>
199
+ </div>
200
+ </div>
201
+ </div>
202
+ </div>
203
+ </div>
204
+ )}
205
+ </div>
206
+ );
207
+ };
208
+
209
+ export default Validations;
views/VirtualAccounts.tsx ADDED
@@ -0,0 +1,505 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useMemo } from 'react';
2
+ import {
3
+ Network,
4
+ Layers,
5
+ Search,
6
+ RefreshCw,
7
+ ArrowUpRight,
8
+ Building,
9
+ ShieldCheck,
10
+ Plus,
11
+ Trash2,
12
+ ChevronRight,
13
+ MoreVertical,
14
+ X,
15
+ Loader2,
16
+ Terminal,
17
+ Activity,
18
+ Settings2,
19
+ CheckCircle2,
20
+ Cpu,
21
+ AlertCircle
22
+ } from 'lucide-react';
23
+ import { VirtualAccount } from '../types/index.ts';
24
+ import { callGemini } from '../services/geminiService.ts';
25
+
26
+ const INITIAL_MOCK_VA: VirtualAccount[] = [
27
+ {
28
+ id: 'VA-88021',
29
+ object: 'virtual_account',
30
+ live_mode: true,
31
+ created_at: '2024-02-15T10:00:00Z',
32
+ updated_at: '2024-03-31T14:20:00Z',
33
+ name: 'Q1 Sales Collection - UK',
34
+ description: 'Virtual account for routing Sterling sales to main treasury.',
35
+ counterparty_id: 'CP-8801',
36
+ internal_account_id: 'IA-4401',
37
+ account_details: [],
38
+ routing_details: [],
39
+ status: 'ACTIVE'
40
+ },
41
+ {
42
+ id: 'VA-90112',
43
+ object: 'virtual_account',
44
+ live_mode: true,
45
+ created_at: '2024-03-01T09:30:00Z',
46
+ updated_at: '2024-03-31T12:00:00Z',
47
+ name: 'EMEA Vendor Pass-through',
48
+ description: 'Temporary buffer for Eurozone contractor payments.',
49
+ counterparty_id: null,
50
+ internal_account_id: 'IA-9902',
51
+ account_details: [],
52
+ routing_details: [],
53
+ status: 'ACTIVE'
54
+ },
55
+ {
56
+ id: 'VA-11234',
57
+ object: 'virtual_account',
58
+ live_mode: true,
59
+ created_at: '2023-12-10T11:45:00Z',
60
+ updated_at: '2024-03-25T16:00:00Z',
61
+ name: 'Legacy APAC Bridge',
62
+ description: 'Deprecated node being phased out.',
63
+ counterparty_id: 'CP-1105',
64
+ internal_account_id: 'IA-4401',
65
+ account_details: [],
66
+ routing_details: [],
67
+ status: 'PENDING'
68
+ }
69
+ ];
70
+
71
+ const VirtualAccounts: React.FC = () => {
72
+ const [loading, setLoading] = useState(false);
73
+ const [accounts, setAccounts] = useState<VirtualAccount[]>(INITIAL_MOCK_VA);
74
+
75
+ // Modal States
76
+ const [isProvisionOpen, setIsProvisionOpen] = useState(false);
77
+ const [isConfigOpen, setIsConfigOpen] = useState(false);
78
+ const [isReconcilerOpen, setIsReconcilerOpen] = useState(false);
79
+ const [selectedVa, setSelectedVa] = useState<VirtualAccount | null>(null);
80
+
81
+ // Provisioning State
82
+ const [provisioning, setProvisioning] = useState(false);
83
+ const [provisionError, setProvisionError] = useState<string | null>(null);
84
+ const [newVa, setNewVa] = useState({ name: '', description: '', parent: 'IA-4401' });
85
+
86
+ // Reconciler State
87
+ const [reconciling, setReconciling] = useState(false);
88
+ const [reconcileReport, setReconcileReport] = useState<string | null>(null);
89
+
90
+ const refreshNodes = () => {
91
+ setLoading(true);
92
+ setTimeout(() => setLoading(false), 1200);
93
+ };
94
+
95
+ const handleProvision = async () => {
96
+ setProvisionError(null);
97
+ const trimmedName = newVa.name.trim();
98
+
99
+ // 1. Client-Side Parity Check: Duplicate Names
100
+ if (accounts.some(acc => acc.name.toLowerCase() === trimmedName.toLowerCase())) {
101
+ setProvisionError("Node identity collision: A node with this designation already exists in the virtual mesh.");
102
+ return;
103
+ }
104
+
105
+ setProvisioning(true);
106
+
107
+ try {
108
+ // 2. Artificial neural network handshake simulation
109
+ await new Promise(r => setTimeout(r, 2000));
110
+
111
+ // 3. Simulated Registry-Level Validation (e.g. invalid parent or system refusal)
112
+ if (trimmedName.toLowerCase() === 'fail') {
113
+ throw new Error("Registry Rejection: Parent Account ID " + newVa.parent + " refused handshake authorization.");
114
+ }
115
+
116
+ const node: VirtualAccount = {
117
+ id: `VA-${Math.floor(10000 + Math.random() * 90000)}`,
118
+ object: 'virtual_account',
119
+ live_mode: true,
120
+ created_at: new Date().toISOString(),
121
+ updated_at: new Date().toISOString(),
122
+ name: trimmedName || 'Untitled Node',
123
+ description: newVa.description || 'Provisioned via Quantum Foundry',
124
+ counterparty_id: null,
125
+ internal_account_id: newVa.parent,
126
+ account_details: [],
127
+ routing_details: [],
128
+ status: 'ACTIVE'
129
+ };
130
+
131
+ setAccounts([node, ...accounts]);
132
+ setIsProvisionOpen(false);
133
+ setNewVa({ name: '', description: '', parent: 'IA-4401' });
134
+ } catch (err: any) {
135
+ setProvisionError(err.message || "Protocol Handshake Failure: The global registry node timed out.");
136
+ } finally {
137
+ setProvisioning(false);
138
+ }
139
+ };
140
+
141
+ const handleRunReconciler = async () => {
142
+ setReconciling(true);
143
+ setReconcileReport(null);
144
+ try {
145
+ const response = await callGemini(
146
+ 'gemini-3-flash-preview',
147
+ "Generate a professional 2-sentence summary for a financial reconciliation process. Mention 99.9% match accuracy and the resolution of 12 incoming virtual node signals across UK and EMEA regions. Tone: Technical and secure."
148
+ );
149
+ setReconcileReport(response.candidates?.[0]?.content?.parts?.[0]?.text);
150
+ } catch (err) {
151
+ setReconcileReport("Neural reconciliation complete. Signal integrity verified at 99.9%. All ledger entries matched with zero drift.");
152
+ } finally {
153
+ setReconciling(false);
154
+ }
155
+ };
156
+
157
+ const deleteNode = (id: string) => {
158
+ if (confirm("De-provision node? This will sever all incoming signal routing.")) {
159
+ setAccounts(accounts.filter(a => a.id !== id));
160
+ }
161
+ };
162
+
163
+ const openProvisionModal = () => {
164
+ setProvisionError(null);
165
+ setIsProvisionOpen(true);
166
+ };
167
+
168
+ return (
169
+ <div className="space-y-10 animate-in fade-in duration-700">
170
+ <div className="flex flex-col md:flex-row justify-between items-start md:items-end gap-6">
171
+ <div>
172
+ <h2 className="text-3xl font-black text-white italic tracking-tighter uppercase mb-2">Virtual <span className="text-blue-500 not-italic">Nodes</span></h2>
173
+ <p className="text-zinc-500 text-[10px] font-black uppercase tracking-[0.3em]">High-Volume Disbursement & Collection Layer</p>
174
+ </div>
175
+ <div className="flex gap-4">
176
+ <button
177
+ onClick={refreshNodes}
178
+ className="flex items-center space-x-2 px-6 py-3 bg-zinc-900 border border-zinc-800 hover:border-zinc-700 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all"
179
+ >
180
+ <RefreshCw size={14} className={loading ? 'animate-spin' : ''} />
181
+ <span>Poll Virtual Mesh</span>
182
+ </button>
183
+ <button
184
+ onClick={openProvisionModal}
185
+ className="flex items-center space-x-2 px-6 py-3 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest transition-all shadow-xl shadow-blue-900/40"
186
+ >
187
+ <Plus size={14} />
188
+ <span>Provision Virtual Account</span>
189
+ </button>
190
+ </div>
191
+ </div>
192
+
193
+ <div className="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-3 gap-8">
194
+ {accounts.map(va => (
195
+ <div key={va.id} className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 flex flex-col justify-between group hover:border-blue-500/30 transition-all relative overflow-hidden shadow-2xl">
196
+ <div className="absolute top-0 right-0 p-8 opacity-5 group-hover:opacity-10 transition-opacity">
197
+ <Layers size={120} />
198
+ </div>
199
+
200
+ <div className="relative z-10">
201
+ <div className="flex justify-between items-start mb-10">
202
+ <div className="w-16 h-16 bg-zinc-900 rounded-3xl flex items-center justify-center border border-zinc-800 text-blue-500 shadow-xl group-hover:scale-110 transition-transform duration-500">
203
+ <Network size={32} />
204
+ </div>
205
+ <div className={`px-4 py-1.5 rounded-full border text-[10px] font-black uppercase tracking-widest ${
206
+ va.status === 'ACTIVE' ? 'bg-emerald-500/10 border-emerald-500/20 text-emerald-500' : 'bg-amber-500/10 border-amber-500/20 text-amber-500'
207
+ }`}>
208
+ {va.status}
209
+ </div>
210
+ </div>
211
+
212
+ <h3 className="text-xl font-black text-white italic tracking-tighter uppercase mb-2 truncate pr-10">{va.name}</h3>
213
+ <p className="text-zinc-500 text-xs leading-relaxed mb-8 h-12 overflow-hidden line-clamp-2">{va.description}</p>
214
+
215
+ <div className="space-y-4 mb-10">
216
+ <div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.2em] text-zinc-600">
217
+ <span>Parent Registry</span>
218
+ <span className="text-zinc-300 mono">{va.internal_account_id}</span>
219
+ </div>
220
+ <div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.2em] text-zinc-600">
221
+ <span>ID Segment</span>
222
+ <span className="text-zinc-300 mono">{va.id}</span>
223
+ </div>
224
+ <div className="flex justify-between items-center text-[10px] font-black uppercase tracking-[0.2em] text-zinc-600">
225
+ <span>Routing Layer</span>
226
+ <span className="text-blue-500 mono">FDX_V6_MESH</span>
227
+ </div>
228
+ </div>
229
+ </div>
230
+
231
+ <div className="flex gap-4 relative z-10">
232
+ <button
233
+ onClick={() => {
234
+ setSelectedVa(va);
235
+ setIsConfigOpen(true);
236
+ }}
237
+ className="flex-1 py-4 bg-zinc-900 hover:bg-zinc-800 text-white rounded-2xl text-[10px] font-black uppercase tracking-widest transition-all border border-zinc-800 flex items-center justify-center gap-2"
238
+ >
239
+ <Settings2 size={14} />
240
+ Config
241
+ </button>
242
+ <button
243
+ onClick={() => deleteNode(va.id)}
244
+ className="p-4 bg-zinc-900 hover:bg-rose-500/10 text-zinc-600 hover:text-rose-500 rounded-2xl transition-all border border-zinc-800"
245
+ >
246
+ <Trash2 size={18} />
247
+ </button>
248
+ </div>
249
+ </div>
250
+ ))}
251
+
252
+ <button
253
+ onClick={openProvisionModal}
254
+ className="border-2 border-dashed border-zinc-900 rounded-[3rem] p-10 flex flex-col items-center justify-center group hover:border-zinc-700 transition-all gap-4 bg-black/20"
255
+ >
256
+ <div className="w-16 h-16 bg-zinc-950 rounded-full flex items-center justify-center text-zinc-800 group-hover:text-blue-500 transition-colors shadow-2xl">
257
+ <Plus size={32} />
258
+ </div>
259
+ <span className="text-[10px] font-black uppercase tracking-[0.4em] text-zinc-700 group-hover:text-zinc-400">Initialize Node</span>
260
+ </button>
261
+ </div>
262
+
263
+ <div className="bg-zinc-950 border border-zinc-900 rounded-[3rem] p-10 relative overflow-hidden group shadow-2xl">
264
+ <div className="flex flex-col md:flex-row justify-between items-center gap-10">
265
+ <div className="flex items-center gap-8">
266
+ <div className="w-20 h-20 bg-blue-600/10 rounded-3xl flex items-center justify-center text-blue-500 shadow-2xl shadow-blue-500/5">
267
+ <ShieldCheck size={40} />
268
+ </div>
269
+ <div>
270
+ <h4 className="text-xl font-black text-white italic tracking-tighter uppercase mb-2">Automated <span className="text-blue-500 not-italic">Reconciliation</span></h4>
271
+ <p className="text-zinc-500 text-xs max-w-lg leading-relaxed font-medium">Lumina Quantum Oracle automatically matches incoming virtual node signals to your internal accounting ledger with 99.9% precision.</p>
272
+ </div>
273
+ </div>
274
+ <button
275
+ onClick={() => {
276
+ setIsReconcilerOpen(true);
277
+ handleRunReconciler();
278
+ }}
279
+ className="px-10 py-5 bg-white hover:bg-zinc-200 text-black rounded-2xl font-black text-xs uppercase tracking-[0.2em] transition-all flex items-center gap-4 shadow-xl"
280
+ >
281
+ <span>View Reconciler</span>
282
+ <ArrowUpRight size={18} />
283
+ </button>
284
+ </div>
285
+ </div>
286
+
287
+ {/* Provision Modal */}
288
+ {isProvisionOpen && (
289
+ <div className="fixed inset-0 z-[100] flex items-center justify-center p-6 backdrop-blur-sm bg-black/60">
290
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3rem] p-10 shadow-2xl animate-in zoom-in-95">
291
+ <div className="flex justify-between items-start mb-10">
292
+ <div className="flex items-center gap-4">
293
+ <div className="w-14 h-14 bg-blue-600/10 text-blue-500 rounded-2xl flex items-center justify-center">
294
+ <Network size={28} />
295
+ </div>
296
+ <div>
297
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Provision <span className="text-blue-500 not-italic">Node</span></h3>
298
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">Initialize Virtual Data Sink</p>
299
+ </div>
300
+ </div>
301
+ <button onClick={() => setIsProvisionOpen(false)} className="p-2 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
302
+ </div>
303
+
304
+ <div className="space-y-6">
305
+ {provisionError && (
306
+ <div className="p-4 bg-rose-500/10 border border-rose-500/20 rounded-2xl flex items-center gap-3 animate-in fade-in slide-in-from-top-2">
307
+ <AlertCircle size={16} className="text-rose-500 shrink-0" />
308
+ <p className="text-[10px] font-bold text-rose-200 uppercase tracking-tight leading-relaxed">{provisionError}</p>
309
+ </div>
310
+ )}
311
+
312
+ <div className="space-y-2">
313
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Node Name</label>
314
+ <input
315
+ value={newVa.name}
316
+ onChange={e => {
317
+ setProvisionError(null);
318
+ setNewVa({...newVa, name: e.target.value});
319
+ }}
320
+ placeholder="e.g. Q4 Marketing Buffer"
321
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-2xl py-4 px-5 text-white text-sm outline-none transition-all placeholder:text-zinc-800 font-bold"
322
+ />
323
+ </div>
324
+ <div className="space-y-2">
325
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Parent Account</label>
326
+ <select
327
+ value={newVa.parent}
328
+ onChange={e => {
329
+ setProvisionError(null);
330
+ setNewVa({...newVa, parent: e.target.value});
331
+ }}
332
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-2xl py-4 px-5 text-white text-sm outline-none transition-all font-bold appearance-none"
333
+ >
334
+ <option value="IA-4401">IA-4401 (J.P. Morgan)</option>
335
+ <option value="IA-9902">IA-9902 (Deutsche Bank)</option>
336
+ <option value="IA-1105">IA-1105 (Custody)</option>
337
+ </select>
338
+ </div>
339
+ <div className="space-y-2">
340
+ <label className="text-[10px] font-black text-zinc-600 uppercase tracking-widest ml-1">Contextual Description</label>
341
+ <textarea
342
+ value={newVa.description}
343
+ onChange={e => {
344
+ setProvisionError(null);
345
+ setNewVa({...newVa, description: e.target.value});
346
+ }}
347
+ placeholder="Routing purpose and ledger mapping..."
348
+ className="w-full bg-black border border-zinc-800 focus:border-blue-500/50 rounded-2xl py-4 px-5 text-white text-sm outline-none transition-all placeholder:text-zinc-800 font-bold h-24 resize-none"
349
+ />
350
+ </div>
351
+
352
+ <button
353
+ onClick={handleProvision}
354
+ disabled={provisioning || !newVa.name.trim()}
355
+ className="w-full py-5 bg-blue-600 hover:bg-blue-500 text-white rounded-[2rem] font-black text-xs uppercase tracking-[0.3em] transition-all flex items-center justify-center gap-4 shadow-xl shadow-blue-900/40 disabled:opacity-50"
356
+ >
357
+ {provisioning ? <Loader2 className="animate-spin" size={20} /> : <ArrowUpRight size={20} />}
358
+ <span>{provisioning ? 'Initializing Handshake...' : 'Establish Virtual Node'}</span>
359
+ </button>
360
+ </div>
361
+ </div>
362
+ </div>
363
+ )}
364
+
365
+ {/* Config Modal */}
366
+ {isConfigOpen && selectedVa && (
367
+ <div className="fixed inset-0 z-[100] flex items-center justify-center p-6 backdrop-blur-sm bg-black/60">
368
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-lg rounded-[3rem] p-10 shadow-2xl animate-in zoom-in-95">
369
+ <div className="flex justify-between items-start mb-10">
370
+ <div className="flex items-center gap-4">
371
+ <div className="w-14 h-14 bg-zinc-900 border border-zinc-800 rounded-2xl flex items-center justify-center text-blue-500">
372
+ <Settings2 size={28} />
373
+ </div>
374
+ <div>
375
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Node <span className="text-blue-500 not-italic">Config</span></h3>
376
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">{selectedVa.id}</p>
377
+ </div>
378
+ </div>
379
+ <button onClick={() => setIsConfigOpen(false)} className="p-2 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
380
+ </div>
381
+
382
+ <div className="space-y-8">
383
+ <div className="p-6 bg-black rounded-3xl border border-zinc-900 flex items-center justify-between">
384
+ <div>
385
+ <p className="text-[10px] font-black text-zinc-600 uppercase tracking-widest mb-1">Signal Status</p>
386
+ <p className="text-white font-bold text-lg italic uppercase">{selectedVa.status}</p>
387
+ </div>
388
+ <div className="w-3 h-3 bg-emerald-500 rounded-full animate-pulse shadow-[0_0_8px_#10b981]"></div>
389
+ </div>
390
+
391
+ <div className="grid grid-cols-2 gap-4">
392
+ <button className="p-5 bg-zinc-900 hover:bg-zinc-800 text-white rounded-2xl border border-zinc-800 transition-all font-black text-[10px] uppercase tracking-widest">
393
+ Rotate Keys
394
+ </button>
395
+ <button className="p-5 bg-zinc-900 hover:bg-zinc-800 text-white rounded-2xl border border-zinc-800 transition-all font-black text-[10px] uppercase tracking-widest">
396
+ Flush Cache
397
+ </button>
398
+ </div>
399
+
400
+ <div className="p-6 bg-blue-600/5 border border-blue-500/20 rounded-3xl">
401
+ <p className="text-[9px] font-black text-blue-500 uppercase tracking-widest mb-3 flex items-center gap-2">
402
+ <Activity size={10} />
403
+ Network Payload Metadata
404
+ </p>
405
+ <p className="text-[10px] text-zinc-500 font-mono leading-relaxed">
406
+ Created: {new Date(selectedVa.created_at).toLocaleString()}<br />
407
+ Routing: FDX_V6_NATIVE_BRIDGE<br />
408
+ Latency: 12ms avg.
409
+ </p>
410
+ </div>
411
+
412
+ <button
413
+ onClick={() => setIsConfigOpen(false)}
414
+ className="w-full py-4 bg-zinc-100 hover:bg-white text-black rounded-2xl font-black text-xs uppercase tracking-widest transition-all"
415
+ >
416
+ Close Configuration
417
+ </button>
418
+ </div>
419
+ </div>
420
+ </div>
421
+ )}
422
+
423
+ {/* Reconciler Modal */}
424
+ {isReconcilerOpen && (
425
+ <div className="fixed inset-0 z-[100] flex items-center justify-center p-6 backdrop-blur-sm bg-black/60">
426
+ <div className="bg-zinc-950 border border-zinc-900 w-full max-w-2xl rounded-[3rem] p-10 shadow-2xl animate-in slide-in-from-bottom-6">
427
+ <div className="flex justify-between items-start mb-10">
428
+ <div className="flex items-center gap-4">
429
+ <div className="w-14 h-14 bg-emerald-500/10 text-emerald-500 rounded-2xl flex items-center justify-center">
430
+ <ShieldCheck size={28} />
431
+ </div>
432
+ <div>
433
+ <h3 className="text-2xl font-black text-white italic tracking-tighter uppercase">Quantum <span className="text-emerald-500 not-italic">Reconciler</span></h3>
434
+ <p className="text-[10px] text-zinc-500 font-black uppercase tracking-widest">Automated Signal Matching Engine</p>
435
+ </div>
436
+ </div>
437
+ <button onClick={() => setIsReconcilerOpen(false)} className="p-2 text-zinc-600 hover:text-white transition-colors"><X size={24} /></button>
438
+ </div>
439
+
440
+ <div className="space-y-8">
441
+ <div className="bg-black border border-zinc-900 rounded-[2.5rem] overflow-hidden shadow-2xl h-48 flex flex-col">
442
+ <div className="bg-zinc-900/80 px-6 py-3 border-b border-zinc-800 flex justify-between items-center">
443
+ <div className="flex items-center gap-2">
444
+ <Terminal size={12} className="text-blue-500" />
445
+ <span className="text-[9px] font-black uppercase tracking-widest text-zinc-500">Live Matching Console</span>
446
+ </div>
447
+ <div className="flex items-center gap-2">
448
+ <Activity size={12} className="text-emerald-500 animate-pulse" />
449
+ <span className="text-[8px] font-black uppercase text-zinc-600">Sync Active</span>
450
+ </div>
451
+ </div>
452
+ <div className="flex-1 p-6 font-mono text-[10px] overflow-y-auto custom-scrollbar space-y-2">
453
+ <div className="text-zinc-500">[14:22:01] Received signal VA-88021...</div>
454
+ <div className="text-zinc-500">[14:22:02] Matching against Treasury Operating Account (IA-4401)...</div>
455
+ <div className="text-emerald-500 font-bold">[14:22:03] MATCH FOUND: 100% confidence. Ledger updated.</div>
456
+ <div className="text-zinc-500">[14:22:10] Scanning global fabric for adrift payloads...</div>
457
+ </div>
458
+ </div>
459
+
460
+ <div className="p-8 bg-zinc-900/50 border border-zinc-800 rounded-[2rem] relative group">
461
+ <div className="absolute top-0 right-0 p-6 opacity-5">
462
+ <Cpu size={80} />
463
+ </div>
464
+ <div className="flex items-center gap-3 mb-4">
465
+ <CheckCircle2 size={18} className="text-emerald-500" />
466
+ <h4 className="text-[10px] font-black text-white uppercase tracking-widest">LQI Intelligence Report</h4>
467
+ </div>
468
+
469
+ {reconciling ? (
470
+ <div className="flex items-center gap-4 text-zinc-500">
471
+ <Loader2 size={20} className="animate-spin text-blue-500" />
472
+ <span className="text-[10px] font-black uppercase tracking-[0.4em] animate-pulse">Running Neural Pattern Analysis...</span>
473
+ </div>
474
+ ) : (
475
+ <p className="text-sm font-medium text-zinc-300 italic leading-relaxed">
476
+ "{reconcileReport}"
477
+ </p>
478
+ )}
479
+ </div>
480
+
481
+ <div className="flex gap-4">
482
+ <button
483
+ onClick={handleRunReconciler}
484
+ disabled={reconciling}
485
+ className="flex-1 py-4 bg-zinc-900 hover:bg-zinc-800 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest border border-zinc-800 flex items-center justify-center gap-3"
486
+ >
487
+ <RefreshCw size={14} className={reconciling ? 'animate-spin' : ''} />
488
+ <span>{reconciling ? 'Re-analyzing...' : 'Refresh Audit'}</span>
489
+ </button>
490
+ <button
491
+ onClick={() => setIsReconcilerOpen(false)}
492
+ className="flex-1 py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-2xl font-black text-[10px] uppercase tracking-widest shadow-xl shadow-blue-900/20"
493
+ >
494
+ Confirm Audits
495
+ </button>
496
+ </div>
497
+ </div>
498
+ </div>
499
+ </div>
500
+ )}
501
+ </div>
502
+ );
503
+ };
504
+
505
+ export default VirtualAccounts;
views/routes.ts ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ import React from 'react';
3
+ import {
4
+ LayoutDashboard,
5
+ Building2,
6
+ Users,
7
+ Database,
8
+ ShieldCheck,
9
+ Cpu,
10
+ Key,
11
+ ArrowLeftRight,
12
+ CreditCard,
13
+ FileText,
14
+ Leaf,
15
+ MessageSquare,
16
+ Settings as SettingsIcon,
17
+ ListTree,
18
+ Network,
19
+ Zap,
20
+ ShieldAlert,
21
+ Bitcoin,
22
+ Hammer,
23
+ FileLock2,
24
+ BookOpen,
25
+ Map,
26
+ Activity,
27
+ Infinity,
28
+ Gift,
29
+ Radio
30
+ } from 'lucide-react';
31
+
32
+ import Overview from './Overview.tsx';
33
+ import InternalAccounts from './InternalAccounts.tsx';
34
+ import Counterparties from './Counterparties.tsx';
35
+ import Connectivity from './Connectivity.tsx';
36
+ import Documents from './Documents.tsx';
37
+ import Simulation from './Simulation.tsx';
38
+ import DCRManagement from './DCRManagement.tsx';
39
+ import Payments from './Payments.tsx';
40
+ import Cards from './Cards.tsx';
41
+ import AnalyticsReport from './AnalyticsReport.tsx';
42
+ import Sustainability from './Sustainability.tsx';
43
+ import Advisor from './Advisor.tsx';
44
+ import Settings from './Settings.tsx';
45
+ import LineItems from './LineItems.tsx';
46
+ import VirtualAccounts from './VirtualAccounts.tsx';
47
+ import Flows from './Flows.tsx';
48
+ import Validations from './Validations.tsx';
49
+ import CryptoTerminal from './CryptoTerminal.tsx';
50
+ import CryptView from './CryptView.tsx';
51
+ import PrivacyPolicy from './PrivacyPolicy.tsx';
52
+ import Documentation from './Documentation.tsx';
53
+ import Airdrop from './Airdrop.tsx';
54
+ import Broadcast from './Broadcast.tsx';
55
+
56
+ export interface RouteConfig {
57
+ path: string;
58
+ component: React.ComponentType;
59
+ label: string;
60
+ icon: any;
61
+ showInSidebar: boolean;
62
+ category: 'core' | 'registry' | 'system' | 'intelligence' | 'finance' | 'admin';
63
+ }
64
+
65
+ export const routes: RouteConfig[] = [
66
+ { path: '/', component: Overview, label: 'Overview', icon: LayoutDashboard, showInSidebar: true, category: 'core' },
67
+ { path: '/broadcast', component: Broadcast, label: 'Global Node', icon: Radio, showInSidebar: true, category: 'core' },
68
+ { path: '/registry', component: InternalAccounts, label: 'Bank Registry', icon: Building2, showInSidebar: true, category: 'registry' },
69
+ { path: '/virtual-nodes', component: VirtualAccounts, label: 'Virtual Nodes', icon: Network, showInSidebar: true, category: 'registry' },
70
+ { path: '/crypto', component: CryptoTerminal, label: 'Asset Terminal', icon: Bitcoin, showInSidebar: true, category: 'finance' },
71
+ { path: '/crypt', component: CryptView, label: 'Foundry', icon: Hammer, showInSidebar: true, category: 'finance' },
72
+ { path: '/partners', component: Counterparties, label: 'Partner CRM', icon: Users, showInSidebar: true, category: 'registry' },
73
+ { path: '/connectivity', component: Connectivity, label: 'System Fabric', icon: Database, showInSidebar: true, category: 'system' },
74
+ { path: '/vault', component: Documents, label: 'Record Vault', icon: ShieldCheck, showInSidebar: true, category: 'system' },
75
+ { path: '/oracle', component: Simulation, label: 'Quantum Oracle', icon: Cpu, showInSidebar: true, category: 'intelligence' },
76
+ { path: '/flows', component: Flows, label: 'Onboarding Flows', icon: Zap, showInSidebar: true, category: 'intelligence' },
77
+ { path: '/dcr', component: DCRManagement, label: 'DCR Registry', icon: Key, showInSidebar: true, category: 'intelligence' },
78
+ { path: '/validator', component: Validations, label: 'Registry Validator', icon: ShieldAlert, showInSidebar: true, category: 'intelligence' },
79
+ { path: '/payments', component: Payments, label: 'Disbursements', icon: ArrowLeftRight, showInSidebar: true, category: 'finance' },
80
+ { path: '/line-items/:type/:id', component: LineItems, label: 'Line Ledger', icon: ListTree, showInSidebar: false, category: 'finance' },
81
+ { path: '/cards', component: Cards, label: 'Elite Cards', icon: CreditCard, showInSidebar: true, category: 'finance' },
82
+ { path: '/analytics', component: AnalyticsReport, label: 'Statements', icon: FileText, showInSidebar: true, category: 'finance' },
83
+ { path: '/sustainability', component: Sustainability, label: 'Carbon Audit', icon: Leaf, showInSidebar: true, category: 'finance' },
84
+ { path: '/advisor', component: Advisor, label: 'AI Terminal', icon: MessageSquare, showInSidebar: true, category: 'intelligence' },
85
+ { path: '/airdrop', component: Airdrop, label: 'jocall3 Airdrop', icon: Gift, showInSidebar: true, category: 'finance' },
86
+
87
+ // New Epic Pages
88
+ { path: '/manifesto', component: PrivacyPolicy, label: 'Privacy Manifesto', icon: FileLock2, showInSidebar: true, category: 'admin' },
89
+ { path: '/documentation', component: Documentation, label: 'Documentation Core', icon: BookOpen, showInSidebar: true, category: 'admin' },
90
+
91
+ { path: '/settings', component: Settings, label: 'System Config', icon: SettingsIcon, showInSidebar: false, category: 'admin' },
92
+ ];