| import React, { useState, useRef } from 'react'; |
| import { |
| MessageSquare, |
| ShoppingCart, |
| IndianRupee, |
| Cpu, |
| Database, |
| FileSpreadsheet, |
| Bell, |
| ArrowRight, |
| CheckCircle2, |
| Clock, |
| ExternalLink, |
| ShieldCheck, |
| Search, |
| Zap, |
| Layers, |
| Activity, |
| Server, |
| Terminal |
| } from 'lucide-react'; |
| import { apiClient } from './api'; |
|
|
| interface Entity { |
| item?: string; |
| qty?: string; |
| customer?: string; |
| amount?: string; |
| } |
|
|
| interface AppEvent { |
| id?: string; |
| source: string; |
| icon: React.ReactNode; |
| title: string; |
| message: string; |
| type: string; |
| entity: Entity; |
| timestamp?: string; |
| } |
|
|
| interface SheetRow { |
| id?: string; |
| [key: string]: string | undefined; |
| } |
|
|
| interface SheetsState { |
| [key: string]: SheetRow[]; |
| } |
|
|
| const VyaparFlow = () => { |
| |
| const [notifications, setNotifications] = useState<AppEvent[]>([]); |
| const [activeEvent, setActiveEvent] = useState<AppEvent | null>(null); |
| const [backendResult, setBackendResult] = useState<any>(null); |
| const [processingStep, setProcessingStep] = useState(0); |
| const [pipelineError, setPipelineError] = useState<string | null>(null); |
| const [view, setView] = useState('dashboard'); |
| const [activeSheet, setActiveSheet] = useState('Orders'); |
| |
| |
| const isProcessingRef = useRef(false); |
|
|
| |
| const [sheets, setSheets] = useState<SheetsState>({ |
| Orders: [ |
| { id: 'ORD-882', source: 'Amazon', customer: 'Suresh K.', item: 'Kurti (XL)', qty: '2', status: 'Delivered' }, |
| { id: 'ORD-881', source: 'WhatsApp', customer: 'Anjali', item: 'Aata', qty: '5kg', status: 'Paid' }, |
| ], |
| Ledger: [ |
| { customer: 'Rahul Sharma', type: 'Credit', amount: '₹12,400', date: '14 Mar', status: 'Settled' }, |
| { customer: 'Mohit', type: 'Debit', amount: '₹1,200', date: '15 Mar', status: 'Pending' }, |
| ], |
| Inventory: [ |
| { item: 'Aata (Premium)', stock: '142kg', alert: 'Normal' }, |
| { item: 'Silk Kurti', stock: '8', alert: 'Low' }, |
| ] |
| }); |
|
|
| |
| |
| const products = ["Silk Kurti", "Cotton Saree", "Jeans", "T-shirt", "Leather Jacket"]; |
| const groceryItems = ["aata", "chawal", "dal", "cheeni", "tel"]; |
| const customers = ["Rahul", "Amit Kumar", "Suresh", "Pooja", "Neha"]; |
| const sources = ["whatsapp", "gpay", "amazon seller"]; |
|
|
| const generateNotification = () => { |
| const source = sources[Math.floor(Math.random() * sources.length)]; |
|
|
| if (source === "whatsapp") { |
| const qty = Math.floor(Math.random() * 5) + 1; |
| const item = groceryItems[Math.floor(Math.random() * groceryItems.length)]; |
| return { |
| source: "whatsapp", |
| message: `bhaiya ${qty} kilo ${item} bhej dena kal tak` |
| }; |
| } |
|
|
| if (source === "gpay") { |
| const amount = Math.floor(Math.random() * 15000) + 500; |
| const customer = customers[Math.floor(Math.random() * customers.length)]; |
| return { |
| source: "gpay", |
| message: `₹${amount} received from ${customer}` |
| }; |
| } |
|
|
| const product = products[Math.floor(Math.random() * products.length)]; |
| const qty = Math.floor(Math.random() * 3) + 1; |
| const total = Math.floor(Math.random() * 3000) + 500; |
|
|
| return { |
| source: "amazon seller", |
| message: `Product: ${product}. Qty: ${qty}. Total: ₹${total}` |
| }; |
| }; |
|
|
| const handleGenerateNotification = async () => { |
| |
| if (isProcessingRef.current) return; |
| isProcessingRef.current = true; |
|
|
| try { |
| |
| const notification = generateNotification(); |
| |
| |
| const sourceMap: { [key: string]: { icon: React.ReactNode; title: string } } = { |
| 'whatsapp': { |
| icon: <MessageSquare size={14} className="text-emerald-400" />, |
| title: 'WhatsApp Order' |
| }, |
| 'gpay': { |
| icon: <IndianRupee size={14} className="text-blue-400" />, |
| title: 'GPay Payment' |
| }, |
| 'amazon seller': { |
| icon: <ShoppingCart size={14} className="text-orange-400" />, |
| title: 'Amazon Order' |
| }, |
| }; |
| |
| const sourceProps = sourceMap[notification.source] || { |
| icon: <MessageSquare size={14} className="text-slate-400" />, |
| title: 'New Notification' |
| }; |
| |
| |
| const uniqueId = `event-${Date.now()}-${Math.floor(Math.random() * 1000)}`; |
| const newEvent: AppEvent = { |
| id: uniqueId, |
| source: notification.source.charAt(0).toUpperCase() + notification.source.slice(1), |
| icon: sourceProps.icon, |
| title: sourceProps.title, |
| message: notification.message, |
| type: notification.source === 'gpay' ? 'payment' : 'order', |
| entity: {}, |
| timestamp: new Date().toLocaleTimeString() |
| }; |
| |
| |
| setNotifications(prev => [newEvent, ...prev].slice(0, 5)); |
| setActiveEvent(newEvent); |
| setBackendResult(null); |
| setProcessingStep(1); |
|
|
| |
| const response = await apiClient.sendNotification({ |
| source: notification.source.toLowerCase(), |
| message: notification.message |
| }); |
|
|
| |
| setBackendResult(response); |
| setProcessingStep(2); |
| |
| |
| await new Promise(r => setTimeout(r, 1000)); |
| setProcessingStep(3); |
|
|
| |
| setSheets(prev => { |
| const newSheets = { ...prev }; |
| const rowId = `RECORD-${Date.now()}`; |
| |
| |
| const entityData = response.data || {}; |
| |
| if (response.intent === 'payment') { |
| newSheets.Ledger = [{ |
| id: rowId, |
| customer: entityData.customer || newEvent.entity.customer || 'Unknown', |
| type: 'Credit', |
| amount: entityData.amount || newEvent.entity.amount || '₹0', |
| date: 'Today', |
| status: 'Settled' |
| }, ...prev.Ledger].slice(0, 10); |
| } else { |
| newSheets.Orders = [{ |
| id: `ORD-${Math.floor(Math.random()*900 + 100)}`, |
| source: newEvent.source, |
| customer: entityData.customer || newEvent.entity.customer || 'Unknown', |
| item: entityData.item || newEvent.entity.item || 'Item', |
| qty: entityData.quantity || newEvent.entity.qty || '1', |
| status: 'Confirmed' |
| }, ...prev.Orders].slice(0, 10); |
| } |
| return newSheets; |
| }); |
|
|
| } catch (error: any) { |
| console.error('Backend error:', error); |
| const msg = error?.message || 'Both models unavailable'; |
| setPipelineError( |
| msg.includes('quota') || msg.includes('429') |
| ? 'Quota exceeded — wait ~1 min' |
| : msg.includes('unavailable') || msg.includes('500') |
| ? 'Models unavailable — retry shortly' |
| : 'Request failed' |
| ); |
| setProcessingStep(4); |
| } finally { |
| |
| setTimeout(() => { |
| setProcessingStep(0); |
| setPipelineError(null); |
| isProcessingRef.current = false; |
| }, 5000); |
| } |
| }; |
|
|
| return ( |
| <div className="flex h-screen bg-[#050505] text-slate-300 font-sans selection:bg-indigo-500/30 overflow-hidden"> |
| |
| {/* SIDEBAR */} |
| <div className="w-20 border-r border-white/5 flex flex-col items-center py-8 gap-8 bg-[#080808]"> |
| <div className="w-10 h-10 bg-gradient-to-br from-indigo-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg shadow-indigo-500/20"> |
| <Zap size={20} className="text-white" fill="white" /> |
| </div> |
| <div className="flex flex-col gap-6 mt-12"> |
| <button |
| onClick={() => setView('dashboard')} |
| className={`p-3 rounded-xl transition-all ${view === 'dashboard' ? 'bg-indigo-500/10 text-indigo-400 border border-indigo-500/20' : 'text-slate-600 hover:text-slate-400'}`} |
| > |
| <Activity size={22} /> |
| </button> |
| <button |
| onClick={() => setView('sheets')} |
| className={`p-3 rounded-xl transition-all ${view === 'sheets' ? 'bg-indigo-500/10 text-indigo-400 border border-indigo-500/20' : 'text-slate-600 hover:text-slate-400'}`} |
| > |
| <FileSpreadsheet size={22} /> |
| </button> |
| <div className="p-3 text-slate-600 hover:text-slate-400 cursor-pointer"><Layers size={22} /></div> |
| <div className="p-3 text-slate-600 hover:text-slate-400 cursor-pointer"><Database size={22} /></div> |
| </div> |
| </div> |
| |
| {/* MOBILE SIMULATION PANEL */} |
| <div className="w-[420px] bg-[#080808] flex flex-col items-center justify-center p-8 relative border-r border-white/5"> |
| <div className="absolute top-8 left-8 flex items-center gap-3"> |
| <h1 className="text-lg font-bold tracking-tight text-white uppercase">NotiFlow</h1> |
| <div className="h-4 w-[1px] bg-white/10"></div> |
| <span className="text-[10px] text-indigo-400 font-black tracking-widest uppercase">Live Agent</span> |
| </div> |
| |
| {/* Device Frame */} |
| <div className="relative w-72 h-[580px] bg-[#000] rounded-[3.5rem] p-3 border-[10px] border-[#1a1a1a] shadow-[0_0_50px_-12px_rgba(79,70,229,0.3)]"> |
| <div className="absolute top-6 left-1/2 -translate-x-1/2 w-24 h-6 bg-[#1a1a1a] rounded-full z-20 flex items-center justify-center gap-1.5"> |
| <div className="w-2 h-2 rounded-full bg-[#0a0a0a]"></div> |
| <div className="w-8 h-1 rounded-full bg-[#0a0a0a]"></div> |
| </div> |
| |
| <div className="h-full w-full bg-[#0d0d0d] rounded-[2.5rem] overflow-hidden flex flex-col relative"> |
| <div className="h-14 pt-8 px-6 flex justify-between items-center text-[11px] text-white/40 font-medium"> |
| <span>9:41</span> |
| <div className="flex gap-1.5 items-center"> |
| <Activity size={12} className="text-indigo-500" /> |
| <div className="w-4 h-2 bg-white/20 rounded-sm"></div> |
| </div> |
| </div> |
| |
| <div className="flex-1 px-3 py-4 space-y-3 overflow-y-auto custom-scrollbar"> |
| <div className="flex items-center justify-between px-2 mb-2"> |
| <span className="text-[10px] text-slate-500 font-bold tracking-tighter uppercase">Signal Stream</span> |
| <div className="flex gap-1"> |
| <div className="w-1.5 h-1.5 bg-indigo-500 rounded-full animate-pulse"></div> |
| <div className="w-1.5 h-1.5 bg-indigo-500/50 rounded-full animate-pulse delay-75"></div> |
| </div> |
| </div> |
| |
| {notifications.length === 0 && ( |
| <div className="h-32 flex flex-col items-center justify-center text-center px-4"> |
| <Clock size={20} className="text-slate-800 mb-2" /> |
| <p className="text-[10px] text-slate-700 italic">Listening for business events...</p> |
| </div> |
| )} |
| |
| {notifications.map((notif) => ( |
| <div |
| key={notif.id} |
| className={`p-3 rounded-2xl border transition-all duration-700 animate-in slide-in-from-top-4 ${ |
| activeEvent?.id === notif.id |
| ? 'bg-indigo-600/10 border-indigo-500/50 scale-[1.02] shadow-xl' |
| : 'bg-white/[0.03] border-white/5 opacity-40' |
| }`} |
| > |
| <div className="flex items-center justify-between mb-1.5"> |
| <div className="flex items-center gap-2"> |
| <div className="w-5 h-5 rounded-md bg-black flex items-center justify-center border border-white/10"> |
| {notif.icon} |
| </div> |
| <span className="text-[10px] font-bold text-white/80">{notif.source}</span> |
| </div> |
| <span className="text-[9px] text-white/30 uppercase tracking-tighter">Live</span> |
| </div> |
| <div className="text-[11px] font-bold text-white mb-0.5">{notif.title}</div> |
| <p className="text-[10px] text-slate-400 leading-snug line-clamp-2">{notif.message}</p> |
| |
| {activeEvent?.id === notif.id && ( |
| <div className="mt-2 pt-2 border-t border-white/5 flex items-center justify-between"> |
| <div className="flex items-center gap-1.5"> |
| <div className="w-1 h-1 bg-indigo-500 rounded-full animate-ping"></div> |
| <span className="text-[9px] text-indigo-400 font-black uppercase tracking-widest">Ingesting...</span> |
| </div> |
| <ArrowRight size={10} className="text-indigo-400 animate-bounce" /> |
| </div> |
| )} |
| </div> |
| ))} |
| </div> |
| |
| <div className="h-10 flex items-center justify-center"> |
| <div className="w-20 h-1 bg-white/10 rounded-full"></div> |
| </div> |
| </div> |
| </div> |
| |
| <div className="mt-8 flex flex-col items-center gap-2"> |
| <div className="flex items-center gap-2 text-[10px] font-bold text-slate-600 uppercase tracking-widest"> |
| <Server size={14} className="text-indigo-500" /> Connection: Stable |
| </div> |
| </div> |
| </div> |
| |
| {/* MAIN CONTENT AREA */} |
| <div className="flex-1 flex flex-col bg-[#050505]"> |
| <header className="h-20 border-b border-white/5 px-10 flex items-center justify-between bg-[#080808]/50 backdrop-blur-xl sticky top-0 z-30"> |
| <nav className="flex gap-1 bg-white/[0.02] p-1 rounded-xl border border-white/5"> |
| <button |
| onClick={() => setView('dashboard')} |
| className={`px-8 py-2 rounded-lg text-xs font-bold transition-all ${view === 'dashboard' ? 'bg-indigo-600 text-white shadow-[0_0_20px_rgba(79,70,229,0.4)]' : 'text-slate-500 hover:text-slate-300'}`} |
| > |
| Reasoning Engine |
| </button> |
| <button |
| onClick={() => setView('sheets')} |
| className={`px-8 py-2 rounded-lg text-xs font-bold transition-all ${view === 'sheets' ? 'bg-indigo-600 text-white shadow-[0_0_20px_rgba(79,70,229,0.4)]' : 'text-slate-500 hover:text-slate-300'}`} |
| > |
| Business Ledger |
| </button> |
| </nav> |
| |
| <div className="flex items-center gap-6"> |
| <button |
| onClick={handleGenerateNotification} |
| disabled={processingStep > 0} |
| className={`px-4 py-2 rounded-lg text-xs font-bold border transition-all ${ |
| processingStep === 4 |
| ? 'bg-red-900/30 text-red-400 border-red-500/40 cursor-not-allowed' |
| : processingStep > 0 |
| ? 'bg-slate-800/50 text-slate-500 border-slate-700/50 cursor-not-allowed' |
| : 'bg-emerald-600/20 text-emerald-400 border-emerald-500/30 hover:bg-emerald-600/30 hover:border-emerald-500/50' |
| }`} |
| title={pipelineError || ''} |
| > |
| {processingStep === 4 |
| ? `❌ ${pipelineError || 'Failed'}` |
| : processingStep > 0 |
| ? '⏳ Processing...' |
| : '⚡ Generate Notification'} |
| </button> |
| |
| <div className="flex flex-col items-end"> |
| <div className="flex items-center gap-2 text-[10px] font-black text-indigo-400 uppercase tracking-widest"> |
| Nova-2-Lite <div className="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse"></div> |
| </div> |
| <span className="text-[9px] text-slate-700 font-mono tracking-widest">V-ENGINE-BETA</span> |
| </div> |
| </div> |
| </header> |
| |
| <main className="flex-1 overflow-y-auto p-10"> |
| {view === 'dashboard' ? ( |
| <div className="max-w-5xl mx-auto space-y-10"> |
| |
| <section className="space-y-6"> |
| <div className="flex items-center justify-between"> |
| <h2 className="text-xl font-bold text-white tracking-tight flex items-center gap-3"> |
| <Activity size={20} className="text-indigo-500" /> |
| AI Processing Pipeline |
| </h2> |
| <div className="px-4 py-1.5 rounded-full border border-white/10 bg-white/[0.03] text-[9px] font-black text-slate-500 tracking-[0.2em] uppercase"> |
| Realtime Trace |
| </div> |
| </div> |
| |
| {!activeEvent ? ( |
| <div className="h-[420px] border border-dashed border-white/5 rounded-[2.5rem] flex flex-col items-center justify-center text-center p-12 bg-white/[0.01]"> |
| <div className="w-20 h-20 rounded-3xl bg-white/[0.03] flex items-center justify-center text-slate-800 mb-8 border border-white/5"> |
| <Bell size={32} /> |
| </div> |
| <h3 className="text-white/60 font-bold text-lg tracking-tight">System Polling Underway</h3> |
| <p className="text-slate-600 text-sm mt-3 max-w-xs leading-relaxed"> NotiFlow is continuously monitoring marketplace APIs and messaging webhooks.</p> |
| </div> |
| ) : ( |
| <div className="grid grid-cols-1 lg:grid-cols-3 gap-6 animate-in fade-in slide-in-from-bottom-6 duration-700"> |
| |
| {/* Step 1 */} |
| <div className={`relative p-8 rounded-[2rem] border transition-all duration-700 ${processingStep >= 1 ? 'bg-white/[0.03] border-indigo-500/30 shadow-2xl' : 'bg-white/[0.01] border-white/5 opacity-40'}`}> |
| <div className="flex items-center gap-3 mb-8"> |
| <div className={`w-8 h-8 rounded-full flex items-center justify-center text-[10px] font-black ${processingStep >= 1 ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-500'}`}>01</div> |
| <span className="text-[10px] font-black text-white tracking-[0.2em] uppercase">POST /Ingest</span> |
| </div> |
| <div className="space-y-5"> |
| <div className="p-4 bg-black/60 rounded-2xl border border-white/5 font-mono text-[11px] leading-relaxed"> |
| <div className="text-indigo-400 mb-2 font-bold uppercase tracking-wider">Payload Received:</div> |
| <div className="text-slate-300">"{activeEvent.message}"</div> |
| </div> |
| <div className="flex items-center justify-between text-[10px] text-slate-500 font-bold uppercase px-1"> |
| <span>Validation Check</span> |
| {processingStep >= 1 && <CheckCircle2 size={12} className="text-green-500" />} |
| </div> |
| </div> |
| {processingStep === 1 && <div className="absolute -right-3 top-1/2 -translate-y-1/2 z-10 text-indigo-500 animate-pulse"><ArrowRight size={24} /></div>} |
| </div> |
| |
| {/* Step 2 */} |
| <div className={`relative p-8 rounded-[2rem] border transition-all duration-700 ${processingStep >= 2 ? 'bg-white/[0.03] border-indigo-500/30 shadow-2xl' : 'bg-white/[0.01] border-white/5 opacity-40'}`}> |
| <div className="flex items-center gap-3 mb-8"> |
| <div className={`w-8 h-8 rounded-full flex items-center justify-center text-[10px] font-black ${processingStep >= 2 ? 'bg-indigo-600 text-white' : 'bg-slate-800 text-slate-500'}`}>02</div> |
| <span className="text-[10px] font-black text-white tracking-[0.2em] uppercase">AI Reasoning</span> |
| </div> |
| |
| <div className="space-y-4 min-h-[140px] relative"> |
| {processingStep === 2 && ( |
| <div className="absolute inset-0 flex flex-col items-center justify-center bg-black/40 backdrop-blur-sm rounded-2xl z-10"> |
| <div className="w-10 h-10 border-2 border-indigo-500 border-t-transparent rounded-full animate-spin"></div> |
| <span className="text-[10px] mt-4 font-black text-indigo-400 uppercase tracking-[0.3em]">Processing...</span> |
| </div> |
| )} |
| <div className="bg-black/60 p-5 rounded-2xl border border-white/5 space-y-4"> |
| <div> |
| <div className="text-[9px] text-slate-500 uppercase font-black mb-1.5 tracking-wider">Detected Intent</div> |
| <div className="text-xs text-indigo-400 font-bold capitalize">{(backendResult?.intent || activeEvent?.type || 'unknown').replace('_', ' ')} Pattern Matched</div> |
| </div> |
| <div className="h-[1px] bg-white/5"></div> |
| <div> |
| <div className="text-[9px] text-slate-500 uppercase font-black mb-2 tracking-wider">Extracted Entities</div> |
| <div className="flex flex-wrap gap-2"> |
| {Object.entries(backendResult?.data || activeEvent?.entity || {}).map(([k, v]) => ( |
| <span key={`${activeEvent?.id}-entity-${k}`} className="text-[10px] px-2.5 py-1 rounded bg-indigo-500/10 text-indigo-300 border border-indigo-500/20 font-bold">{k}: {String(v)}</span> |
| ))} |
| </div> |
| </div> |
| </div> |
| </div> |
| {processingStep === 2 && <div className="absolute -right-3 top-1/2 -translate-y-1/2 z-10 text-indigo-500 animate-pulse"><ArrowRight size={24} /></div>} |
| </div> |
| |
| {/* Step 3 */} |
| <div className={`relative p-8 rounded-[2rem] border transition-all duration-700 ${processingStep >= 3 ? 'bg-indigo-600/5 border-indigo-500/40 shadow-2xl' : 'bg-white/[0.01] border-white/5 opacity-40'}`}> |
| <div className="flex items-center gap-3 mb-8"> |
| <div className={`w-8 h-8 rounded-full flex items-center justify-center text-[10px] font-black ${processingStep >= 3 ? 'bg-green-500 text-black' : 'bg-slate-800 text-slate-500'}`}>03</div> |
| <span className="text-[10px] font-black text-white tracking-[0.2em] uppercase">Skills Applied</span> |
| </div> |
| <div className="space-y-6"> |
| <div className="flex items-center gap-4"> |
| <div className="w-12 h-12 rounded-2xl bg-green-500/10 flex items-center justify-center text-green-500 border border-green-500/20 shadow-[0_0_20px_rgba(34,197,94,0.15)]"> |
| <CheckCircle2 size={24} /> |
| </div> |
| <div> |
| <div className="text-[11px] font-bold text-white uppercase tracking-wider">Record Finalized</div> |
| <div className="text-[10px] text-slate-500 mt-0.5">DB Synchronization: Success</div> |
| </div> |
| </div> |
| <button |
| onClick={() => setView('sheets')} |
| className="w-full py-3.5 rounded-2xl bg-white/[0.05] border border-white/5 text-[10px] font-black uppercase tracking-[0.25em] text-white hover:bg-white/10 transition-all flex items-center justify-center gap-2" |
| > |
| View Updates <ExternalLink size={12} /> |
| </button> |
| </div> |
| </div> |
| </div> |
| )} |
| </section> |
| |
| <section className="grid grid-cols-1 md:grid-cols-3 gap-6"> |
| <div className="p-8 rounded-[2rem] bg-white/[0.02] border border-white/5 flex flex-col gap-1 hover:bg-white/[0.03] transition-colors"> |
| <span className="text-[10px] font-black text-slate-600 uppercase tracking-widest">Global Success Rate</span> |
| <span className="text-3xl font-bold text-white tracking-tighter">99.2%</span> |
| <div className="w-full h-1 bg-white/5 rounded-full mt-6 overflow-hidden"> |
| <div className="w-[99%] h-full bg-gradient-to-r from-indigo-500 to-emerald-500"></div> |
| </div> |
| </div> |
| <div className="p-8 rounded-[2rem] bg-white/[0.02] border border-white/5 flex flex-col gap-1 hover:bg-white/[0.03] transition-colors"> |
| <span className="text-[10px] font-black text-slate-600 uppercase tracking-widest">Active Connectors</span> |
| <span className="text-3xl font-bold text-white tracking-tighter">14</span> |
| <div className="flex gap-2 mt-6"> |
| <div className="w-2 h-2 rounded-full bg-green-500 animate-pulse"></div> |
| <div className="w-2 h-2 rounded-full bg-orange-500 animate-pulse delay-75"></div> |
| <div className="w-2 h-2 rounded-full bg-blue-500 animate-pulse delay-150"></div> |
| <div className="w-2 h-2 rounded-full bg-purple-500 animate-pulse delay-200"></div> |
| </div> |
| </div> |
| <div className="p-8 rounded-[2rem] bg-[#0c0c12] border border-indigo-500/20 flex flex-col gap-1 shadow-inner shadow-indigo-500/5"> |
| <span className="text-[10px] font-black text-indigo-400 uppercase tracking-widest">Model Performance</span> |
| <span className="text-3xl font-bold text-white tracking-tighter">Ultra-Low</span> |
| <span className="text-[10px] text-indigo-400/50 mt-6 font-mono font-bold tracking-widest uppercase">Nova-2-L-optimized</span> |
| </div> |
| </section> |
| |
| </div> |
| ) : ( |
| <div className="max-w-6xl mx-auto space-y-10 animate-in fade-in slide-in-from-bottom-6 duration-700"> |
| <div className="flex items-end justify-between"> |
| <div className="space-y-1"> |
| <h2 className="text-2xl font-bold text-white tracking-tight">Business Operations Console</h2> |
| <p className="text-slate-500 text-sm">Real-time structured business data extracted from the unified signal stream.</p> |
| </div> |
| <div className="flex bg-white/[0.03] p-1.5 rounded-2xl border border-white/5 shadow-2xl"> |
| {['Orders', 'Ledger', 'Inventory'].map((s) => ( |
| <button |
| key={`sheet-tab-${s}`} |
| onClick={() => setActiveSheet(s)} |
| className={`px-8 py-2.5 rounded-xl text-xs font-black tracking-widest uppercase transition-all ${activeSheet === s ? 'bg-indigo-600 text-white shadow-lg' : 'text-slate-500 hover:text-white hover:bg-white/5'}`} |
| > |
| {s} |
| </button> |
| ))} |
| </div> |
| </div> |
| |
| <div className="bg-[#080808] rounded-[2.5rem] border border-white/5 overflow-hidden shadow-[0_32px_64px_-16px_rgba(0,0,0,0.8)] relative"> |
| <div className="overflow-x-auto"> |
| <table className="w-full text-left text-sm border-collapse"> |
| <thead> |
| <tr className="bg-white/[0.03] border-b border-white/5"> |
| {sheets[activeSheet] && sheets[activeSheet].length > 0 && Object.keys(sheets[activeSheet][0]).filter(k => k !== 'id').map(key => ( |
| <th key={`header-${activeSheet}-${key}`} className="px-10 py-6 font-black text-slate-500 uppercase text-[10px] tracking-[0.25em]">{key}</th> |
| ))} |
| </tr> |
| </thead> |
| <tbody> |
| {sheets[activeSheet].map((row, i) => ( |
| <tr key={`row-${activeSheet}-${row.id || i}`} className={`border-b border-white/[0.02] hover:bg-white/[0.03] transition-colors group ${i === 0 ? 'bg-indigo-600/5' : ''}`}> |
| {Object.entries(row).filter(([k]) => k !== 'id').map(([key, val = ''], j) => ( |
| <td key={`cell-${activeSheet}-${i}-${j}`} className="px-10 py-6"> |
| <span className={` |
| ${val === 'Pending' || val === 'Low' ? 'text-orange-400 font-bold' : ''} |
| ${val === 'Delivered' || val === 'Paid' || val === 'Settled' || val === 'Confirmed' ? 'text-emerald-400 font-bold' : ''} |
| ${typeof val === 'string' && val.includes('₹') ? 'text-indigo-300 font-mono font-bold' : 'text-slate-300'} |
| `}> |
| {val} |
| </span> |
| {i === 0 && j === 0 && <span className="ml-3 px-2 py-0.5 rounded bg-indigo-500 text-[8px] font-black text-white uppercase tracking-tighter animate-pulse shadow-sm">Recent</span>} |
| </td> |
| ))} |
| </tr> |
| ))} |
| </tbody> |
| </table> |
| </div> |
| </div> |
| |
| <div className="flex items-center justify-between p-8 rounded-[2rem] bg-white/[0.02] border border-white/5"> |
| <div className="flex items-center gap-4"> |
| <Terminal size={18} className="text-indigo-500" /> |
| <span className="text-[11px] text-slate-500 font-mono font-bold tracking-tighter">Auto-sync with VyaparFlow Cloud v1.0.4 - Connection Secure</span> |
| </div> |
| <div className="flex gap-4"> |
| <button className="px-6 py-2.5 rounded-xl bg-white/5 hover:bg-white/10 text-xs font-bold text-white border border-white/10 transition-all">Download .CSV</button> |
| <button className="px-6 py-2.5 rounded-xl bg-indigo-600 hover:bg-indigo-500 text-xs font-bold text-white transition-all shadow-lg shadow-indigo-600/20">Push to Tally / GST</button> |
| </div> |
| </div> |
| </div> |
| )} |
| </main> |
| |
| <footer className="h-12 border-t border-white/5 bg-[#080808] px-10 flex items-center justify-between text-[10px] font-black uppercase tracking-[0.3em]"> |
| <div className="flex gap-10 text-slate-600"> |
| <div className="flex items-center gap-2.5"><div className="w-1.5 h-1.5 bg-green-500 rounded-full shadow-[0_0_8px_rgba(34,197,94,0.5)]"></div> Network: Online</div> |
| <span className="flex items-center gap-2.5"><Cpu size={12} className="text-indigo-400" /> Model: Nova-2-Lite</span> |
| <span className="flex items-center gap-2.5"><Database size={12} className="text-indigo-400" /> Storage: Cloud Active</span> |
| </div> |
| <div className="flex gap-6 text-slate-700"> |
| <span>SES-ID: 771-VY-99</span> |
| <span>BUILD: 2024.MAR.PRO</span> |
| </div> |
| </footer> |
| </div> |
| </div> |
| ); |
| }; |
|
|
| export default VyaparFlow; |