import { useState, useEffect } from “react”; const GlobalStyles = () => ( <style>{` @import url('https://fonts.googleapis.com/css2?family=Rajdhani:wght@400;500;600;700&family=Share+Tech+Mono&family=Exo+2:wght@300;400;600;700;900&display=swap'); *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } :root { --bg: #0f1923; --bg-panel: #151f2e; --bg-card: #1a2738; --bg-card2: #1e2d40; --bg-input: #213044; --border: rgba(100,180,255,0.15); --accent: #38b6ff; --accent2: #00e5a0; --danger: #ff4f4f; --warn: #ffcc00; --text-primary: #dceeff; --text-secondary: #7aa8cc; --text-dim: #3d6080; --grid: rgba(56,182,255,0.05); } body { background: var(--bg); color: var(--text-primary); font-family: 'Exo 2', sans-serif; min-height: 100vh; } body::after { content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none; background-image: linear-gradient(var(--grid) 1px, transparent 1px), linear-gradient(90deg, var(--grid) 1px, transparent 1px); background-size: 44px 44px; } ::-webkit-scrollbar { width: 4px; } ::-webkit-scrollbar-track { background: var(--bg); } ::-webkit-scrollbar-thumb { background: var(--accent); border-radius: 2px; } @keyframes blink { 0%,100%{opacity:1} 50%{opacity:0.15} } @keyframes scan-h { 0%{top:0} 100%{top:100%} } @keyframes fade-up { from{transform:translateY(14px);opacity:0} to{transform:translateY(0);opacity:1} } @keyframes wave { 0%,100%{height:4px} 50%{height:18px} } @keyframes spin { from{transform:rotate(0deg)} to{transform:rotate(360deg)} } @keyframes pulse-border { 0%,100%{box-shadow:0 0 0 0 rgba(255,79,79,0.35)} 50%{box-shadow:0 0 0 5px rgba(255,79,79,0)} } .btn { cursor:pointer; transition: all 0.18s; border-radius:4px; } .btn:hover { filter: brightness(1.18); transform: translateY(-1px); } .btn:active { transform: translateY(0); filter: brightness(0.94); } .card { background: var(--bg-card); border: 1px solid var(--border); border-radius: 8px; } `}</style> ); // ── Data ────────────────────────────────────────────────────────────────────── const CAMERAS_DATA = [ { id:“CAM-01”, name:“Main Entrance”, zone:“Zone A”, status:“ALERT”, threat:95, fps:30, recording:true, uptime:“99.1%” }, { id:“CAM-02”, name:“Parking Lot N”, zone:“Zone B”, status:“ACTIVE”, threat:12, fps:30, recording:true, uptime:“100%” }, { id:“CAM-03”, name:“Server Room”, zone:“Zone C”, status:“ACTIVE”, threat:8, fps:25, recording:true, uptime:“100%” }, { id:“CAM-04”, name:“Rooftop East”, zone:“Zone D”, status:“MOTION”, threat:44, fps:30, recording:true, uptime:“98.7%” }, { id:“CAM-05”, name:“Lobby Interior”, zone:“Zone A”, status:“ACTIVE”, threat:5, fps:30, recording:true, uptime:“100%” }, { id:“CAM-06”, name:“Parking Lot S”, zone:“Zone B”, status:“OFFLINE”, threat:0, fps:0, recording:false, uptime:“0%” }, ]; const ALERTS_INIT = [ { id:1, time:“02:14:37”, cam:“CAM-01”, type:“INTRUSION”, severity:“CRITICAL”, msg:“Unknown person detected at Main Entrance — face unrecognised”, whatsapp:true, call:true, ack:false }, { id:2, time:“01:58:12”, cam:“CAM-04”, type:“MOTION”, severity:“MEDIUM”, msg:“Unusual motion on Rooftop East beyond business hours”, whatsapp:true, call:false, ack:false }, { id:3, time:“01:22:05”, cam:“CAM-02”, type:“LOITERING”, severity:“LOW”, msg:“Person loitering > 5 min in Parking Lot N”, whatsapp:true, call:false, ack:true }, { id:4, time:“00:44:50”, cam:“CAM-03”, type:“TAILGATING”, severity:“HIGH”, msg:“Tailgating event detected at Server Room access door”, whatsapp:true, call:true, ack:true }, ]; const CONTACTS_DATA = [ { name:“Ahmed Al-Rashid”, role:“Head of Security”, phone:”+971 50 123 4567”, status:“NOTIFIED”, avatar:“AA” }, { name:“Fatima Hassan”, role:“Security Officer”, phone:”+971 55 987 6543”, status:“NOTIFIED”, avatar:“FH” }, { name:“Control Room”, role:“Operations Centre”, phone:”+971 4 000 0000”, status:“ON CALL”, avatar:“CR” }, ]; const RECORDINGS_DATA = [ { id:“REC-001”, cam:“CAM-01”, date:“Today 02:14”, duration:“3m 42s”, size:“184 MB”, type:“INTRUSION” }, { id:“REC-002”, cam:“CAM-04”, date:“Today 01:58”, duration:“1m 10s”, size:“54 MB”, type:“MOTION” }, { id:“REC-003”, cam:“CAM-03”, date:“Today 00:44”, duration:“2m 05s”, size:“98 MB”, type:“TAILGATING”}, { id:“REC-004”, cam:“CAM-01”, date:“Yesterday 23:11”, duration:“0m 33s”, size:“26 MB”, type:“MOTION” }, { id:“REC-005”, cam:“CAM-02”, date:“Yesterday 20:30”, duration:“5m 00s”, size:“241 MB”, type:“LOITERING” }, ]; const severityColor = s => ({ CRITICAL:”#ff4f4f”, HIGH:”#ff8c00”, MEDIUM:”#ffcc00”, LOW:”#38b6ff” }[s] || “#38b6ff”); const typeColor = t => ({ INTRUSION:”#ff4f4f”, TAILGATING:”#ff8c00”, MOTION:”#ffcc00”, LOITERING:”#38b6ff” }[t] || “#38b6ff”); // ── Shared UI ───────────────────────────────────────────────────────────────── const Badge = ({ label, color, blink }) => ( <span style={{ fontFamily:“Share Tech Mono”, fontSize:9, color, border:`1px solid ${color}`, padding:“2px 7px”, borderRadius:10, animation:blink?“blink 1s infinite”:“none”, whiteSpace:“nowrap” }}>{label}</span> ); const ThreatBar = ({ value }) => { const c = value > 70 ? “#ff4f4f” : value > 40 ? “#ffcc00” : “#00e5a0”; return ( <div style={{ display:“flex”, alignItems:“center”, gap:8 }}> <div style={{ flex:1, height:5, background:“rgba(255,255,255,0.06)”, borderRadius:3, overflow:“hidden” }}> <div style={{ width:`${value}%`, height:“100%”, background:c, borderRadius:3, transition:“width 1s ease”, boxShadow:`0 0 8px ${c}88` }} /> </div> <span style={{ fontFamily:“Share Tech Mono”, fontSize:10, color:c, width:32 }}>{value}%</span> </div> ); }; const Stat = ({ label, value, sub, color=”#38b6ff”, icon }) => ( <div style={{ background:"var(--bg-card)", border:"1px solid var(--border)", borderRadius:8, padding:"13px 15px", borderLeft:`3px solid ${color}` }}> <div style={{ display:"flex", justifyContent:"space-between" }}> <div> <div style={{ fontFamily:"Rajdhani", fontSize:11, color:"var(--text-secondary)", letterSpacing:1, textTransform:"uppercase" }}>{label}</div> <div style={{ fontFamily:"Rajdhani", fontSize:26, fontWeight:700, color, lineHeight:1.2 }}>{value}</div> {sub && <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)", marginTop:2 }}>{sub}</div>} </div> <span style={{ fontSize:18, opacity:0.3, alignSelf:"flex-start" }}>{icon}</span> </div> </div> ); const Waveform = ({ active }) => ( <div style={{ display:"flex", alignItems:"center", gap:2, height:22 }}> {Array.from({length:18}).map((_,i) => ( <div key={i} style={{ width:2.5, borderRadius:2, background:active?"var(--accent)":"var(--text-dim)", height:active?undefined:4, animation:active?`wave ${0.35+Math.random()*0.5}s ease-in-out infinite alternate`:"none", animationDelay:`${i*0.04}s` }} /> ))} </div> ); // ── Camera Card ─────────────────────────────────────────────────────────────── const CameraCard = ({ cam, selected, onClick }) => { const isAlert = cam.status === “ALERT”; const isMotion = cam.status === “MOTION”; const isOffline = cam.status === “OFFLINE”; const sc = isAlert?”#ff4f4f”:isMotion?”#ffcc00”:isOffline?”#3d6080”:”#00e5a0”; return ( <div onClick={()=>onClick(cam)} style={{ background:“var(–bg-card)”, borderRadius:8, overflow:“hidden”, cursor:“pointer”, border:`1px solid ${selected?"var(--accent)":isAlert?"rgba(255,79,79,0.55)":"var(--border)"}`, boxShadow:selected?“0 0 16px rgba(56,182,255,0.22)”:isAlert?“0 0 14px rgba(255,79,79,0.18)”:“none”, transition:“all 0.2s”, animation:isAlert?“pulse-border 1.8s ease-in-out infinite”:“none” }}> <div style={{ aspectRatio:“16/9”, position:“relative”, background:isOffline?”#111a24”:”#0d1c2c” }}> {!isOffline && <div style={{ position:“absolute”, left:0, right:0, height:2, background:“linear-gradient(90deg,transparent,rgba(56,182,255,0.45),transparent)”, animation:“scan-h 3.5s linear infinite” }} />} {!isOffline && ( <svg style={{ position:“absolute”, inset:0, width:“100%”, height:“100%”, opacity:0.07 }}> <defs><pattern id={`gp${cam.id}`} width=“22” height=“22” patternUnits=“userSpaceOnUse”><path d="M22 0L0 0 0 22" fill="none" stroke="#38b6ff" strokeWidth="0.5"/></pattern></defs> <rect width=“100%” height=“100%” fill={`url(#gp${cam.id})`}/> </svg> )} {isAlert && ( <> <div style={{ position:“absolute”, inset:0, border:“2px solid #ff4f4f”, animation:“blink 0.9s ease-in-out infinite”, pointerEvents:“none” }} /> <div style={{ position:“absolute”, top:“18%”, left:“33%”, width:“30%”, height:“66%”, border:“2px solid #ff4f4f”, boxShadow:“0 0 12px rgba(255,79,79,0.5)” }}> <div style={{ position:“absolute”, top:-17, left:0, background:”#ff4f4f”, padding:“1px 5px”, fontSize:8, fontFamily:“Share Tech Mono”, color:”#fff”, whiteSpace:“nowrap” }}>INTRUDER 97.4%</div> </div> </> )} {isMotion && ( <div style={{ position:“absolute”, top:“28%”, right:“18%”, width:“24%”, height:“44%”, border:“2px solid #ffcc00”, boxShadow:“0 0 10px rgba(255,204,0,0.35)” }}> <div style={{ position:“absolute”, top:-17, left:0, background:”#ffcc00”, padding:“1px 5px”, fontSize:8, fontFamily:“Share Tech Mono”, color:”#000” }}>MOTION</div> </div> )} {isOffline && ( <div style={{ position:“absolute”, inset:0, display:“flex”, flexDirection:“column”, alignItems:“center”, justifyContent:“center”, gap:6 }}> <div style={{ fontSize:24, opacity:0.18 }}>📷</div> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8, color:“var(–text-dim)” }}>NO SIGNAL</span> </div> )} {cam.recording && !isOffline && ( <div style={{ position:“absolute”, top:6, right:7, display:“flex”, alignItems:“center”, gap:4 }}> <div style={{ width:5,height:5,borderRadius:“50%”,background:”#ff4f4f”,animation:“blink 1.1s infinite” }} /> <span style={{ fontFamily:“Share Tech Mono”, fontSize:7.5, color:”#ff4f4f” }}>REC</span> </div> )} {!isOffline && <div style={{ position:“absolute”, bottom:5, left:6, fontFamily:“Share Tech Mono”, fontSize:7.5, color:“rgba(255,255,255,0.3)” }}>{new Date().toLocaleTimeString(“en-GB”)} · {cam.fps}fps</div>} </div> <div style={{ padding:“7px 10px”, display:“flex”, justifyContent:“space-between”, alignItems:“center” }}> <div> <div style={{ fontFamily:“Rajdhani”, fontWeight:600, fontSize:12 }}>{cam.name}</div> <div style={{ fontFamily:“Share Tech Mono”, fontSize:8, color:“var(–text-secondary)” }}>{cam.id} · {cam.zone}</div> </div> <Badge label={cam.status} color={sc} blink={isAlert} /> </div> </div> ); }; // ── LIVE PAGE ───────────────────────────────────────────────────────────────── const LivePage = ({ cameras, alerts, onAck, onAckAll }) => { const [sel, setSel] = useState(cameras[0]); const [callActive, setCallActive] = useState(true); const [sideTab, setSideTab] = useState(“alerts”); const unacked = alerts.filter(a=>!a.ack).length; return ( <div style={{ display:“grid”, gridTemplateColumns:“1fr 330px”, height:“100%” }}> {/* Left */} <div style={{ padding:“16px 12px 20px 20px”, overflowY:“auto”, display:“flex”, flexDirection:“column”, gap:13 }}> <div style={{ display:“grid”, gridTemplateColumns:“repeat(4,1fr)”, gap:10 }}> <Stat label="Threat Level" value="95%" sub="CAM-01 CRITICAL" color="#ff4f4f" icon="🔴" /> <Stat label="Active Cameras" value="5/6" sub="CAM-06 offline" color="#38b6ff" icon="📹" /> <Stat label=“Alerts Today” value={alerts.length} sub={`${unacked} unacknowledged`} color=”#ffcc00” icon=“⚡” /> <Stat label="Avg Response" value="0:23s" sub="Agent dispatch time" color="#00e5a0" icon="🤖" /> </div> ``` <div> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:9 }}> <span style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:12, color:"var(--text-secondary)", letterSpacing:2 }}>LIVE FEEDS</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-dim)" }}>{new Date().toLocaleTimeString("en-GB")}</span> </div> <div style={{ display:"grid", gridTemplateColumns:"repeat(3,1fr)", gap:8 }}> {cameras.map(c=><CameraCard key={c.id} cam={c} selected={sel?.id===c.id} onClick={setSel}/>)} </div> </div> <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:12 }}> {/* Camera detail */} <div className="card" style={{ padding:14 }}> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:11, color:"var(--text-secondary)", letterSpacing:2, marginBottom:11 }}>CAMERA DETAIL — {sel?.id}</div> {[["Location",sel?.name],["Zone",sel?.zone],["Status",sel?.status],["Frame Rate",`${sel?.fps} fps`],["Uptime",sel?.uptime],["Recording",sel?.recording?"ACTIVE":"STOPPED"]].map(([k,v])=>( <div key={k} style={{ display:"flex", justifyContent:"space-between", borderBottom:"1px solid var(--border)", paddingBottom:5, marginBottom:5 }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)" }}>{k}</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-primary)" }}>{v}</span> </div> ))} <div style={{ marginTop:5 }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-dim)", marginBottom:5 }}>Threat Score</div> <ThreatBar value={sel?.threat||0}/> </div> <div style={{ display:"flex", gap:5, marginTop:11 }}> {["PTZ","ZOOM","SNAPSHOT","PLAYBACK"].map(b=>( <button key={b} className="btn" style={{ flex:1, background:"rgba(56,182,255,0.07)", border:"1px solid var(--border)", color:"var(--text-secondary)", padding:"5px 0", fontSize:7.5, fontFamily:"Share Tech Mono" }} onMouseEnter={e=>{e.currentTarget.style.borderColor="var(--accent)";e.currentTarget.style.color="var(--accent)"}} onMouseLeave={e=>{e.currentTarget.style.borderColor="var(--border)";e.currentTarget.style.color="var(--text-secondary)"}} >{b}</button> ))} </div> </div> {/* AI Agent */} <div className="card" style={{ padding:14 }}> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:11, color:"var(--text-secondary)", letterSpacing:2, marginBottom:11 }}>AI AGENT STATUS</div> <div style={{ background:"rgba(37,211,102,0.06)", border:"1px solid rgba(37,211,102,0.22)", borderRadius:6, padding:10, marginBottom:9 }}> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:6 }}> <div style={{ display:"flex", alignItems:"center", gap:7 }}> <span style={{ fontSize:14 }}>💬</span> <span style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:12 }}>WhatsApp Alert</span> </div> <Badge label="✓ DELIVERED" color="#25D366"/> </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)", lineHeight:1.7 }}> To: Ahmed Al-Rashid, Fatima Hassan, Control Room<br/> "🚨 INTRUSION — CAM-01 Main Entrance<br/> 02:14:37 | CRITICAL 97.4% | Zone A" </div> <div style={{ marginTop:6, fontFamily:"Share Tech Mono", fontSize:7.5, color:"#25D366" }}>Sent 02:14:39 · All 3 confirmed</div> </div> <div style={{ background:"rgba(56,182,255,0.06)", border:"1px solid rgba(56,182,255,0.22)", borderRadius:6, padding:10 }}> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginBottom:6 }}> <div style={{ display:"flex", alignItems:"center", gap:7 }}> <span style={{ fontSize:14 }}>📞</span> <span style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:12 }}>AI Voice Call</span> </div> <Badge label={callActive?"● ON CALL":"● ENDED"} color={callActive?"var(--accent)":"var(--text-dim)"} blink={callActive}/> </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)", marginBottom:7 }}> {callActive?"Agent → Control Room (+971 4 000 0000)":"Call ended — duration 01:47"} </div> <Waveform active={callActive}/> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", marginTop:7 }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:7.5, color:"var(--text-dim)" }}>{callActive?"Duration: 01:23":"Call ended"}</span> <button onClick={()=>setCallActive(v=>!v)} className="btn" style={{ background:callActive?"rgba(255,79,79,0.1)":"rgba(0,229,160,0.1)", border:`1px solid ${callActive?"#ff4f4f":"#00e5a0"}`, color:callActive?"#ff4f4f":"#00e5a0", padding:"4px 10px", fontSize:8, fontFamily:"Share Tech Mono" }}>{callActive?"END CALL":"RECONNECT"}</button> </div> </div> </div> </div> {/* Timeline */} <div className="card" style={{ padding:14 }}> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:11, color:"var(--text-secondary)", letterSpacing:2, marginBottom:11 }}>INCIDENT TIMELINE</div> <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:"0 24px" }}> {alerts.map(a=>( <div key={a.id} style={{ display:"flex", gap:9, paddingBottom:11 }}> <div style={{ display:"flex", flexDirection:"column", alignItems:"center" }}> <div style={{ width:7,height:7,borderRadius:"50%",background:severityColor(a.severity),flexShrink:0,marginTop:4 }}/> <div style={{ width:1,flex:1,background:"var(--border)",marginTop:4 }}/> </div> <div style={{ flex:1 }}> <div style={{ display:"flex", justifyContent:"space-between" }}> <span style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:12 }}>{a.type}</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-dim)" }}>{a.time}</span> </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-secondary)", marginTop:2 }}>{a.cam} · {a.msg.slice(0,46)}…</div> </div> </div> ))} </div> </div> </div> {/* Right sidebar */} <div style={{ borderLeft:"1px solid var(--border)", background:"var(--bg-panel)", display:"flex", flexDirection:"column", height:"100%", overflow:"hidden" }}> <div style={{ display:"flex", borderBottom:"1px solid var(--border)", flexShrink:0 }}> {[["alerts","ALERTS"],["contacts","CONTACTS"],["zones","ZONES"]].map(([k,l])=>( <button key={k} onClick={()=>setSideTab(k)} className="btn" style={{ flex:1, padding:"10px 0", background:sideTab===k?"rgba(56,182,255,0.09)":"transparent", border:"none", borderBottom:sideTab===k?"2px solid var(--accent)":"2px solid transparent", color:sideTab===k?"var(--accent)":"var(--text-dim)", fontFamily:"Rajdhani", fontWeight:700, fontSize:11, letterSpacing:1 }}>{l}</button> ))} </div> {sideTab==="alerts" && ( <div style={{ flex:1, overflowY:"auto" }}> <div style={{ padding:"9px 14px", display:"flex", justifyContent:"space-between", borderBottom:"1px solid var(--border)" }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)" }}>{unacked} unacknowledged</span> <button onClick={onAckAll} className="btn" style={{ background:"transparent", border:"none", color:"var(--accent)", fontFamily:"Share Tech Mono", fontSize:8.5, cursor:"pointer" }}>ACK ALL</button> </div> {alerts.map(a=>( <div key={a.id} style={{ display:"grid", gridTemplateColumns:"56px 1fr auto", gap:8, padding:"10px 13px", borderBottom:"1px solid var(--border)", background:a.ack?"transparent":"rgba(255,79,79,0.03)", opacity:a.ack?0.5:1, transition:"opacity 0.3s" }}> <div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-secondary)" }}>{a.time}</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:7.5, color:"var(--text-dim)" }}>{a.cam}</div> </div> <div> <div style={{ display:"flex", gap:5, alignItems:"center", marginBottom:3 }}> <Badge label={a.severity} color={severityColor(a.severity)}/> <span style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:11 }}>{a.type}</span> </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)" }}>{a.msg}</div> <div style={{ display:"flex", gap:8, marginTop:4 }}> {a.whatsapp && <span style={{ fontSize:7.5, color:"#25D366" }}>✓ WhatsApp</span>} {a.call && <span style={{ fontSize:7.5, color:"var(--accent)" }}>✓ Voice Call</span>} </div> </div> {!a.ack && ( <button onClick={()=>onAck(a.id)} className="btn" style={{ background:"transparent", border:"1px solid var(--accent)", color:"var(--accent)", padding:"3px 7px", fontFamily:"Share Tech Mono", fontSize:7.5, whiteSpace:"nowrap", alignSelf:"flex-start" }}>ACK</button> )} </div> ))} </div> )} {sideTab==="contacts" && ( <div style={{ flex:1, overflowY:"auto", padding:13, display:"flex", flexDirection:"column", gap:10 }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)", marginBottom:2 }}>SECURITY TEAM — ALL NOTIFIED</div> {CONTACTS_DATA.map((c,i)=>( <div key={i} className="card" style={{ padding:12 }}> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:7 }}> <div style={{ display:"flex", gap:9, alignItems:"center" }}> <div style={{ width:30,height:30,borderRadius:"50%",background:"rgba(56,182,255,0.12)",border:"1px solid var(--border)",display:"flex",alignItems:"center",justifyContent:"center",fontFamily:"Rajdhani",fontWeight:700,fontSize:10,color:"var(--accent)",flexShrink:0 }}>{c.avatar}</div> <div> <div style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:13 }}>{c.name}</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:7.5, color:"var(--text-dim)" }}>{c.role}</div> </div> </div> <Badge label={c.status} color={c.status==="ON CALL"?"var(--accent)":"#25D366"} blink={c.status==="ON CALL"}/> </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-secondary)", marginBottom:9 }}>{c.phone}</div> <div style={{ display:"flex", gap:6 }}> <button className="btn" style={{ flex:1, background:"rgba(37,211,102,0.07)", border:"1px solid rgba(37,211,102,0.25)", color:"#25D366", padding:"6px 0", fontFamily:"Share Tech Mono", fontSize:7.5 }}>💬 WHATSAPP</button> <button className="btn" style={{ flex:1, background:"rgba(56,182,255,0.07)", border:"1px solid rgba(56,182,255,0.25)", color:"var(--accent)", padding:"6px 0", fontFamily:"Share Tech Mono", fontSize:7.5 }}>📞 CALL</button> </div> </div> ))} <div className="card" style={{ padding:12 }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--accent)", marginBottom:8 }}>AI AGENT CALL SCRIPT</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)", lineHeight:1.75 }}> "This is AIORA, your AI security agent.<br/> CRITICAL intrusion detected at Main Entrance,<br/> Camera 01, Zone Alpha — 02:14:37.<br/> Threat confidence: 97.4%.<br/> Immediate response required.<br/> Team notified via WhatsApp. Please confirm<br/> and dispatch personnel immediately." </div> </div> </div> )} {sideTab==="zones" && ( <div style={{ flex:1, overflowY:"auto", padding:13 }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)", marginBottom:10 }}>FACILITY ZONE MAP</div> <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:5, marginBottom:14, aspectRatio:"1" }}> {[ {label:"ZONE A\nMain Entrance\nLobby", bg:"rgba(255,79,79,0.1)", border:"#ff4f4f", st:"ALERT"}, {label:"ZONE B\nParking N\nParking S", bg:"rgba(255,204,0,0.07)", border:"#ffcc00", st:"MOTION"}, {label:"ZONE C\nServer Room", bg:"rgba(56,182,255,0.05)",border:"#38b6ff", st:"SECURE"}, {label:"ZONE D\nRooftop East", bg:"rgba(255,140,0,0.07)", border:"#ff8c00", st:"CAUTION"}, ].map((z,i)=>( <div key={i} style={{ background:z.bg, border:`1px solid ${z.border}`, borderRadius:5, padding:9, display:"flex", flexDirection:"column", justifyContent:"space-between" }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)", whiteSpace:"pre-line", lineHeight:1.65 }}>{z.label}</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:z.border, borderTop:`1px solid ${z.border}44`, paddingTop:5, marginTop:5 }}>{z.st}</div> </div> ))} </div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)", marginBottom:9 }}>CAMERA THREAT SCORES</div> {CAMERAS_DATA.filter(c=>c.status!=="OFFLINE").map(cam=>( <div key={cam.id} style={{ marginBottom:10 }}> <div style={{ display:"flex", justifyContent:"space-between", marginBottom:4 }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-secondary)" }}>{cam.id} · {cam.name}</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:7.5, color:"var(--text-dim)" }}>{cam.zone}</span> </div> <ThreatBar value={cam.threat}/> </div> ))} </div> )} {/* System health footer */} <div style={{ borderTop:"1px solid var(--border)", padding:13, flexShrink:0 }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)", marginBottom:8 }}>SYSTEM HEALTH</div> {[["AI Engine","ONLINE","#00e5a0"],["WhatsApp API","ONLINE","#25D366"],["Voice Agent","ACTIVE","#38b6ff"],["Storage","84% used","#ffcc00"],["Network","12 ms","#00e5a0"]].map(([l,v,c])=>( <div key={l} style={{ display:"flex", justifyContent:"space-between", marginBottom:5 }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-dim)" }}>{l}</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:c }}>{v}</span> </div> ))} </div> </div> </div> ``` ); }; // ── RECORDINGS PAGE ─────────────────────────────────────────────────────────── const RecordingsPage = () => { const [playing, setPlaying] = useState(null); const [filter, setFilter] = useState(“ALL”); const types = [“ALL”,“INTRUSION”,“MOTION”,“TAILGATING”,“LOITERING”]; const filtered = filter===“ALL” ? RECORDINGS_DATA : RECORDINGS_DATA.filter(r=>r.type===filter); return ( <div style={{ padding:20, overflowY:“auto”, height:“100%” }}> <div style={{ display:“flex”, justifyContent:“space-between”, alignItems:“center”, marginBottom:16 }}> <div> <h2 style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:20, letterSpacing:2 }}>EVENT RECORDINGS</h2> <p style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)”, marginTop:2 }}>{RECORDINGS_DATA.length} recordings · Auto-saved on detection</p> </div> <div style={{ display:“flex”, gap:5 }}> {types.map(t=>( <button key={t} onClick={()=>setFilter(t)} className=“btn” style={{ background:filter===t?“rgba(56,182,255,0.12)”:“transparent”, border:`1px solid ${filter===t?"var(--accent)":"var(--border)"}`, color:filter===t?“var(–accent)”:“var(–text-secondary)”, padding:“5px 11px”, fontFamily:“Rajdhani”, fontWeight:600, fontSize:11, letterSpacing:0.5 }}>{t}</button> ))} </div> </div> ``` <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill,minmax(290px,1fr))", gap:12 }}> {filtered.map(rec=>( <div key={rec.id} className="card" style={{ overflow:"hidden", animation:"fade-up 0.3s ease" }}> <div style={{ aspectRatio:"16/9", background:"#0d1c2c", display:"flex", alignItems:"center", justifyContent:"center", position:"relative", cursor:"pointer" }} onClick={()=>setPlaying(playing===rec.id?null:rec.id)}> <div style={{ fontSize:32, opacity:0.4 }}>🎬</div> {playing===rec.id ? ( <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center", background:"rgba(56,182,255,0.04)" }}> <Waveform active={true}/> </div> ) : ( <div style={{ position:"absolute", inset:0, display:"flex", alignItems:"center", justifyContent:"center" }}> <div style={{ width:42,height:42,borderRadius:"50%",background:"rgba(56,182,255,0.15)",border:"1px solid var(--accent)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:16,backdropFilter:"blur(4px)" }}>▶</div> </div> )} <div style={{ position:"absolute", top:8, right:8 }}><Badge label={rec.type} color={typeColor(rec.type)}/></div> <div style={{ position:"absolute", bottom:7, left:8, fontFamily:"Share Tech Mono", fontSize:7.5, color:"rgba(255,255,255,0.35)" }}>{rec.duration}</div> </div> <div style={{ padding:12 }}> <div style={{ display:"flex", justifyContent:"space-between", alignItems:"flex-start", marginBottom:7 }}> <div> <div style={{ fontFamily:"Rajdhani", fontWeight:600, fontSize:13 }}>{rec.id}</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-dim)" }}>{rec.cam} · {rec.date}</div> </div> <span style={{ fontFamily:"Share Tech Mono", fontSize:8, color:"var(--text-secondary)" }}>{rec.size}</span> </div> <div style={{ display:"flex", gap:5 }}> {["⬇ DOWNLOAD","✂ CLIP","🔗 SHARE"].map(b=>( <button key={b} className="btn" style={{ flex:1, background:"rgba(56,182,255,0.06)", border:"1px solid var(--border)", color:"var(--text-secondary)", padding:"5px 0", fontFamily:"Share Tech Mono", fontSize:7.5 }} onMouseEnter={e=>{e.currentTarget.style.borderColor="var(--accent)";e.currentTarget.style.color="var(--accent)"}} onMouseLeave={e=>{e.currentTarget.style.borderColor="var(--border)";e.currentTarget.style.color="var(--text-secondary)"}} >{b}</button> ))} </div> </div> </div> ))} </div> </div> ``` ); }; // ── ANALYTICS PAGE ──────────────────────────────────────────────────────────── const AnalyticsPage = () => { const events = [0,0,1,0,0,1,0,2,1,0,0,1,2,0,0,0,1,0,2,3,1,0,1,2]; const maxE = Math.max(…events); return ( <div style={{ padding:20, overflowY:“auto”, height:“100%” }}> <h2 style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:20, letterSpacing:2, marginBottom:4 }}>ANALYTICS</h2> <p style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)”, marginBottom:16 }}>Last 24 hours · Live refresh every 60s</p> ``` <div style={{ display:"grid", gridTemplateColumns:"repeat(4,1fr)", gap:10, marginBottom:16 }}> <Stat label="Total Events" value="14" sub="Last 24 hours" color="#38b6ff" icon="📊"/> <Stat label="Intrusions" value="3" sub="Avg 1 per 8h" color="#ff4f4f" icon="🚨"/> <Stat label="False Positives" value="1" sub="7.1% rate" color="#ffcc00" icon="⚠️"/> <Stat label="AI Accuracy" value="97.4%" sub="Model v3.2" color="#00e5a0" icon="🤖"/> </div> <div className="card" style={{ padding:16, marginBottom:13 }}> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:11, color:"var(--text-secondary)", letterSpacing:2, marginBottom:13 }}>EVENTS BY HOUR</div> <div style={{ display:"flex", alignItems:"flex-end", gap:3, height:90 }}> {events.map((v,i)=>( <div key={i} style={{ flex:1, display:"flex", flexDirection:"column", alignItems:"center", gap:3 }}> <div style={{ width:"100%", background:v>0?(v>=3?"#ff4f4f":v>=2?"#ffcc00":"#38b6ff"):"var(--bg-input)", borderRadius:"2px 2px 0 0", height:`${maxE>0?(v/maxE)*72:0}px`, minHeight:v>0?4:0, transition:"height 0.6s ease", boxShadow:v>0?`0 0 8px ${v>=3?"#ff4f4f44":v>=2?"#ffcc0044":"#38b6ff44"}`:"none" }}/> {i%4===0 && <span style={{ fontFamily:"Share Tech Mono", fontSize:7, color:"var(--text-dim)" }}>{String(i).padStart(2,"0")}</span>} </div> ))} </div> </div> <div className="card" style={{ padding:16 }}> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:11, color:"var(--text-secondary)", letterSpacing:2, marginBottom:12 }}>EVENTS PER CAMERA</div> {[["CAM-01 Main Entrance",8,"#ff4f4f"],["CAM-04 Rooftop East",3,"#ffcc00"],["CAM-02 Parking Lot N",2,"#38b6ff"],["CAM-03 Server Room",1,"#38b6ff"]].map(([label,val,c])=>( <div key={label} style={{ marginBottom:11 }}> <div style={{ display:"flex", justifyContent:"space-between", marginBottom:4 }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--text-secondary)" }}>{label}</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:c }}>{val} events</span> </div> <div style={{ height:6, background:"rgba(255,255,255,0.05)", borderRadius:3, overflow:"hidden" }}> <div style={{ width:`${(val/8)*100}%`, height:"100%", background:c, borderRadius:3, boxShadow:`0 0 6px ${c}88`, transition:"width 0.8s ease" }}/> </div> </div> ))} </div> </div> ``` ); }; // ── ZONES PAGE ──────────────────────────────────────────────────────────────── const ZonesPage = () => { const [activeZone, setActiveZone] = useState(“A”); const zones = [ { id:“A”, name:“Zone A — Main Entrance & Lobby”, cameras:[“CAM-01”,“CAM-05”], status:“ALERT”, risk:“CRITICAL”, guards:2 }, { id:“B”, name:“Zone B — Parking (N & S)”, cameras:[“CAM-02”,“CAM-06”], status:“MOTION”, risk:“MEDIUM”, guards:1 }, { id:“C”, name:“Zone C — Server Room”, cameras:[“CAM-03”], status:“SECURE”, risk:“LOW”, guards:1 }, { id:“D”, name:“Zone D — Rooftop East”, cameras:[“CAM-04”], status:“CAUTION”, risk:“HIGH”, guards:0 }, ]; const zc = { ALERT:”#ff4f4f”, MOTION:”#ffcc00”, SECURE:”#00e5a0”, CAUTION:”#ff8c00” }; const az = zones.find(z=>z.id===activeZone); return ( <div style={{ padding:20, display:“grid”, gridTemplateColumns:“260px 1fr”, gap:16, height:“100%”, overflowY:“auto” }}> <div style={{ display:“flex”, flexDirection:“column”, gap:9 }}> <h2 style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:18, letterSpacing:2, marginBottom:4 }}>ZONE CONTROL</h2> {zones.map(z=>( <div key={z.id} onClick={()=>setActiveZone(z.id)} className=“card” style={{ padding:12, cursor:“pointer”, border:`1px solid ${activeZone===z.id?zc[z.status]:"var(--border)"}`, background:activeZone===z.id?`${zc[z.status]}0f`:“var(–bg-card)”, transition:“all 0.2s” }}> <div style={{ display:“flex”, justifyContent:“space-between”, alignItems:“center”, marginBottom:5 }}> <span style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:13 }}>{z.name}</span> <Badge label={z.status} color={zc[z.status]} blink={z.status===“ALERT”}/> </div> <div style={{ fontFamily:“Share Tech Mono”, fontSize:8, color:“var(–text-dim)” }}>{z.cameras.length} camera(s) · {z.guards} guard(s)</div> </div> ))} </div> <div className=“card” style={{ padding:18 }}> <div style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:15, letterSpacing:2, marginBottom:14 }}>{az?.name}</div> <div style={{ display:“grid”, gridTemplateColumns:“1fr 1fr”, gap:10, marginBottom:16 }}> {[[“Status”,az?.status],[“Risk Level”,az?.risk],[“Cameras”,az?.cameras.join(”, “)],[“Guards on Duty”,String(az?.guards)]].map(([k,v])=>( <div key={k} className=“card” style={{ padding:10, background:“var(–bg-card2)” }}> <div style={{ fontFamily:“Share Tech Mono”, fontSize:8, color:“var(–text-dim)”, marginBottom:4 }}>{k}</div> <div style={{ fontFamily:“Rajdhani”, fontWeight:600, fontSize:14 }}>{v}</div> </div> ))} </div> <div style={{ display:“flex”, gap:8 }}> {[“🔒 LOCK DOWN”,“📢 BROADCAST”,“🚨 DISPATCH”,“📋 REPORT”].map(b=>( <button key={b} className=“btn” style={{ flex:1, background:“rgba(56,182,255,0.07)”, border:“1px solid var(–border)”, color:“var(–text-secondary)”, padding:“9px 6px”, fontFamily:“Rajdhani”, fontWeight:600, fontSize:11, letterSpacing:0.4 }} onMouseEnter={e=>{e.currentTarget.style.borderColor=“var(–accent)”;e.currentTarget.style.color=“var(–accent)”}} onMouseLeave={e=>{e.currentTarget.style.borderColor=“var(–border)”;e.currentTarget.style.color=“var(–text-secondary)”}} >{b}</button> ))} </div> </div> </div> ); }; // ── SETTINGS PAGE ───────────────────────────────────────────────────────────── const SettingsPage = () => { const [wa, setWa] = useState(true); const [call, setCall] = useState(true); const [sens, setSens] = useState(85); const [ret, setRet] = useState(30); const [saved, setSaved] = useState(false); const Toggle = ({val, onToggle}) => ( <div onClick={onToggle} style={{ width:38,height:20,borderRadius:10,background:val?“rgba(0,229,160,0.25)”:“var(–bg-input)”,border:`1px solid ${val?"var(--accent2)":"var(--border)"}`,position:“relative”,cursor:“pointer”,transition:“all 0.2s” }}> <div style={{ width:14,height:14,borderRadius:“50%”,background:val?“var(–accent2)”:“var(–text-dim)”,position:“absolute”,top:2,left:val?20:2,transition:“all 0.2s” }}/> </div> ); return ( <div style={{ padding:20, maxWidth:620, overflowY:“auto”, height:“100%” }}> <h2 style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:20, letterSpacing:2, marginBottom:18 }}>SYSTEM SETTINGS</h2> <div className=“card” style={{ padding:16, marginBottom:11 }}> <div style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:11, color:“var(–text-secondary)”, letterSpacing:2, marginBottom:12 }}>NOTIFICATIONS</div> {[[“WhatsApp Alerts”,wa,()=>setWa(v=>!v)],[“AI Voice Calls”,call,()=>setCall(v=>!v)]].map(([l,v,fn])=>( <div key={l} style={{ display:“flex”, justifyContent:“space-between”, alignItems:“center”, marginBottom:11 }}> <span style={{ fontFamily:“Rajdhani”, fontSize:14 }}>{l}</span> <Toggle val={v} onToggle={fn}/> </div> ))} </div> <div className=“card” style={{ padding:16, marginBottom:11 }}> <div style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:11, color:“var(–text-secondary)”, letterSpacing:2, marginBottom:12 }}>AI DETECTION SENSITIVITY</div> <input type=“range” min={50} max={100} value={sens} onChange={e=>setSens(+e.target.value)} style={{ width:“100%”, accentColor:“var(–accent)”, marginBottom:6 }}/> <div style={{ display:“flex”, justifyContent:“space-between” }}> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)” }}>Low (fewer alerts)</span> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–accent)” }}>{sens}%</span> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)” }}>High (more alerts)</span> </div> </div> <div className=“card” style={{ padding:16, marginBottom:16 }}> <div style={{ fontFamily:“Rajdhani”, fontWeight:700, fontSize:11, color:“var(–text-secondary)”, letterSpacing:2, marginBottom:12 }}>RECORDING RETENTION</div> <input type=“range” min={7} max={90} value={ret} onChange={e=>setRet(+e.target.value)} style={{ width:“100%”, accentColor:“var(–accent)”, marginBottom:6 }}/> <div style={{ display:“flex”, justifyContent:“space-between” }}> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)” }}>7 days</span> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–accent)” }}>{ret} days</span> <span style={{ fontFamily:“Share Tech Mono”, fontSize:8.5, color:“var(–text-dim)” }}>90 days</span> </div> </div> <button onClick={()=>{setSaved(true);setTimeout(()=>setSaved(false),2500)}} className=“btn” style={{ background:“rgba(0,229,160,0.12)”, border:“1px solid var(–accent2)”, color:“var(–accent2)”, padding:“10px 24px”, fontFamily:“Rajdhani”, fontWeight:700, fontSize:14, letterSpacing:1 }}> {saved?“✓ SAVED”:“SAVE SETTINGS”} </button> </div> ); }; // ── ROOT ────────────────────────────────────────────────────────────────────── export default function AIORADashboard() { const [tab, setTab] = useState(“LIVE”); const [alerts, setAlerts] = useState(ALERTS_INIT); const [time, setTime] = useState(new Date()); const [banner, setBanner] = useState(true); useEffect(()=>{ const t=setInterval(()=>setTime(new Date()),1000); return()=>clearInterval(t); },[]); const ackAlert = id => setAlerts(p=>p.map(a=>a.id===id?{…a,ack:true}:a)); const ackAll = () => setAlerts(p=>p.map(a=>({…a,ack:true}))); const unacked = alerts.filter(a=>!a.ack).length; const TABS = [“LIVE”,“RECORDINGS”,“ANALYTICS”,“ZONES”,“SETTINGS”]; return ( <div style={{ position:“relative”, zIndex:1, display:“flex”, flexDirection:“column”, height:“100vh”, overflow:“hidden” }}> <GlobalStyles/> ``` {/* Banner */} {banner && ( <div style={{ background:"linear-gradient(90deg,rgba(255,79,79,0.16),rgba(255,79,79,0.07),rgba(255,79,79,0.16))", borderBottom:"1px solid rgba(255,79,79,0.4)", padding:"7px 20px", display:"flex", alignItems:"center", justifyContent:"space-between", flexShrink:0 }}> <div style={{ display:"flex", alignItems:"center", gap:9 }}> <div style={{ width:7,height:7,borderRadius:"50%",background:"#ff4f4f",animation:"blink 0.65s infinite" }}/> <span style={{ fontFamily:"Share Tech Mono", fontSize:11, color:"#ff4f4f", letterSpacing:1.2 }}>⚠ CRITICAL INTRUSION — CAM-01 MAIN ENTRANCE — AGENT DISPATCHED — 02:14:37</span> </div> <div style={{ display:"flex", gap:14, alignItems:"center" }}> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"#25D366" }}>● WhatsApp SENT</span> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"var(--accent)", animation:"blink 1.2s infinite" }}>● VOICE CALL ACTIVE</span> <button onClick={()=>setBanner(false)} className="btn" style={{ background:"transparent", border:"1px solid #ff4f4f", color:"#ff4f4f", padding:"3px 9px", fontFamily:"Share Tech Mono", fontSize:8 }}>✕</button> </div> </div> )} {/* Header */} <header style={{ display:"flex", alignItems:"center", justifyContent:"space-between", padding:"10px 22px", borderBottom:"1px solid var(--border)", background:"rgba(21,31,46,0.98)", backdropFilter:"blur(10px)", flexShrink:0, gap:16 }}> <div style={{ display:"flex", alignItems:"center", gap:12, flexShrink:0 }}> <svg width="36" height="36" viewBox="0 0 40 40"> <circle cx="20" cy="20" r="17" fill="none" stroke="var(--accent)" strokeWidth="1" opacity="0.3"/> <circle cx="20" cy="20" r="13" fill="none" stroke="var(--accent)" strokeWidth="0.6" strokeDasharray="4 2" style={{animation:"spin 9s linear infinite"}}/> <circle cx="20" cy="20" r="7" fill="rgba(56,182,255,0.1)" stroke="var(--accent)" strokeWidth="1.5"/> <circle cx="20" cy="20" r="2.5" fill="var(--accent)"/> {[0,60,120,180,240,300].map((a,i)=><circle key={i} cx={20+13*Math.cos(a*Math.PI/180)} cy={20+13*Math.sin(a*Math.PI/180)} r="1.2" fill="var(--accent)" opacity="0.5"/>)} </svg> <div> <div style={{ fontFamily:"Rajdhani", fontWeight:700, fontSize:20, letterSpacing:5, color:"var(--accent)", lineHeight:1 }}>AIORA</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:7, color:"var(--text-dim)", letterSpacing:1.5 }}>AI OPERATIONS & RESPONSE AGENT</div> </div> </div> <nav style={{ display:"flex", gap:1 }}> {TABS.map(t=>( <button key={t} onClick={()=>setTab(t)} className="btn" style={{ background:tab===t?"rgba(56,182,255,0.09)":"transparent", border:"none", borderBottom:tab===t?"2px solid var(--accent)":"2px solid transparent", color:tab===t?"var(--accent)":"var(--text-secondary)", padding:"7px 15px", fontFamily:"Rajdhani", fontWeight:700, fontSize:12, letterSpacing:1 }}>{t}</button> ))} </nav> <div style={{ display:"flex", alignItems:"center", gap:16, flexShrink:0 }}> <div style={{ textAlign:"right" }}> <div style={{ fontFamily:"Share Tech Mono", fontSize:14, color:"var(--text-primary)" }}>{time.toLocaleTimeString("en-GB")}</div> <div style={{ fontFamily:"Share Tech Mono", fontSize:7.5, color:"var(--text-dim)" }}>{time.toLocaleDateString("en-GB",{day:"2-digit",month:"short",year:"numeric"})}</div> </div> {unacked>0 && ( <div style={{ background:"rgba(255,79,79,0.1)", border:"1px solid #ff4f4f", borderRadius:20, padding:"4px 11px", display:"flex", alignItems:"center", gap:5, animation:"blink 2s infinite" }}> <div style={{ width:5,height:5,borderRadius:"50%",background:"#ff4f4f" }}/> <span style={{ fontFamily:"Share Tech Mono", fontSize:8.5, color:"#ff4f4f" }}>{unacked} ACTIVE</span> </div> )} <div style={{ width:30,height:30,borderRadius:"50%",background:"rgba(56,182,255,0.1)",border:"1px solid var(--border)",display:"flex",alignItems:"center",justifyContent:"center",fontSize:13,cursor:"pointer" }}>👤</div> </div> </header> {/* Main */} <main style={{ flex:1, overflow:"hidden" }}> {tab==="LIVE" && <div style={{ height:"100%", overflow:"hidden" }}><LivePage cameras={CAMERAS_DATA} alerts={alerts} onAck={ackAlert} onAckAll={ackAll}/></div>} {tab==="RECORDINGS" && <div style={{ height:"100%", overflow:"hidden" }}><RecordingsPage/></div>} {tab==="ANALYTICS" && <div style={{ height:"100%", overflow:"hidden" }}><AnalyticsPage/></div>} {tab==="ZONES" && <div style={{ height:"100%", overflow:"hidden" }}><ZonesPage/></div>} {tab==="SETTINGS" && <div style={{ height:"100%", overflow:"hidden" }}><SettingsPage/></div>} </main> </div> ``` ); }
I'll implement this AIORA security dashboard in your Next.js project. Let me first check the current project structure and then create the dashboard.
Now I'll create the AIORA security dashboard. I need to fix some issues in the provided code (there were some character encoding issues with dashes) and adapt it for Next.js with proper structure. Let me create the main page and update the layout with the required fonts.
Now let me update the globals.css with the AIORA-specific styles:
Now let me create the main dashboard page with all the components:
I've implemented the AIORA security dashboard. The dashboard includes:
The code was adapted from your provided design with fixes for character encoding issues (en-dashes converted to proper double-hyphens for CSS variables), proper TypeScript types added, and CSS moved to globals.css using CSS custom properties for the theme colors and animations.