[{"data":1,"prerenderedAt":386},["ShallowReactive",2],{"project-lapor-cepat-bpbd":3},{"id":4,"title":5,"body":6,"description":364,"duration":365,"extension":366,"featured":206,"github":367,"image":368,"live":369,"meta":370,"navigation":206,"order":147,"path":371,"role":372,"seo":373,"status":374,"stem":375,"team_size":372,"tech":376,"type":383,"year":384,"__hash__":385},"projects\u002Fproject\u002Flapor-cepat-bpbd.md","LaporCepat: AI-Powered Disaster Reporting",{"type":7,"value":8,"toc":356},"minimark",[9,14,18,22,30,63,67,72,78,84,88,93,96,345,349,352],[10,11,13],"h2",{"id":12},"the-problem","The Problem",[15,16,17],"p",{},"Indonesia is highly disaster-prone, yet existing emergency reporting systems heavily rely on manual phone calls to BPBD command centers. During mass casualty events, dispatchers are overwhelmed, communication is vague, and manual data entry is slow. When a citizen is panicked, forcing them to navigate a clunky web form or wait in a call queue costs critical response time and, ultimately, lives.",[10,19,21],{"id":20},"my-solution","My Solution",[15,23,24,25,29],{},"I engineered ",[26,27,28],"strong",{},"LaporCepat",", a complete overhaul of the emergency reporting pipeline that uses artificial intelligence to completely eliminate form-filling for victims while providing perfect situational awareness for dispatchers.",[31,32,33,40,46,52],"ul",{},[34,35,36,39],"li",{},[26,37,38],{},"Voice-First Triage Pipeline:"," Citizens simply tap a button and speak. The audio is instantly transcribed using Groq Whisper Large V3.",[34,41,42,45],{},[26,43,44],{},"Multimodal Extraction:"," Google Gemini 2.5 Flash analyzes the text transcript alongside uploaded field photos to automatically extract disaster type, victim estimates, severity level (CRITICAL to LOW), and generate survival instructions.",[34,47,48,51],{},[26,49,50],{},"Frictionless Geolocation:"," Automatically captures exact GPS coordinates via browser APIs with an IP-based fallback, preventing location miscommunication.",[34,53,54,57,58,62],{},[26,55,56],{},"Real-Time BPBD Dashboard:"," An operations dashboard for dispatchers powered by Server-Sent Events (SSE) and Firebase ",[59,60,61],"code",{},"onSnapshot"," that streams live reports onto a spatial map without requiring page reloads.",[10,64,66],{"id":65},"technical-deep-dive","Technical Deep Dive",[68,69,71],"h3",{"id":70},"architecture-decisions","Architecture Decisions",[15,73,74,77],{},[26,75,76],{},"Why Voice-First Reporting (Groq Whisper)?","\nUnder severe psychological stress, fine motor skills degrade, making typing difficult. By leveraging Groq's insanely fast inference engine for Whisper Large V3, I allowed victims to simply describe their emergency verbally, converting chaotic audio into perfectly structured text in milliseconds.",[15,79,80,83],{},[26,81,82],{},"Why Upgrade to Nuxt 4?","\nThe project utilizes the bleeding-edge Nuxt 4 framework to leverage its improved module resolution and optimized Nitro server engine. This was crucial for handling the custom Server-Sent Events (SSE) endpoints efficiently on Vercel's serverless environment.",[68,85,87],{"id":86},"key-features-i-built","Key Features I Built",[89,90,92],"h4",{"id":91},"_1-zero-latency-incident-streaming-sse","1. Zero-Latency Incident Streaming (SSE)",[15,94,95],{},"Instead of forcing the dispatcher dashboard to constantly poll the database or implementing heavy WebSockets, I built a lightweight Server-Sent Events (SSE) stream. The Nitro backend listens to Firebase Firestore mutations and pushes raw JSON chunks directly to the frontend's reactive state.",[97,98,103],"pre",{"className":99,"code":100,"language":101,"meta":102,"style":102},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F Real-time unidirectional feed using SSE over HTTP\u002F2\nexport default defineEventHandler((event) => {\n  setHeader(event, \"Content-Type\", \"text\u002Fevent-stream\");\n  setHeader(event, \"Cache-Control\", \"no-cache\");\n  setHeader(event, \"Connection\", \"keep-alive\");\n\n  const unsubscribe = db\n    .collection(\"reports\")\n    .where(\"status\", \"==\", \"PENDING\")\n    .onSnapshot((snapshot) => {\n      \u002F\u002F Push live incident updates directly to the dispatcher's map\n      event.node.res.write(`data: ${JSON.stringify(reports)}\\n\\n`);\n    });\n});\n","typescript","",[59,104,105,114,145,167,184,201,208,224,242,267,285,291,333,339],{"__ignoreMap":102},[106,107,110],"span",{"class":108,"line":109},"line",1,[106,111,113],{"class":112},"sJ8bj","\u002F\u002F Real-time unidirectional feed using SSE over HTTP\u002F2\n",[106,115,117,121,124,128,132,136,139,142],{"class":108,"line":116},2,[106,118,120],{"class":119},"szBVR","export",[106,122,123],{"class":119}," default",[106,125,127],{"class":126},"sScJk"," defineEventHandler",[106,129,131],{"class":130},"sVt8B","((",[106,133,135],{"class":134},"s4XuR","event",[106,137,138],{"class":130},") ",[106,140,141],{"class":119},"=>",[106,143,144],{"class":130}," {\n",[106,146,148,151,154,158,161,164],{"class":108,"line":147},3,[106,149,150],{"class":126},"  setHeader",[106,152,153],{"class":130},"(event, ",[106,155,157],{"class":156},"sZZnC","\"Content-Type\"",[106,159,160],{"class":130},", ",[106,162,163],{"class":156},"\"text\u002Fevent-stream\"",[106,165,166],{"class":130},");\n",[106,168,170,172,174,177,179,182],{"class":108,"line":169},4,[106,171,150],{"class":126},[106,173,153],{"class":130},[106,175,176],{"class":156},"\"Cache-Control\"",[106,178,160],{"class":130},[106,180,181],{"class":156},"\"no-cache\"",[106,183,166],{"class":130},[106,185,187,189,191,194,196,199],{"class":108,"line":186},5,[106,188,150],{"class":126},[106,190,153],{"class":130},[106,192,193],{"class":156},"\"Connection\"",[106,195,160],{"class":130},[106,197,198],{"class":156},"\"keep-alive\"",[106,200,166],{"class":130},[106,202,204],{"class":108,"line":203},6,[106,205,207],{"emptyLinePlaceholder":206},true,"\n",[106,209,211,214,218,221],{"class":108,"line":210},7,[106,212,213],{"class":119},"  const",[106,215,217],{"class":216},"sj4cs"," unsubscribe",[106,219,220],{"class":119}," =",[106,222,223],{"class":130}," db\n",[106,225,227,230,233,236,239],{"class":108,"line":226},8,[106,228,229],{"class":130},"    .",[106,231,232],{"class":126},"collection",[106,234,235],{"class":130},"(",[106,237,238],{"class":156},"\"reports\"",[106,240,241],{"class":130},")\n",[106,243,245,247,250,252,255,257,260,262,265],{"class":108,"line":244},9,[106,246,229],{"class":130},[106,248,249],{"class":126},"where",[106,251,235],{"class":130},[106,253,254],{"class":156},"\"status\"",[106,256,160],{"class":130},[106,258,259],{"class":156},"\"==\"",[106,261,160],{"class":130},[106,263,264],{"class":156},"\"PENDING\"",[106,266,241],{"class":130},[106,268,270,272,274,276,279,281,283],{"class":108,"line":269},10,[106,271,229],{"class":130},[106,273,61],{"class":126},[106,275,131],{"class":130},[106,277,278],{"class":134},"snapshot",[106,280,138],{"class":130},[106,282,141],{"class":119},[106,284,144],{"class":130},[106,286,288],{"class":108,"line":287},11,[106,289,290],{"class":112},"      \u002F\u002F Push live incident updates directly to the dispatcher's map\n",[106,292,294,297,300,302,305,308,311,314,316,319,322,325,328,331],{"class":108,"line":293},12,[106,295,296],{"class":130},"      event.node.res.",[106,298,299],{"class":126},"write",[106,301,235],{"class":130},[106,303,304],{"class":156},"`data: ${",[106,306,307],{"class":216},"JSON",[106,309,310],{"class":156},".",[106,312,313],{"class":126},"stringify",[106,315,235],{"class":156},[106,317,318],{"class":130},"reports",[106,320,321],{"class":156},")",[106,323,324],{"class":156},"}",[106,326,327],{"class":216},"\\n\\n",[106,329,330],{"class":156},"`",[106,332,166],{"class":130},[106,334,336],{"class":108,"line":335},13,[106,337,338],{"class":130},"    });\n",[106,340,342],{"class":108,"line":341},14,[106,343,344],{"class":130},"});\n",[89,346,348],{"id":347},"_2-robust-ai-fallback-mechanism","2. Robust AI Fallback Mechanism",[15,350,351],{},"Emergency systems cannot afford downtime. I designed a failover mechanism in the AI pipeline. If the primary Google Gemini 2.5 Flash model hits a rate limit or exhausts its quota, the system autonomously degrades to Gemini 2.0 Flash to ensure triage continuity without interrupting the citizen's submission process.",[353,354,355],"style",{},"html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":102,"searchDepth":116,"depth":116,"links":357},[358,359,360],{"id":12,"depth":116,"text":13},{"id":20,"depth":116,"text":21},{"id":65,"depth":116,"text":66,"children":361},[362,363],{"id":70,"depth":147,"text":71},{"id":86,"depth":147,"text":87},"A real-time emergency reporting system that replaces manual call centers with an AI pipeline (Groq Whisper STT + Gemini 2.5 Flash) for instant triage and live dispatch.","3 weeks","md","https:\u002F\u002Fgithub.com\u002Fszuryuu\u002Flapor-cepat-bpbd","\u002Fimages\u002Fprojects\u002Fnuxt.png","https:\u002F\u002Flapor-cepat.vercel.app",{},"\u002Fproject\u002Flapor-cepat-bpbd",null,{"title":5,"description":364},"Completed","project\u002Flapor-cepat-bpbd",[377,378,379,380,381,382],"Nuxt 4","Groq Whisper AI","Google Gemini 2.5 Flash","Firebase","SSE","Leaflet","Solo Project","2025","WCGEdHBRjCfKQ2UkC_qXkqu5JCxvxqbfscvPN0l7CxM",1776582962960]