Meta Description" name="description" />
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CRIMEMAP | Safety System</title>
<style>
*{box-sizing:border-box}
body{
margin:0;font-family:Arial,sans-serif;
background:#f1f5f9;color:#10213b
}
header{
background:#071b36;color:white;padding:20px;
text-align:center
}
header h1{margin:0;font-size:28px}
header span{color:#ff4055}
nav{
display:flex;gap:8px;justify-content:center;
flex-wrap:wrap;background:#102b50;padding:12px
}
button{
border:0;border-radius:9px;padding:12px 16px;
cursor:pointer;font-weight:bold;font-size:14px
}
nav button{background:#294568;color:white}
nav button:hover{background:#41668f}
main{max-width:1000px;margin:auto;padding:18px}
.panel{
background:white;padding:20px;border-radius:14px;
margin-bottom:18px;box-shadow:0 3px 12px #0000000c
}
h2{margin-top:0}
.sos{
display:block;width:175px;height:175px;border-radius:50%;
background:#e9233d;color:white;font-size:28px;
border:8px solid #ffd4da;margin:20px auto
}
.sos:active{transform:scale(.96)}
.center{text-align:center}
.muted{color:#64748b;font-size:14px}
.status{padding:12px;background:#eaf2ff;border-radius:8px}
.grid{
display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
gap:14px
}
.stat{background:#edf4ff;padding:15px;border-radius:10px}
.stat strong{font-size:25px;display:block;margin-top:8px}
.action{background:#125fd1;color:white;margin:4px}
.danger{background:#e9233d;color:white}
.green{background:#168754;color:white}
input,select{
width:100%;padding:12px;margin:7px 0 12px;
border:1px solid #cbd5e1;border-radius:8px
}
table{width:100%;border-collapse:collapse;font-size:13px}
th,td{padding:10px 6px;text-align:left;border-bottom:1px solid #e2e8f0}
.tablewrap{overflow-x:auto}
#map{height:260px;border:0;width:100%;border-radius:10px}
.hidden{display:none}
footer{text-align:center;padding:20px;color:#64748b}
@media(max-width:520px){
main{padding:10px}
.panel{padding:14px}
.grid{grid-template-columns:1fr 1fr}
nav button{padding:10px}
}
</style>
</head>
<body>
<header>
<h1>🛡️ CRIME<span>MAP</span></h1>
<p>Citizen Safety & Police Response System</p>
</header>
<nav>
<button onclick="showPage('home')">Citizen SOS</button>
<button onclick="showPage('history')">Incident History</button>
<button onclick="showPage('police')">Police Dashboard</button>
</nav>
<main>
<section id="home">
<div class="panel center">
<h2>Emergency Assistance</h2>
<p class="muted">Press SOS to create a demonstration alert.</p>
<button class="sos" onclick="sendSOS()">🚨<br>SOS</button>
<div id="status" class="status" aria-live="polite">
Ready. No alert has been created.
</div>
<p id="location" class="muted">Location not yet captured.</p>
<button class="action" onclick="getLocation()">📍 Get GPS Location</button>
<button class="danger" onclick="call112()">📞 Call 112</button>
</div>
<div class="panel">
<h2>My Location</h2>
<p class="muted">The map needs an internet connection.</p>
<iframe id="map" title="Map"
src="https://www.openstreetmap.org/export/embed.html?bbox=76%2C8%2C81%2C13&layer=mapnik">
</iframe>
<button class="action" onclick="openMap()">Open Map</button>
</div>
<div class="panel">
<h2>Offline Status</h2>
<p id="network">Checking connection...</p>
<p class="muted">
Alerts are saved on this device and can be retried
when internet returns. Local storage is not police delivery.
</p>
<button class="action" onclick="retryAlerts()">Retry Pending Alerts</button>
</div>
</section>
<section id="history" class="hidden">
<div class="panel">
<h2>Incident History</h2>
<button class="action" onclick="renderIncidents()">Refresh</button>
<div id="historyList"></div>
</div>
</section>
<section id="police" class="hidden">
<div class="panel">
<h2>👮 Police Dashboard</h2>
<p class="muted">DEMO ONLY — sample incidents are stored in this browser.</p>
<div class="grid">
<div class="stat">Total Incidents<strong id="total">0</strong></div>
<div class="stat">Pending<strong id="pending">0</strong></div>
<div class="stat">Acknowledged<strong id="acked">0</strong></div>
<div class="stat">Escalated<strong id="escalated">0</strong></div>
</div>
</div>
<div class="panel">
<h2>Incident Management</h2>
<div class="tablewrap">
<table>
<thead>
<tr><th>ID / Time</th><th>Location</th><th>Status</th><th>Action</th></tr>
</thead>
<tbody id="incidentRows"></tbody>
</table>
</div>
</div>
<div class="panel">
<h2>Escalation Timer Demo</h2>
<label for="timerMinutes">Escalate unacknowledged incidents after (minutes)</label>
<select id="timerMinutes">
<option value="1">1 minute (quick test)</option>
<option value="10" selected>10 minutes</option>
<option value="20">20 minutes</option>
</select>
<p class="muted">
This browser demo checks timers while the page is open.
Real escalation needs a secure server and background jobs.
</p>
</div>
</section>
</main>
<footer>CRIMEMAP • Student Project Prototype • Not an official emergency service</footer>
<script>
const KEY = "crimemap_demo_incidents";
let gps = null;
function getIncidents(){
try { return JSON.parse(localStorage.getItem(KEY) || "[]"); }
catch(e){ return []; }
}
function saveIncidents(items){
localStorage.setItem(KEY, JSON.stringify(items));
}
function showPage(page){
["home","history","police"].forEach(id=>{
document.getElementById(id).classList.toggle("hidden",id!==page);
});
renderAll();
}
function getLocation(){
const out=document.getElementById("location");
if(!navigator.geolocation){
out.textContent="GPS is not supported by this browser.";
return;
}
out.textContent="Requesting your location...";
navigator.geolocation.getCurrentPosition(pos=>{
gps={
lat:pos.coords.latitude,
lon:pos.coords.longitude,
accuracy:Math.round(pos.coords.accuracy),
time:new Date().toISOString()
};
out.textContent=`Latitude: ${gps.lat.toFixed(5)}, Longitude: ${gps.lon.toFixed(5)} (accuracy approx. ${gps.accuracy} m)`;
document.getElementById("map").src=
"https://www.openstreetmap.org/export/embed.html?bbox="+
(gps.lon-0.01)+","+(gps.lat-0.01)+","+
(gps.lon+0.01)+","+(gps.lat+0.01)+
"&layer=mapnik&marker="+gps.lat+","+gps.lon;
},err=>{
out.textContent="Could not get location. Check permission and GPS settings.";
},{enableHighAccuracy:true,timeout:15000,maximumAge:0});
}
function sendSOS(){
if(!confirm("Create a DEMO SOS alert on this device? This will not contact police."))return;
const items=getIncidents();
const incident={
id:"CM-"+Date.now(),
time:new Date().toISOString(),
lat:gps?gps.lat:null,
lon:gps?gps.lon:null,
accuracy:gps?gps.accuracy:null,
locationTime:gps?gps.time:null,
status:"Pending",
createdAt:Date.now(),
escalated:false
};
items.unshift(incident);
saveIncidents(items);
document.getElementById("status").textContent=
"Demo alert saved. It has NOT been sent to police.";
renderAll();
}
function call112(){
if(confirm("Call India's emergency number 112? Use only for a genuine emergency.")){
window.location.href="tel:112";
}
}
function openMap(){
if(gps){
window.open("https://www.openstreetmap.org/?mlat="+gps.lat+
"&mlon="+gps.lon+"#map=16/"+gps.lat+"/"+gps.lon,"_blank");
}else{
window.open("https://www.openstreetmap.org/","_blank");
}
}
function retryAlerts(){
if(!navigator.onLine){
alert("No internet. The alerts remain saved on this device.");
return;
}
alert("Internet is available, but no server is connected yet. Alerts have NOT been transmitted.");
}
function updateNetwork(){
document.getElementById("network").textContent=
navigator.onLine?"🟢 Internet connection detected":"🔴 Offline — local storage only";
}
window.addEventListener("online",updateNetwork);
window.addEventListener("offline",updateNetwork);
function renderIncidents(){
const items=getIncidents();
const el=document.getElementById("historyList");
if(!items.length){el.innerHTML="<p>No incidents yet.</p>";return;}
el.innerHTML=items.map(i=>`
<div class="stat" style="margin:10px 0">
<b>${i.id}</b><p>${new Date(i.time).toLocaleString()}</p>
<p>Status: ${i.status}</p>
<p>${i.lat===null?"Location unavailable":i.lat.toFixed(5)+", "+i.lon.toFixed(5)}</p>
</div>`).join("");
}
function setStatus(id,status){
const items=getIncidents();
const item=items.find(i=>i.id===id);
if(!item)return;
item.status=status;
item.updatedAt=Date.now();
saveIncidents(items);
renderAll();
}
function renderPolice(){
const items=getIncidents();
document.getElementById("total").textContent=items.length;
document.getElementById("pending").textContent=
items.filter(i=>i.status==="Pending").length;
document.getElementById("acked").textContent=
items.filter(i=>i.status==="Acknowledged").length;
document.getElementById("escalated").textContent=
items.filter(i=>i.escalated).length;
document.getElementById("incidentRows").innerHTML=items.map(i=>`
<tr>
<td>${i.id}<br>${new Date(i.time).toLocaleTimeString()}</td>
<td>${i.lat===null?"Unavailable":i.lat.toFixed(3)+", "+i.lon.toFixed(3)}</td>
<td>${i.escalated?"ESCALATED":i.status}</td>
<td>
<button class="green" onclick="setStatus('${i.id}','Acknowledged')">Acknowledge</button>
<button class="action" onclick="setStatus('${i.id}','Resolved')">Resolve</button>
</td>
</tr>`).join("") || "<tr><td colspan='4'>No incidents recorded.</td></tr>";
}
function checkEscalation(){
const items=getIncidents();
const limit=Number(document.getElementById("timerMinutes").value)*60000;
let changed=false;
items.forEach(i=>{
if(i.status==="Pending" && !i.escalated &&
Date.now()-i.createdAt>=limit){
i.escalated=true;
i.escalatedAt=Date.now();
changed=true;
}
});
if(changed)saveIncidents(items);
renderPolice();
}
function renderAll(){
renderIncidents();
renderPolice();
updateNetwork();
}
document.getElementById("timerMinutes").addEventListener("change",checkEscalation);
renderAll();
setInterval(checkEscalation,5000);
</script>
</body>
</html>
13
4
402KB
1499KB
245.0ms
300.0ms
896.0ms