Miyaka/alertify-system2
0
1<!DOCTYPE html>2<html lang="en">3<head>4 <meta charset="UTF-8" />5 <title>Alertify // Command Nexus v5.0</title>6 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />7 8 <link href="https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />9 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css" />10 11 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />12 13 <style>14 /* --- THEME: RED & BLACK CYBERPUNK (Production) --- */15 :root {16 --bg-void: #020003;17 --accent: #ff1b3d; /* Main Red */18 --accent-dim: rgba(255, 27, 61, 0.15);19 20 /* Status Colors */21 --st-crit: #ff1b3d;22 --st-high: #ff9500;23 --st-mod: #00ff9d;24 --st-low: #00f3ff;25 /* UI Elements */26 --glass-border: 1px solid rgba(255, 27, 61, 0.2);27 --glass-bg: rgba(12, 5, 8, 0.92);28 --font-head: 'Orbitron', sans-serif;29 --font-body: 'Inter', sans-serif;30 --font-code: 'JetBrains Mono', monospace;31 }32 * { margin: 0; padding: 0; box-sizing: border-box; outline: none; }33 body {34 background: var(--bg-void);35 background-image: 36 linear-gradient(rgba(255, 27, 61, 0.03) 1px, transparent 1px),37 linear-gradient(90deg, rgba(255, 27, 61, 0.03) 1px, transparent 1px);38 background-size: 40px 40px;39 color: #fff; font-family: var(--font-body);40 height: 100vh; display: flex; overflow: hidden;41 }42 /* --- ANIMATIONS --- */43 @keyframes pulse { 0% { box-shadow: 0 0 0 0 rgba(255, 27, 61, 0.7); } 70% { box-shadow: 0 0 0 15px rgba(255, 27, 61, 0); } 100% { box-shadow: 0 0 0 0 rgba(255, 27, 61, 0); } }44 @keyframes radar { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }45 /* --- SIDEBAR --- */46 .sidebar {47 width: 64px; background: #050002; border-right: var(--glass-border);48 display: flex; flex-direction: column; align-items: center; padding: 20px 0; z-index: 100;49 }50 .brand-logo {51 width: 42px; height: 42px; border-radius: 8px; background: linear-gradient(135deg, var(--accent), #500);52 display: grid; place-items: center; font-family: var(--font-head); font-weight: 800; font-size: 1.2rem;53 margin-bottom: 40px; box-shadow: 0 0 20px rgba(255, 27, 61, 0.5);54 }55 .nav-icon {56 width: 40px; height: 40px; border-radius: 6px; display: grid; place-items: center;57 color: #666; font-size: 1.1rem; margin-bottom: 12px; cursor: pointer; transition: 0.2s;58 }59 .nav-icon:hover { color: #fff; background: rgba(255,255,255,0.05); }60 .nav-icon.active { color: var(--accent); background: rgba(255, 27, 61, 0.1); border-left: 3px solid var(--accent); }61 /* --- MAIN LAYOUT --- */62 .main-grid { flex: 1; display: flex; flex-direction: column; position: relative; }63 /* HEADER */64 .top-bar {65 height: 70px; border-bottom: var(--glass-border); background: rgba(5, 0, 3, 0.9);66 display: flex; justify-content: space-between; align-items: center; padding: 0 24px;67 backdrop-filter: blur(10px); z-index: 50;68 }69 .page-title h1 {70 font-family: var(--font-head); font-size: 1.4rem; margin: 0; line-height: 1;71 background: linear-gradient(90deg, #fff, #888); -webkit-background-clip: text; -webkit-text-fill-color: transparent;72 }73 .page-title span { color: var(--accent); -webkit-text-fill-color: var(--accent); }74 .page-sub { font-size: 0.65rem; color: #666; letter-spacing: 0.2em; text-transform: uppercase; margin-top: 4px; }75 .sys-status { display: flex; gap: 24px; align-items: center; }76 .stat-block { text-align: right; }77 .stat-lbl { font-size: 0.6rem; color: #555; text-transform: uppercase; display: block; letter-spacing: 0.1em; }78 .stat-val { font-family: var(--font-code); color: var(--st-low); font-size: 0.85rem; font-weight: 600; }79 .stat-val.live { color: var(--st-mod); display: flex; align-items: center; gap: 6px; justify-content: flex-end; }80 .blink-dot { width: 6px; height: 6px; background: currentColor; border-radius: 50%; animation: pulse 2s infinite; }81 /* CONTENT SPLIT */82 .workspace { flex: 1; display: grid; grid-template-columns: 1fr 450px; overflow: hidden; }83 /* --- LEFT: MAP & VISUALS --- */84 .vis-container { position: relative; display: flex; flex-direction: column; border-right: var(--glass-border); }85 /* KPI HUD (Floating) */86 .hud-stats {87 position: absolute; top: 16px; left: 16px; right: 16px; z-index: 20;88 display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; pointer-events: none;89 }90 .hud-card {91 background: rgba(10, 5, 8, 0.9); border: 1px solid rgba(255,255,255,0.1); border-radius: 4px;92 padding: 12px 16px; pointer-events: auto; position: relative; overflow: hidden;93 box-shadow: 0 10px 30px rgba(0,0,0,0.5);94 }95 .hud-card::before { content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: #444; }96 .hud-card.crit::before { background: var(--st-crit); box-shadow: 0 0 10px var(--st-crit); }97 .hud-card.high::before { background: var(--st-high); }98 99 .hud-num { font-family: var(--font-head); font-size: 1.8rem; font-weight: 700; line-height: 1; color: #fff; }100 .hud-txt { font-size: 0.65rem; text-transform: uppercase; color: #888; margin-top: 4px; letter-spacing: 0.1em; }101 /* MAP FRAME */102 .map-frame { flex: 1; background: #000; position: relative; }103 #map { width: 100%; height: 100%; background: #050505; z-index: 1; }104 105 /* Radar Overlay */106 .radar-layer {107 position: absolute; inset: 0; pointer-events: none; z-index: 5;108 background: radial-gradient(circle at 50% 50%, transparent 60%, rgba(0,0,0,0.8) 100%);109 }110 .radar-sweep {111 position: absolute; top: 50%; left: 50%; width: 150vmax; height: 150vmax;112 background: conic-gradient(from 0deg, transparent 0deg, rgba(255, 27, 61, 0.05) 20deg, transparent 40deg);113 transform-origin: center; animation: radar 8s linear infinite;114 margin-left: -75vmax; margin-top: -75vmax; pointer-events: none;115 }116 /* MAP CONTROLS */117 .map-dock {118 position: absolute; bottom: 20px; left: 20px; z-index: 20;119 background: rgba(5, 4, 6, 0.95); padding: 10px; border: var(--glass-border); border-radius: 6px;120 display: flex; gap: 8px; backdrop-filter: blur(10px);121 }122 .dock-btn {123 background: rgba(255,255,255,0.05); border: 1px solid rgba(255,255,255,0.1);124 color: #888; padding: 6px 12px; font-family: var(--font-code); font-size: 0.7rem; text-transform: uppercase;125 cursor: pointer; transition: 0.2s; border-radius: 2px;126 }127 .dock-btn:hover { color: #fff; border-color: #fff; }128 .dock-btn.active { background: rgba(255, 27, 61, 0.2); color: #fff; border-color: var(--accent); box-shadow: 0 0 10px rgba(255,27,61,0.2); }129 /* --- RIGHT: INTEL FEED --- */130 .intel-panel {131 background: rgba(8, 6, 8, 0.95); display: flex; flex-direction: column;132 border-left: var(--glass-border); overflow: hidden;133 }134 .feed-search {135 padding: 16px; border-bottom: 1px solid rgba(255,255,255,0.08);136 }137 .search-input {138 width: 100%; background: #000; border: 1px solid rgba(255,255,255,0.15);139 padding: 10px 14px; color: #fff; font-family: var(--font-code); font-size: 0.75rem; border-radius: 4px;140 }141 .search-input:focus { border-color: var(--accent); box-shadow: 0 0 10px rgba(255,27,61,0.2); }142 /* FEED LIST */143 .feed-list {144 flex: 1; overflow-y: auto; padding: 16px; display: flex; flex-direction: column; gap: 10px;145 }146 /* CARD DESIGN */147 .card {148 background: linear-gradient(180deg, rgba(255,255,255,0.03), rgba(255,255,255,0));149 border: 1px solid rgba(255,255,255,0.08); border-radius: 4px; padding: 14px;150 cursor: pointer; transition: all 0.2s; position: relative; border-left: 2px solid transparent;151 }152 .card:hover { background: rgba(255,255,255,0.05); transform: translateX(4px); }153 .card.active { border-left-color: var(--accent); background: rgba(255, 27, 61, 0.05); border-color: rgba(255, 27, 61, 0.3); }154 .card-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }155 .card-title { font-family: var(--font-head); font-size: 0.85rem; font-weight: 700; color: #fff; }156 .card-badge {157 font-size: 0.6rem; padding: 2px 6px; border-radius: 2px; font-weight: 700; font-family: var(--font-code); text-transform: uppercase;158 }159 .bg-crit { color: var(--st-crit); border: 1px solid var(--st-crit); background: rgba(255,27,61,0.1); }160 .bg-high { color: var(--st-high); border: 1px solid var(--st-high); background: rgba(255,149,0,0.1); }161 .bg-mod { color: var(--st-mod); border: 1px solid var(--st-mod); background: rgba(0,255,157,0.1); }162 .bg-low { color: var(--st-low); border: 1px solid var(--st-low); background: rgba(33,150,243,0.1); }163 .card-meta { font-size: 0.7rem; color: #888; display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }164 .card-meta i { color: var(--accent); }165 .card-body { font-size: 0.8rem; color: #ccc; line-height: 1.4; margin-bottom: 8px; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }166 .card-time { font-family: var(--font-code); font-size: 0.6rem; color: #555; text-align: right; display: block; }167 /* DETAIL VIEW */168 .detail-zone {169 height: 350px; background: rgba(0,0,0,0.5); border-top: 1px solid rgba(255,255,255,0.15);170 display: flex; flex-direction: column;171 }172 .dz-head {173 padding: 12px 16px; background: rgba(255,255,255,0.02); border-bottom: 1px solid rgba(255,255,255,0.05);174 display: flex; justify-content: space-between; align-items: center;175 }176 .dz-title { font-family: var(--font-head); font-size: 0.85rem; color: var(--accent); letter-spacing: 0.1em; font-weight: 700; }177 178 .dz-body { padding: 16px; overflow-y: auto; flex: 1; }179 .dz-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 16px; }180 .dz-item label { font-size: 0.6rem; color: #555; text-transform: uppercase; display: block; margin-bottom: 2px; font-family: var(--font-code); }181 .dz-item span { font-size: 0.85rem; color: #fff; font-weight: 500; }182 .ai-wrapper {183 background: rgba(255, 27, 61, 0.05); border: 1px solid rgba(255, 27, 61, 0.15);184 padding: 10px; border-radius: 4px; font-size: 0.75rem; color: #bbb; line-height: 1.4;185 }186 .ai-wrapper strong { color: var(--accent); font-family: var(--font-head); font-size: 0.7rem; text-transform: uppercase; display: block; margin-bottom: 4px; }187 .dz-actions {188 padding: 12px 16px; border-top: 1px solid rgba(255,255,255,0.05); display: flex; gap: 10px;189 }190 .act-btn {191 flex: 1; padding: 10px; background: transparent; border: 1px solid rgba(255,255,255,0.15);192 color: #aaa; text-transform: uppercase; font-family: var(--font-head); font-size: 0.7rem;193 cursor: pointer; transition: 0.2s; border-radius: 2px;194 }195 .act-btn:hover { border-color: #fff; color: #fff; background: rgba(255,255,255,0.05); }196 .act-btn.resolve { border-color: var(--st-mod); color: var(--st-mod); }197 .act-btn.resolve:hover { background: rgba(0, 255, 157, 0.1); color: #fff; }198 /* TICKER */199 .footer-bar {200 height: 28px; background: #000; border-top: 1px solid #111;201 display: flex; align-items: center; padding: 0 16px; overflow: hidden;202 font-family: var(--font-code); font-size: 0.65rem; color: #444; z-index: 50;203 }204 .tick-lbl { color: var(--accent); font-weight: 700; margin-right: 12px; white-space: nowrap; }205 /* --- MAP MARKERS (Custom CSS Pulse) --- */206 .pin-box { width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; position: relative; }207 .pin-dot { width: 8px; height: 8px; background: currentColor; border-radius: 50%; z-index: 2; box-shadow: 0 0 10px currentColor; }208 .pin-wave { position: absolute; inset: 0; border: 1px solid currentColor; border-radius: 50%; opacity: 0; animation: pulse 2s infinite; }209 .pin-wave.fast { animation-duration: 1s; }210 .pin-target { position: absolute; width: 100%; height: 100%; border: 1px dashed currentColor; border-radius: 50%; opacity: 0.3; animation: radar 8s linear infinite; }211 </style>212</head>213<body>214 215 <div class="os-overlay"></div>216 217 <aside class="sidebar">218 <div class="brand-logo">A</div>219 <div class="nav-icon active" title="Dashboard"><i class="fa-solid fa-chart-pie"></i></div>220 <div class="nav-icon" title="Feed"><i class="fa-solid fa-layer-group"></i></div>221 <div class="nav-icon" title="Map"><i class="fa-solid fa-earth-asia"></i></div>222 <div class="sb-spacer"></div>223 <div class="nav-icon" id="logoutBtn" title="Logout" style="color:var(--accent)"><i class="fa-solid fa-power-off"></i></div>224 </aside>225 226 <div class="main-grid">227 228 <header class="top-bar">229 <div class="page-title">230 <h1>Alertify <span>Command</span></h1>231 <div class="page-sub">System Online // Node: LIPA_HQ</div>232 </div>233 <div class="sys-status">234 <div class="stat-block">235 <span class="stat-lbl">Time</span>236 <div class="stat-val" id="clock-text">--:--:--</div>237 </div>238 <div class="stat-block">239 <span class="stat-lbl">Status</span>240 <div class="stat-val live"><div class="blink-dot"></div> LIVE</div>241 </div>242 </div>243 </header>244 245 <main class="workspace">246 247 <section class="vis-container">248 <div class="hud-stats">249 <div class="hud-card">250 <div class="hud-num" id="kpi-total">0</div>251 <div class="hud-txt">Active Alerts</div>252 </div>253 <div class="hud-card crit">254 <div class="hud-num" id="kpi-crit" style="color:var(--st-crit)">0</div>255 <div class="hud-txt" style="color:var(--st-crit)">Critical</div>256 </div>257 <div class="hud-card high">258 <div class="hud-num" id="kpi-high" style="color:var(--st-high)">0</div>259 <div class="hud-txt" style="color:var(--st-high)">High Prio</div>260 </div>261 <div class="hud-card">262 <div class="hud-num" id="kpi-locs">0</div>263 <div class="hud-txt">Zones</div>264 </div>265 </div>266 267 <div class="map-frame">268 <div id="map"></div>269 <div class="radar-layer"><div class="radar-sweep"></div></div>270 271 <div class="map-dock">272 <button class="dock-btn active" data-filter="ALL">Global</button>273 <button class="dock-btn" data-filter="CRITICAL" style="color:var(--st-crit)">CRIT</button>274 <button class="dock-btn" data-filter="HIGH" style="color:var(--st-high)">HIGH</button>275 <div style="width:1px; height:16px; background:rgba(255,255,255,0.2);"></div>276 <button class="dock-btn" data-type="FLOOD">Flood</button>277 <button class="dock-btn" data-type="FIRE">Fire</button>278 </div>279 </div>280 </section>281 282 <section class="intel-panel">283 <div class="feed-search">284 <input type="text" id="alert-search" class="search-input" placeholder="SEARCH INTEL DATABASE..." />285 </div>286 287 <div id="alerts-scroll" class="feed-list">288 <div style="text-align:center; padding:60px; color:#444;">289 <i class="fa-solid fa-satellite-dish fa-spin" style="font-size:2rem; margin-bottom:10px; color:var(--accent);"></i><br>290 <span style="font-family:var(--font-code); font-size:0.7rem;">ESTABLISHING UPLINK...</span>291 </div>292 </div>293 294 <div id="incident-detail" class="detail-zone">295 <div class="dz-head">296 <div class="dz-title" id="det-id">NO SELECTION</div>297 <div id="det-badge" class="card-badge bg-low">IDLE</div>298 </div>299 300 <div class="dz-body">301 <div class="dz-grid">302 <div class="dz-item">303 <label>Classification</label>304 <span id="det-cat">--</span>305 </div>306 <div class="dz-item">307 <label>Geolocation</label>308 <span id="det-loc">--</span>309 </div>310 <div class="dz-item">311 <label>AI Confidence</label>312 <span id="det-conf">--</span>313 </div>314 <div class="dz-item">315 <label>Reporter</label>316 <span id="det-user">--</span>317 </div>318 </div>319 320 <div class="ai-wrapper">321 <strong><i class="fa-solid fa-microchip"></i> Neural Analysis</strong>322 <span id="det-ai-reason">Select a data packet to view extracted vector logic.</span>323 </div>324 </div>325 326 <div class="dz-actions">327 <button class="act-btn" onclick="act(null, currentId, 'ack')">Acknowledge</button>328 <button class="act-btn resolve" onclick="act(null, currentId, 'resolve')">Resolve</button>329 </div>330 </div>331 </section>332 333 </main>334 335 <footer class="footer-bar">336 <div class="tick-lbl">SYSTEM LOG //</div>337 <div id="sys-log">INITIALIZING ALERTIFY COMMAND OS V5.0... LGU NODE CONNECTED... AI MODEL READY...</div>338 </footer>339 340 </div>341 342 <script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>343 344 <script>345 /**346 * ALERTIFY COMMAND OS V5.0347 * Production Engine | Red & Black Theme348 */349 350 // --- CONFIG ---351 const LIPA_CENTER = [13.941, 121.163];352 353 // EXPANDED GAZETTEER (Ensures pins land correctly)354 const GEO_MAP = {355 "sabang": [13.925, 121.180], "tambo": [13.960, 121.190], "antipolo": [13.970, 121.140],356 "mataas na lupa": [13.950, 121.130], "inosloban": [13.910, 121.120], "pinagtongulan": [13.890, 121.150],357 "sico": [13.965, 121.175], "lodlod": [13.940, 121.190], "balintawak": [13.955, 121.145],358 "marawoy": [13.960, 121.155], "bugtong": [13.980, 121.160], "plaridel": [13.950, 121.200],359 "san carlos": [13.950, 121.180], "talisay": [13.965, 121.165], "bulacnin": [13.900, 121.140],360 "malabanan": [13.920, 121.150], "lumbang": [13.910, 121.170], "sm lipa": [13.960, 121.135],361 "pusil": [13.910, 121.100], "halang": [13.930, 121.140], "bolbok": [13.950, 121.160],362 "latag": [13.940, 121.150], "mabini": [13.930, 121.160], "sampaguita": [13.945, 121.170], 363 "sabang": [13.925, 121.180],364"tambo": [13.960, 121.190],365"antipolo": [13.970, 121.140],366"mataas na lupa": [13.950, 121.130],367"inosloban": [13.910, 121.120],368"pinagtongulan": [13.890, 121.150],369"sico": [13.965, 121.175],370"lodlod": [13.940, 121.190],371"balintawak": [13.955, 121.145],372"marawoy": [13.960, 121.155],373"bugtong": [13.980, 121.160],374"plaridel": [13.950, 121.200],375"san carlos": [13.950, 121.180],376"talisay": [13.965, 121.165],377"bulacnin": [13.900, 121.140],378"malabanan": [13.920, 121.150],379"lumbang": [13.910, 121.170],380"sm lipa": [13.960, 121.135],381"pusil": [13.910, 121.100],382"halang": [13.930, 121.140],383"bolbok": [13.950, 121.160],384"dagatan": [13.970, 121.180],385"duhatan": [13.920, 121.130],386"latag": [13.940, 121.150],387"mabini": [13.930, 121.160],388"pagolingin": [13.880, 121.140],389"pangao": [13.900, 121.110],390"quezon": [13.940, 121.165],391"rizal": [13.935, 121.155],392"sampaguita": [13.945, 121.170],393"san benito": [13.955, 121.175],394"san celestino": [13.915, 121.145],395"san francisco": [13.925, 121.135],396"san guillermo": [13.905, 121.115],397"san jose": [13.945, 121.155],398"san lucas": [13.960, 121.185],399"san salvador": [13.910, 121.130],400"san sebastian": [13.950, 121.140],401"santo nino": [13.930, 121.175],402"santo toribio": [13.940, 121.180],403"tangob": [13.915, 121.165],404"tibig": [13.935, 121.185],405"tipacan": [13.895, 121.155],406"poblacion barangay 1": (13.9404, 121.1579),407"poblacion barangay 2": (13.9421, 121.1625),408"poblacion barangay 3": (13.9439, 121.1608),409"poblacion barangay 4": (13.9405, 121.1621),410"poblacion barangay 5": (13.9392, 121.1633),411"poblacion barangay 6": (13.9412, 121.1645),412"poblacion barangay 7": (13.9432, 121.1668),413"poblacion barangay 8": (13.9452, 121.1685),414"poblacion barangay 9": (13.9428, 121.1644),415"poblacion barangay 9 a": (13.9415, 121.1652),416"poblacion barangay 10": (13.9418, 121.1690),417"poblacion barangay 11": (13.9405, 121.1680),418"barangay 12": (13.9511, 121.1273),419"adya": (13.8764, 121.1387),420"anilao": (13.9062, 121.1730),421"anilao labac": (13.8916, 121.1858),422"antipolo del norte": (13.9252, 121.1851),423"antipolo del sur": (13.9140, 121.1885),424"bagong pook": (13.9358, 121.1098),425"balintawak": (13.9472, 121.1764),426"banaybanay": (13.9433, 121.1121),427"bolbok": (13.9212, 121.1685),428"bugtong na pulo": (14.0054, 121.1717),429"bulacnin": (13.9854, 121.1414),430"bulaklakan": (13.9417, 121.0978),431"calamias": (13.8641, 121.1552),432"cumba": (13.8582, 121.2185),433"dagatan": (13.9681, 121.1242),434"duhatan": (13.9351, 121.0859),435"halang": (13.9472, 121.0826),436"inosloban": (13.9888, 121.1710),437"kayumanggi": (13.9258, 121.1601),438"latag": (13.9365, 121.1843),439"lodlod": (13.9306, 121.1415),440"lumbang": (13.9842, 121.2006),441"mabini": (13.9184, 121.1642),442"malagonlong": (13.9116, 121.1564),443"malitlit": (13.9752, 121.2054),444"marawoy": (13.9644, 121.1657),445"mataas na lupa": (13.9419, 121.1501),446"munting pulo": (13.9519, 121.1850),447"pagolingin bata": (13.8824, 121.1625),448"pagolingin east": (13.8781, 121.1754),449"pagolingin west": (13.8852, 121.1703),450"pangao": (13.9125, 121.1458),451"pinagkawitan": (13.8989, 121.1962),452"pinagtongulan": (13.9297, 121.0946),453"plaridel": (14.0125, 121.1552),454"pusil": (13.9558, 121.1354),455"quezon": (13.8752, 121.1452),456"rizal": (13.8654, 121.1402),457"sabang": (13.9461, 121.1669),458"sampaguita": (13.9142, 121.1440),459"san benito": (13.9352, 121.2305),460"san carlos": (13.9592, 121.1824),461"san celestino": (13.9152, 121.2354),462"san francisco": (13.8905, 121.2302),463"san guillermo": (13.8702, 121.1854),464"sapac": (13.9562, 121.2045),465"san jose": (13.9252, 121.1902),466"san lucas": (14.0058, 121.1452),467"san salvador": (13.9852, 121.1458),468"san sebastian": (13.9422, 121.1621),469"santo nino": (13.9599, 121.2106),470"santo toribio": (13.9037, 121.2223),471"sico": (13.9434, 121.1279),472"talisay": (13.9652, 121.2258),473"tambo": (13.9432, 121.1352),474"tangob": (13.9058, 121.1152),475"tanguay": (13.9452, 121.1258),476"tibig": (13.9552, 121.1952),477"tipacan": (13.9152, 121.2152),478"sm city lipa": (13.9558, 121.1642),479"robinsons place lipa": (13.9388, 121.1610),480"the outlets at lima": (14.0094, 121.1685),481"lipa city hall": (13.9565, 121.1510),482"san sebastian cathedral": (13.9416, 121.1614),483"lipa city public market": (13.9372, 121.1585),484"basilio fernando air base": (13.9544, 121.1244),485"lipa grand terminal": (13.9575, 121.1645),486// --- POBLACION (City Proper) ---487 "barangay 1": [13.9404, 121.1579], "barangay 2": [13.9421, 121.1625],488 "barangay 3": [13.9439, 121.1608], "barangay 4": [13.9405, 121.1621],489 "barangay 5": [13.9392, 121.1633], "barangay 6": [13.9412, 121.1645],490 "barangay 7": [13.9432, 121.1668], "barangay 8": [13.9452, 121.1685],491 "barangay 9": [13.9428, 121.1644], "barangay 10": [13.9418, 121.1690],492 "barangay 11": [13.9405, 121.1680], "barangay 12": [13.9511, 121.1273],493 494 // --- RURAL & SUBURBAN BARANGAYS ---495 "adya": [13.8764, 121.1387],496 "anilao": [13.9062, 121.1730], "anilao-labac": [13.8916, 121.1858],497 "antipolo del norte": [13.9285, 121.1676], "antipolo del sur": [13.9140, 121.1885],498 "bagong pook": [13.9358, 121.1098], "balintawak": [13.9472, 121.1764],499 "banaybanay": [13.9433, 121.1121], "bolbok": [13.9212, 121.1685],500 "bugtong na pulo": [14.0054, 121.1717], "bulacnin": [13.9854, 121.1414],501 "bulaklakan": [13.9417, 121.0978], "calamias": [13.8641, 121.1552],502 "cumba": [13.8582, 121.2185], "dagatan": [13.9681, 121.1242],503 "duhatan": [13.9351, 121.0859], "halang": [13.9472, 121.0826],504 "inosloban": [13.9888, 121.1710], "kayumanggi": [13.9258, 121.1601],505 "latag": [13.9365, 121.1843], "lodlod": [13.9306, 121.1415],506 "lumbang": [13.9842, 121.2006], "mabini": [13.9184, 121.1642],507 "malagonlong": [13.9116, 121.1564], "malitlit": [13.9752, 121.2054],508 "marawoy": [13.9644, 121.1657], "mataas na lupa": [13.9419, 121.1501],509 "munting pulo": [13.9519, 121.1850], "pagolingin bata": [13.8824, 121.1625],510 "pagolingin east": [13.8781, 121.1754], "pagolingin west": [13.8852, 121.1703],511 "pangao": [13.9125, 121.1458], "pinagkawitan": [13.8989, 121.1962],512 "pinagtongulan": [13.9297, 121.0946], "plaridel": [14.0125, 121.1552],513 "pusil": [13.9558, 121.1354], "quezon": [13.8752, 121.1452],514 "rizal": [13.8654, 121.1402], "sabang": [13.9461, 121.1669],515 "sampaguita": [13.9142, 121.1440], "san benito": [13.9352, 121.2305],516 "san carlos": [13.9485, 121.1473], "san celestino": [13.9152, 121.2354],517 "san francisco": [13.8905, 121.2302], "san guillermo": [13.8702, 121.1854],518 "san isidro": [13.9600, 121.2000], "san jose": [13.9252, 121.1902],519 "san lucas": [14.0058, 121.1452], "san salvador": [13.9852, 121.1458],520 "san sebastian": [13.9422, 121.1621], "santa cruz": [13.9830, 121.2160],521 "santo nino": [13.9599, 121.2106], "santo toribio": [13.9037, 121.2223],522 "sapac": [13.9534, 121.2025], "sico": [13.9434, 121.1279],523 "talisay": [13.9652, 121.2258], "tambo": [13.9432, 121.1352],524 "tangob": [13.9058, 121.1152], "tanguay": [13.9452, 121.1258],525 "tibig": [13.9552, 121.1952], "tipacan": [13.9152, 121.2152],526 527 // --- KEY LANDMARKS ---528 "sm city lipa": [13.9558, 121.1642], "robinsons": [13.9388, 121.1610],529 "city hall": [13.9565, 121.1510], "cathedral": [13.9416, 121.1614],530 "public market": [13.9372, 121.1585], "air base": [13.9544, 121.1244],531 "bus terminal": [13.9575, 121.1645],532 // --- MAJOR MALLS & COMPLEXES ---533 "SM City Lipa": [13.9558, 121.1642],534 "Robinsons Place Lipa": [13.9388, 121.1610],535 "The Outlets at LIMA": [14.0094, 121.1685],536 "Fiesta World Mall": [13.9620, 121.1650],537 "Big Ben Complex": [13.9350, 121.1600],538 "Lipa Town Center": [13.9280, 121.1650],539 "CVC Supermarket": [13.9410, 121.1620],540 "South Supermarket": [13.9480, 121.1550],541 "Puregold Lipa": [13.9320, 121.1580],542 "S&R Membership Shopping": [13.9500, 121.1520],543 "Wilcon Depot": [13.9450, 121.1300],544 "Levitown": [13.9600, 121.1500],545 // --- TRANSPORT & TRANSIT ---546 "Lipa Grand Terminal": [13.9575, 121.1645],547 "SM Transport Terminal": [13.9560, 121.1640],548 "Robinsons Jeepney Stop": [13.9385, 121.1615],549 "Tambo Exit": [13.9432, 121.1352],550 "Balete Exit": [14.0000, 121.1800],551 "Star Tollway Lipa": [13.9450, 121.1300],552 "J.P. Laurel Highway": [13.9450, 121.1550],553 "Ayala Highway": [13.9500, 121.1600],554 "C.M. Recto Avenue": [13.9410, 121.1620],555 "P. Torres Street": [13.9430, 121.1650],556 "M.K. Lina Street": [13.9400, 121.1630],557 "Morada Avenue": [13.9420, 121.1610],558 "Leviste Highway": [14.0050, 121.1600],559 "Banay-Banay Crossing": [13.9433, 121.1121],560 "Sabang Bridge": [13.9460, 121.1670],561 "Marawoy Bridge": [13.9640, 121.1650],562 "Inosloban Junction": [13.9880, 121.1710],563 "Lodlod Bridge": [13.9300, 121.1410],564 // --- GOVERNMENT & SERVICES ---565 "Lipa City Hall": [13.9565, 121.1510],566 "Lipa City Police Station": [13.9560, 121.1520],567 "Bureau of Fire Protection Lipa": [13.9550, 121.1530],568 "LTO Lipa District Office": [13.9600, 121.1700],569 "PhilHealth Lipa": [13.9555, 121.1640],570 "SSS Lipa": [13.9560, 121.1645],571 "Lipa City Public Market": [13.9372, 121.1585],572 "Pamilihang Bayan ng Lipa": [13.9370, 121.1580],573 "City Engineering Office": [13.9570, 121.1500],574 // --- RELIGIOUS ---575 "San Sebastian Cathedral": [13.9416, 121.1614],576 "Our Lady of Mount Carmel Church": [13.9280, 121.1680],577 "Redemptorist Church": [13.9300, 121.1550],578 "Divino Amor Chapel": [13.9310, 121.1560],579 "Iglesia ni Cristo Lipa": [13.9450, 121.1600],580 "Victory Lipa": [13.9390, 121.1610],581 // --- MEDICAL / HOSPITALS ---582 "Mary Mediatrix Medical Center": [13.9485, 121.1525],583 "Lipa Medix Medical Center": [13.9520, 121.1600],584 "Metro Lipa Medical Center": [13.9600, 121.1400],585 "Ospital ng Lipa": [13.9550, 121.1500],586 "N.L. Villa Memorial Medical Center": [13.9400, 121.1620],587 "San Antonio Medical Center": [13.9420, 121.1630],588 "Divine Love General Hospital": [13.9350, 121.1550],589 "Villa De Lipa": [13.9650, 121.1600],590 // --- EDUCATION / SCHOOLS ---591 "De La Salle Lipa": [13.9455, 121.1528],592 "Batangas State University - Lipa": [13.9650, 121.1650],593 "University of Batangas - Lipa": [13.9550, 121.1600],594 "Lipa City Colleges": [13.9400, 121.1600],595 "Canossa Academy Lipa": [13.9430, 121.1550],596 "The Mabini Academy": [13.9410, 121.1610],597 "Stonyhurst Southville International School": [13.9500, 121.1580],598 "Kolehiyo ng Lungsod ng Lipa": [13.9600, 121.1500],599 "STI College Lipa": [13.9380, 121.1620],600 "AMA Computer College Lipa": [13.9550, 121.1630],601 "Golden Gate Colleges": [13.9420, 121.1600],602 // --- FOOD & LIFESTYLE ---603 "Starbucks Lipa": [13.9460, 121.1540],604 "Jollibee Tambo": [13.9430, 121.1350],605 "Jollibee Bayan": [13.9410, 121.1620],606 "McDo Ayala Highway": [13.9500, 121.1600],607 "KFC Big Ben": [13.9350, 121.1600],608 "Chowking Uptown": [13.9550, 121.1640],609 "Mang Inasal Robinsons": [13.9388, 121.1610],610 "Burger King Lipa": [13.9490, 121.1560],611 "Lomi King": [13.9450, 121.1650],612 "Liam's Lomi House": [13.9300, 121.1600],613 "Benok's Lomi": [13.9600, 121.1650],614 "Renfel Lomi House": [13.9400, 121.1700],615 "Casa de Segunda": [13.9380, 121.1600],616 "Museo de Lipa": [13.9420, 121.1610],617 "Summit Point Golf & Country Club": [13.9900, 121.1500],618 "Mount Malarayat Golf & Country Club": [13.9800, 121.1300],619 "Hotel 1925": [13.9500, 121.1400],620 "The Jet Hotel": [13.9550, 121.1550],621 // --- BANKS (ATMs) ---622 "BDO SM Lipa": [13.9558, 121.1642],623 "BPI Lipa Highway": [13.9460, 121.1540],624 "Metrobank Bayan": [13.9410, 121.1620],625 "Landbank Lipa": [13.9420, 121.1610],626 "RCBC Lipa": [13.9450, 121.1550],627 "Chinabank Lipa": [13.9400, 121.1600],628 "PNB Lipa City Hall": [13.9565, 121.1510]629 };630 const State = {631 map: null,632 markers: [],633 posts: [],634 filters: { urgency: 'ALL', type: 'ALL', search: '' }635 };636 637 let currentId = null;638 // --- API WRAPPER ---639 async function api(url, opts={}) {640 const r = await fetch(url, {credentials:'include', headers:{'Content-Type':'application/json'}, ...opts});641 const j = await r.json().catch(()=>({}));642 if(!r.ok) throw new Error(j.error||'Request failed');643 return j;644 }645 // --- INITIALIZATION ---646 document.addEventListener('DOMContentLoaded', async () => {647 initClock();648 initMap();649 setupUI();650 try {651 const me = await api('/api/me');652 if(me.role !== 'admin') { window.location.href = '/login.html'; return; }653 654 await refreshData();655 656 const es = new EventSource('/api/stream');657 es.onmessage = (ev) => {658 const msg = JSON.parse(ev.data);659 if(['new_post', 'update_post'].includes(msg.type)) {660 logSystem(`NEW INTEL PACKET RECEIVED: ID #${msg.post.id}`);661 refreshData();662 }663 };664 document.getElementById('logoutBtn').onclick = async () => {665 await api('/api/auth/logout', {method:'POST'});666 window.location.href='/';667 };668 } catch(e) { console.error(e); }669 });670 // --- MAP SYSTEM ---671 function initMap() {672 if(State.map) return;673 State.map = L.map('map', {zoomControl:false, attributionControl:false}).setView(LIPA_CENTER, 13);674 675 // High-Contrast Dark Tiles676 L.tileLayer('https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', {677 maxZoom: 19, opacity: 0.95678 }).addTo(State.map);679 }680 function renderMap(posts) {681 State.markers.forEach(m => State.map.removeLayer(m));682 State.markers = [];683 posts.forEach(p => {684 if(p.status === 'RESOLVED') return;685 let coords = LIPA_CENTER; // Default686 let locName = (p.location_label || "").toLowerCase();687 let matched = false;688 // Force Pin Logic: Try match, else random offset near center689 for (const [key, val] of Object.entries(GEO_MAP)) {690 if (locName.includes(key)) {691 coords = [val[0] + (Math.random()-0.5)*0.003, val[1] + (Math.random()-0.5)*0.003];692 matched = true;693 break;694 }695 }696 697 if (!matched) {698 // Fallback jitter so it appears SOMEWHERE699 coords = [LIPA_CENTER[0] + (Math.random()-0.5)*0.02, LIPA_CENTER[1] + (Math.random()-0.5)*0.02];700 }701 const u = (p.urgency_level||"").toUpperCase();702 let color = '#00f3ff'; // Low703 let animClass = '';704 if(u === 'MODERATE') color = '#00ff9d';705 if(u === 'HIGH') color = '#ff9500';706 if(u === 'CRITICAL') { color = '#ff1b3d'; animClass = 'fast'; }707 // Tactical Marker708 const icon = L.divIcon({709 className: 'marker-box',710 html: `<div class="pin-box" style="color:${color}">711 <div class="pin-wave ${animClass}"></div>712 <div class="pin-target"></div>713 <div class="pin-dot"></div>714 </div>`,715 iconSize: [30, 30], iconAnchor: [15, 15]716 });717 const pin = L.marker(coords, { icon: icon }).addTo(State.map);718 719 // Popup with full details720 pin.bindPopup(`721 <div style="text-align:center; font-family:'Orbitron', sans-serif; color:#333; font-size:0.85rem; min-width:150px;">722 <strong style="color:${color}; text-transform:uppercase; font-size:0.9rem;">${p.disaster_type}</strong><br>723 <div style="margin:4px 0; font-family:'Inter'; font-weight:600;">${p.location_label || 'Unknown Loc'}</div>724 <span style="background:${color}; color:#000; padding:1px 6px; border-radius:3px; font-size:0.7rem; font-weight:700;">${u}</span>725 </div>726 `);727 728 pin.on('click', () => {729 selectAlert(p);730 const el = document.querySelector(`.card[data-id="${p.id}"]`);731 if(el) el.scrollIntoView({behavior:'smooth', block:'center'});732 });733 State.markers.push(pin);734 });735 }736 // --- DATA ---737 async function refreshData() {738 const data = await api('/api/posts?status=NEW&limit=150');739 State.posts = data.posts || [];740 runFilter();741 }742 function runFilter() {743 const { urgency, type, search } = State.filters;744 const term = search.toLowerCase();745 const filtered = State.posts.filter(p => {746 if(term && !JSON.stringify(p).toLowerCase().includes(term)) return false;747 const u = (p.urgency_level||"").toUpperCase();748 if(urgency !== 'ALL' && u !== urgency) return false;749 const t = (p.disaster_type||"").toUpperCase();750 if(type !== 'ALL' && !t.includes(type)) return false;751 return true;752 });753 renderKPIs(State.posts);754 renderFeed(filtered);755 renderMap(filtered);756 }757 // --- FEED ---758 function renderFeed(posts) {759 const cont = document.getElementById('alerts-scroll');760 cont.innerHTML = '';761 if(posts.length === 0) {762 cont.innerHTML = '<div style="text-align:center; padding:40px; color:#555; font-family:var(--font-code); font-size:0.8rem;">// NO ACTIVE SIGNALS</div>';763 return;764 }765 posts.forEach(p => {766 const u = (p.urgency_level||"NORMAL").toUpperCase();767 let bClass = 'bg-low';768 if(u==='MODERATE') bClass = 'bg-mod';769 if(u==='HIGH') bClass = 'bg-high';770 if(u==='CRITICAL') bClass = 'bg-crit';771 const card = document.createElement('div');772 card.className = 'card';773 card.dataset.id = p.id;774 775 card.innerHTML = `776 <div class="card-head">777 <span class="card-title">${escapeHtml(p.disaster_type)}</span>778 <span class="card-badge ${bClass}">${u}</span>779 </div>780 <div class="card-loc">781 <i class="fa-solid fa-crosshairs"></i> ${escapeHtml(p.location_label || 'Unknown Sector')}782 </div>783 <div class="card-body">${escapeHtml(p.text)}</div>784 <div class="card-foot">785 <span class="card-time">T-${timeAgo(p.created_at)}</span>786 <i class="fa-solid fa-chevron-right" style="color:#444; font-size:0.7rem;"></i>787 </div>788 `;789 790 card.onclick = () => selectAlert(p);791 cont.appendChild(card);792 });793 }794 function selectAlert(p) {795 currentId = p.id;796 document.querySelectorAll('.card').forEach(c => c.classList.remove('active'));797 const activeCard = document.querySelector(`.card[data-id="${p.id}"]`);798 if(activeCard) activeCard.classList.add('active');799 // AI Logic800 let conf = "N/A", aiReason = "Insufficient vector data.";801 if(p.ai_json) {802 try {803 const ai = JSON.parse(p.ai_json);804 if(ai.ml_location_prob) conf = (ai.ml_location_prob * 100).toFixed(2) + "%";805 if(ai.gazetteer_debug) aiReason = "Hybrid Lock: " + JSON.stringify(ai.gazetteer_debug);806 } catch(e){}807 }808 const s = (id, v) => document.getElementById(id).textContent = v;809 s('det-id', `INCIDENT #${p.id}`);810 s('det-cat', p.disaster_type);811 s('det-loc', p.location_label || "Unknown Sector");812 s('det-conf', conf);813 s('det-user', p.author_name || "Citizen Node");814 s('det-ai-reason', aiReason);815 816 const b = document.getElementById('det-badge');817 b.className = 'card-badge';818 const u = (p.urgency_level||"").toUpperCase();819 if(u==='CRITICAL') b.classList.add('bg-crit');820 else if(u==='HIGH') b.classList.add('bg-high');821 else if(u==='MODERATE') b.classList.add('bg-mod');822 else b.classList.add('bg-low');823 b.textContent = u;824 }825 window.act = async (e, id, type) => {826 if(e) e.stopPropagation();827 if(!id) return;828 const verb = type === 'ack' ? 'Acknowledge' : 'Resolve';829 if(!confirm(`Confirm ${verb} for Incident #${id}?`)) return;830 try {831 await api(`/api/posts/${id}/${type}`, {method:'POST'});832 logSystem(`INCIDENT #${id} STATUS: ${verb.toUpperCase()}D`);833 refreshData();834 if(type === 'resolve') {835 currentId = null;836 document.getElementById('det-id').textContent = "NO SELECTION";837 }838 } catch(e) { alert(e.message); }839 };840 function renderKPIs(posts) {841 const count = (u) => posts.filter(p => (p.urgency_level||"").toUpperCase() === u).length;842 document.getElementById('kpi-total').textContent = posts.length;843 document.getElementById('kpi-crit').textContent = count('CRITICAL');844 document.getElementById('kpi-high').textContent = count('HIGH');845 document.getElementById('kpi-locs').textContent = new Set(posts.map(p => p.location_label)).size;846 }847 function logSystem(msg) {848 const log = document.getElementById('sys-log');849 log.textContent = `${new Date().toLocaleTimeString()} // ${msg} --- ` + log.textContent.substring(0, 100);850 }851 function setupUI() {852 document.querySelectorAll('.map-dock .dock-btn').forEach(btn => {853 btn.onclick = () => {854 if(btn.dataset.filter) {855 document.querySelectorAll('[data-filter]').forEach(b => b.classList.remove('active'));856 State.filters.urgency = btn.dataset.filter;857 } else {858 document.querySelectorAll('[data-type]').forEach(b => b.classList.remove('active'));859 State.filters.type = btn.dataset.type;860 }861 btn.classList.add('active');862 runFilter();863 };864 });865 document.getElementById('alert-search').oninput = (e) => {866 State.filters.search = e.target.value;867 runFilter();868 };869 }870 function initClock() {871 setInterval(() => {872 document.getElementById('clock-text').textContent = new Date().toLocaleTimeString('en-US', {hour12:false});873 }, 1000);874 }875 function timeAgo(date) {876 if(!date) return '--';877 const sec = Math.floor((new Date() - new Date(date))/1000);878 if(sec < 60) return `${sec}s`;879 const min = Math.floor(sec/60);880 if(min < 60) return `${min}m`;881 return `${Math.floor(min/60)}h`;882 }883 function escapeHtml(t) { return (t||"").replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">"); }884 </script>885</body>886</html>