CoolFace
Apppublic

Miyaka/alertify-system2

sourceHugging Faceupdated 8mo agoView on Hugging Face
0likes
dashboard.html886 linesDownload Raw Back to public
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, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); }884  </script>885</body>886</html>