📱 Instala Camuy Alerta
Accede desde tu pantalla de inicio

🏘️

Portal Ciudadano

Reporta vehículos, querellas y propiedades en tu comunidad

🏛️

Personal Municipal

Administradores e inspectores del municipio

Portal Ciudadano

Inicia sesión para reportar

Nuevo Registro

Crea tu cuenta en Camuy Alerta

🏘️ PORTAL CIUDADANO

📭

No tienes reportes aún

Personal Municipal

Camuy — Gestión de Casos
0
Total
0
Pendiente
0
En Proceso
0
Resuelto

🚗 VEHÍCULOS

0
total
0 pend
0 proc
0 res

📋 QUERELLAS

0
total
0 abierta
0 proc
0 res

🏚️ PROPIEDADES

0
total
0 activo
0 proc
0 res

ACTIVIDAD RECIENTE

Cargando...

🔍 MIS CASOS

'; }); con.innerHTML=h; }).catch(function(e){con.innerHTML='
Error: '+e.message+'
';}); } // ── CITIZEN SUBMIT VEHICLE ──────────────────────────────────── function submitVeh(){ if(!cRes){T('Error','Debes iniciar sesión','r');return;} var vt=v('NR_vt'),vc=v('NR_vco'),ad=v('NR_ad').trim(); var err=document.getElementById('cVehErr');err.style.display='none'; if(!vt){err.textContent='Selecciona el tipo.';err.style.display='block';return;} if(!vc){err.textContent='Selecciona la condición.';err.style.display='block';return;} if(!ad){err.textContent='Ingresa la dirección.';err.style.display='block';return;} if(!db){err.textContent='Sin conexión.';err.style.display='block';return;} var btn=document.getElementById('NR_btn');btn.disabled=true;btn.textContent='Enviando...';loader(true); nextID('CAM').then(function(cid){ var co={ id:cid,dateStr:dStr(),timeStr:tStr(), vehicleType:vt,color:v('NR_vc')||'N/E',plate:v('NR_vp')||'N/D', make:'',condition:vc,address:ad, barrio:v('NR_ba')||'N/E',coords:v('NR_co'),timeLeft:v('NR_ti'), reporter:cRes.nombre+' '+cRes.apellido,phone:cRes.telefono, comments:v('NR_cm'),status:'Pendiente',assignedTo:'Sin asignar', createdBy:'Portal Ciudadano',residenteUsuario:cRes.usuario, history:[{type:'Reporte Ciudadano',icon:'ciudadano', notes:'Enviado por '+cRes.nombre+' '+cRes.apellido+' — Portal Ciudadano.', status:'Pendiente',ts:nowStr(),by:cRes.nombre+' '+cRes.apellido}] }; return saveCase(co).then(function(){ savePhLocal(cid,photos.veh);photos.veh=[];renderPh('veh','cVehPrev'); return cid; }); }).then(function(cid){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR REPORTE'; emailNuevoReporte(co.vehicleType,cid,co.address,co.reporter,'Vehículo Abandonado'); ['NR_vt','NR_vc','NR_vco','NR_ad','NR_ba','NR_co','NR_ti','NR_cm'].forEach(function(id){sv(id);}); document.getElementById('GPS').style.display='none'; document.getElementById('cVehSucID').textContent=cid; document.getElementById('cVehSuc').style.display='block'; document.getElementById('NR_btn').style.display='none'; T('Reporte enviado',cid,'g'); }).catch(function(e){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR REPORTE'; err.textContent='Error: '+e.message;err.style.display='block'; }); } // ── CITIZEN SUBMIT QUERELLA ─────────────────────────────────── function submitQue(){ if(!cRes){T('Error','Debes iniciar sesión','r');return;} var tipo=v('CQ_tipo'),sev=v('CQ_sev'),addr=v('CQ_addr').trim(),desc=v('CQ_desc').trim(); var err=document.getElementById('cQueErr');err.style.display='none'; if(!tipo){err.textContent='Selecciona el tipo de violación.';err.style.display='block';return;} if(!sev){err.textContent='Selecciona la severidad.';err.style.display='block';return;} if(!addr){err.textContent='Ingresa la dirección.';err.style.display='block';return;} if(!desc){err.textContent='Describe la situación.';err.style.display='block';return;} if(!db){err.textContent='Sin conexión.';err.style.display='block';return;} var btn=document.getElementById('CQ_btn');btn.disabled=true;btn.textContent='Enviando...';loader(true); nextID('QUE').then(function(qid){ var data={ id:qid,dateStr:dStr(),timeStr:tStr(), tipo:tipo,severidad:sev,articulo:'', address:addr,barrio:v('CQ_barr'),parcela:v('CQ_parc'), reporter:cRes.nombre+' '+cRes.apellido,phone:cRes.telefono, description:desc,assignedTo:'Sin asignar', status:'Abierta',createdBy:'Portal Ciudadano', residenteUsuario:cRes.usuario, history:[{type:'Querella Ciudadana',icon:'ciudadano', notes:'Enviada por '+cRes.nombre+' '+cRes.apellido+' — Portal Ciudadano.', status:'Abierta',ts:nowStr(),by:cRes.nombre+' '+cRes.apellido}] }; return saveDoc('querellas',qid,data).then(function(){ savePhLocal(qid,photos.que);photos.que=[];renderPh('que','cQuePrev'); return qid; }); }).then(function(qid){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR QUERELLA'; emailNuevoReporte(tipo,qid,addr,cRes.nombre+' '+cRes.apellido,'Querella'); ['CQ_tipo','CQ_sev','CQ_addr','CQ_barr','CQ_parc','CQ_desc'].forEach(function(id){sv(id);}); document.getElementById('cQueSucID').textContent=qid; document.getElementById('cQueSuc').style.display='block'; document.getElementById('CQ_btn').style.display='none'; T('Querella enviada',qid,'g'); }).catch(function(e){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR QUERELLA'; err.textContent='Error: '+e.message;err.style.display='block'; }); } // ── CITIZEN SUBMIT PROPIEDAD ────────────────────────────────── function submitProp(){ if(!cRes){T('Error','Debes iniciar sesión','r');return;} var tipo=v('CP_tipo'),addr=v('CP_addr').trim(),desc=v('CP_desc').trim(); var err=document.getElementById('cPropErr');err.style.display='none'; if(!tipo){err.textContent='Selecciona el tipo de problema.';err.style.display='block';return;} if(!addr){err.textContent='Ingresa la dirección.';err.style.display='block';return;} if(!desc){err.textContent='Describe el problema.';err.style.display='block';return;} if(!db){err.textContent='Sin conexión.';err.style.display='block';return;} var btn=document.getElementById('CP_btn');btn.disabled=true;btn.textContent='Enviando...';loader(true); nextID('PROP').then(function(pid){ var data={ id:pid,dateStr:dStr(),timeStr:tStr(), tipo:tipo,severidad:'Media', address:addr,barrio:v('CP_barr'), catastro:v('CP_cat'),dueno:v('CP_dueno'), area:'',coords:'',telDueno:'',dirDueno:'',emailDueno:'', description:desc,assignedTo:'Sin asignar', status:'Activo',createdBy:'Portal Ciudadano', residenteUsuario:cRes.usuario, history:[{type:'Reporte Ciudadano',icon:'ciudadano', notes:'Reportado por '+cRes.nombre+' '+cRes.apellido+' — Portal Ciudadano.', status:'Activo',ts:nowStr(),by:cRes.nombre+' '+cRes.apellido}] }; return saveDoc('propiedades',pid,data).then(function(){ savePhLocal(pid,photos.prop);photos.prop=[];renderPh('prop','cPropPrev'); return pid; }); }).then(function(pid){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR REPORTE'; emailNuevoReporte(tipo,pid,addr,cRes.nombre+' '+cRes.apellido,'Propiedad'); ['CP_tipo','CP_addr','CP_barr','CP_cat','CP_dueno','CP_desc'].forEach(function(id){sv(id);}); document.getElementById('cPropSucID').textContent=pid; document.getElementById('cPropSuc').style.display='block'; document.getElementById('CP_btn').style.display='none'; T('Propiedad reportada',pid,'g'); }).catch(function(e){ loader(false);btn.disabled=false;btn.textContent='➤ ENVIAR REPORTE'; err.textContent='Error: '+e.message;err.style.display='block'; }); } // ── GPS ─────────────────────────────────────────────────────── function doGPS(){ var el=document.getElementById('GPS'); el.className='gps';el.style.display='block';el.textContent='Obteniendo ubicación...'; if(!navigator.geolocation){el.className='gps e';el.textContent='GPS no disponible.';return;} navigator.geolocation.getCurrentPosition(function(pos){ var lat=pos.coords.latitude.toFixed(6),lng=pos.coords.longitude.toFixed(6); document.getElementById('NR_co').value=lat+', '+lng; el.className='gps k';el.textContent='✓ '+lat+', '+lng; },function(){el.className='gps e';el.textContent='No se pudo obtener GPS.';},{timeout:8000}); } // ── INSPECTOR ───────────────────────────────────────────────── function iTab(t){ document.getElementById('iTabCasos').style.display=t==='casos'?'block':'none'; document.getElementById('iTabGestion').style.display=t==='gestion'?'block':'none'; document.getElementById('itab-casos').classList.toggle('on',t==='casos'); var ig=document.getElementById('itab-gestion'); if(ig)ig.classList.toggle('on',t==='gestion'); } function loadInspCases(){ if(!db||!cUser)return; loader(true); db.collection('casos').where('assignedTo','==',cUser.name).orderBy('timestamp','desc').get() .then(function(snap){ loader(false);iCasesData=[]; snap.forEach(function(d){iCasesData.push(Object.assign({id:d.id},d.data()));}); if(!iCasesData.length){ document.getElementById('iCases').innerHTML='

No tienes casos asignados

'; return; } var h=''; iCasesData.forEach(function(c){ var ph=loadPhLocal(c.id); h+='
'+ '
'+ '
'+c.id+'
'+ '
'+c.vehicleType+' '+c.color+'
'+ '
📍 '+c.address+', '+c.barrio+'
'+ '
'+bdg(c.status)+ '
'+ (ph.length?'
📷 '+ph.length+' foto(s)
':'')+ '
Toca para gestionar →
'+ '
'; }); document.getElementById('iCases').innerHTML=h; }).catch(function(e){loader(false);T('Error',e.message,'r');}); } function openInspCase(cid){ var co=iCasesData.find(function(c){return c.id===cid;});if(!co)return; var ph=loadPhLocal(cid); var hh=(co.history||[]).map(function(x){ return '
'+ '
'+x.type+'
'+x.notes+'
'+ '
'+x.ts+(x.by?' · '+x.by+'':'')+'
'+ '
'; }).join(''); var ats=ATYPES.map(function(a){ return ''; }).join(''); var con=document.getElementById('iGestCase'); con.innerHTML=''; var bkBtn=document.createElement('button'); bkBtn.style.cssText='display:flex;align-items:center;gap:5px;padding:7px 12px;background:var(--navy);color:var(--gold);border:none;font-family:var(--fh);font-size:11px;letter-spacing:2px;margin-bottom:11px;cursor:pointer'; bkBtn.textContent='← MIS CASOS'; bkBtn.onclick=function(){iTab('casos');}; con.appendChild(bkBtn); var card=document.createElement('div'); card.style.cssText='background:#fff;border:1px solid var(--border);border-left:4px solid var(--gold);padding:13px'; card.innerHTML= '
'+ '
'+co.id+'
'+co.vehicleType+' '+co.color+'
'+ bdg(co.status)+ '
'+ '
'+ '
'+(co.plate||'N/D')+'
'+ '
'+co.condition+'
'+ '
'+co.address+'
'+ '
'+co.barrio+'
'+ '
'+co.reporter+'
'+ '
'+(co.phone||'N/D')+'
'+ '
'+ (co.coords?'📍 Ver en Google Maps':'')+ (ph.length?'
'+ ph.map(function(src){return '
';}).join('')+'
':'')+ '
'+hh+'
'+ '

REGISTRAR GESTIÓN

'+ '
'+ats+'
'+ ''+ '
'+ ''+ ''+ '
'+ '
'+ ''+ '
'+ ''+ '

Agregar fotos de evidencia

'+ '
'+ '
'+ '
'+ ''+ '
'; con.appendChild(card); var phInp=document.getElementById('iph_'+cid); if(phInp)phInp.onchange=function(){addInspPh(cid,this);}; document.getElementById('itab-gestion').style.display='block'; iTab('gestion'); } // ── PHOTO ENGINE ────────────────────────────────────────────── function addPh(ctx,input){ var arr=photos[ctx]||[]; Array.from(input.files).slice(0,5-arr.length).forEach(function(file){ var r=new FileReader(); r.onload=function(e){arr.push(e.target.result);photos[ctx]=arr;renderPh(ctx,ctx==='veh'?'cVehPrev':ctx==='que'?'cQuePrev':ctx==='prop'?'cPropPrev':'mNewPrev');}; r.readAsDataURL(file); }); input.value=''; } function addInspPh(cid,input){ if(!photos.insp[cid])photos.insp[cid]=[]; Array.from(input.files).slice(0,5-photos.insp[cid].length).forEach(function(file){ var r=new FileReader(); r.onload=function(e){photos.insp[cid].push(e.target.result);renderInspPh(cid);}; r.readAsDataURL(file); }); input.value=''; } function renderPh(ctx,prevId){ var arr=photos[ctx]||[]; var prev=document.getElementById(prevId);if(!prev)return; prev.innerHTML=arr.map(function(src,i){ return '
'+ '
'; }).join(''); } function renderInspPh(cid){ var arr=photos.insp[cid]||[]; var prev=document.getElementById('iprev_'+cid);if(!prev)return; prev.innerHTML=arr.map(function(src,i){ return '
'+ '
'; }).join(''); } function rmPh(ctx,idx,prevId){photos[ctx].splice(idx,1);renderPh(ctx,prevId);} function rmInspPh(cid,idx){if(photos.insp[cid])photos.insp[cid].splice(idx,1);renderInspPh(cid);} function savePhLocal(id,arr){try{if(arr&&arr.length>0)localStorage.setItem('ph_'+id,JSON.stringify(arr));}catch(e){}} function loadPhLocal(id){try{var v=localStorage.getItem('ph_'+id);return v?JSON.parse(v):[];}catch(e){return[];}} function lbOpen(src){document.getElementById('lbox-img').src=src;document.getElementById('lbox').className='on';} function lbClose(){document.getElementById('lbox').className='';document.getElementById('lbox-img').src='';} document.getElementById('lbox').addEventListener('click',function(e){if(e.target===this)lbClose();}); // ── MAP ─────────────────────────────────────────────────────── var map=null,mapInited=false,mapMkrs=[]; function initMap(){ if(mapInited||typeof L==='undefined')return; mapInited=true; map=L.map('mapContainer',{center:[18.4845,-66.8424],zoom:13}); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{ attribution:'© OpenStreetMap',maxZoom:19 }).addTo(map); renderMapMkrs(); } function renderMapMkrs(){ if(!map)return; mapMkrs.forEach(function(m){map.removeLayer(m);});mapMkrs=[]; var cols={Pendiente:'#e8a020','En Proceso':'#1a3a6b',Resuelto:'#27ae60',Cancelado:'#8a9bb0'}; var wGPS=cases.filter(function(c){return c.coords&&c.coords.indexOf(',')>-1;}); var stats=document.getElementById('mapStats'); if(stats){ var p=wGPS.filter(function(c){return c.status==='Pendiente';}).length; var pr=wGPS.filter(function(c){return c.status==='En Proceso';}).length; var r=wGPS.filter(function(c){return c.status==='Resuelto';}).length; stats.innerHTML= '
'+wGPS.length+'
CON GPS
'+ '
'+p+'
PENDIENTES
'+ '
'+pr+'
EN PROCESO
'+ '
'+r+'
RESUELTOS
'; } wGPS.forEach(function(c){ var pts=c.coords.split(','); var lat=parseFloat(pts[0]),lng=parseFloat(pts[1]); if(isNaN(lat)||isNaN(lng))return; var col=cols[c.status]||'#8a9bb0'; var pulse=c.status==='Pendiente'?'animation:pulse 1.5s infinite;':''; var mk=L.marker([lat,lng],{icon:L.divIcon({ html:'
🚗
', className:'',iconSize:[30,30],iconAnchor:[15,15] })}); mk.bindPopup('
'+c.id+'
'+c.vehicleType+' '+c.color+'
📍 '+c.address+'
'+c.status+'
',{maxWidth:240}); mk.addTo(map);mapMkrs.push(mk); }); if(mapMkrs.length>0){var g=L.featureGroup(mapMkrs);map.fitBounds(g.getBounds().pad(0.1));} } // ── QR ──────────────────────────────────────────────────────── function showQR(){ document.getElementById('qrModal').classList.add('on'); setTimeout(function(){ var el=document.getElementById('qrCanvas'); if(!el)return; el.innerHTML=''; if(typeof QRCode==='undefined'){T('Error','Librería QR no disponible','r');return;} new QRCode(el,{text:'https://camuyalerta.pages.dev',width:220,height:220, colorDark:'#0a1628',colorLight:'#ffffff',correctLevel:QRCode.CorrectLevel.H}); },200); } function closeQR(){document.getElementById('qrModal').classList.remove('on');} document.getElementById('qrModal').addEventListener('click',function(e){if(e.target===this)closeQR();}); function downloadQR(){ var el=document.getElementById('qrCanvas');if(!el)return; var img=el.querySelector('img'),qrc=el.querySelector('canvas'); var src=img?img.src:(qrc?qrc.toDataURL():null); if(!src){T('Error','Genera el QR primero','r');return;} var pc=document.createElement('canvas');pc.width=300;pc.height=380; var ctx=pc.getContext('2d'); ctx.fillStyle='#ffffff';ctx.fillRect(0,0,300,380); ctx.fillStyle='#0a1628';ctx.fillRect(0,0,300,60); ctx.fillStyle='#e8a020';ctx.font='bold 20px Arial';ctx.textAlign='center';ctx.fillText('CAMUY ALERTA',150,26); ctx.fillStyle='#ffffff';ctx.font='11px Arial';ctx.fillText('Reporta un vehiculo abandonado',150,46); var qi=new Image();qi.onload=function(){ ctx.drawImage(qi,40,68,220,220); ctx.fillStyle='#0a1628';ctx.font='bold 13px monospace';ctx.fillText('camuyalerta.pages.dev',150,312); ctx.fillStyle='#8a9bb0';ctx.font='11px Arial';ctx.fillText('Municipio de Camuy, Puerto Rico',150,334); ctx.fillText('Escanea con la camara de tu telefono',150,352); ctx.fillStyle='#e8a020';ctx.fillRect(30,365,240,3); var lnk=document.createElement('a');lnk.download='CamuyAlerta_QR.png';lnk.href=pc.toDataURL('image/png');lnk.click(); T('QR descargado','','g'); };qi.src=src; } function printQR(){ var el=document.getElementById('qrCanvas');if(!el)return; var img=el.querySelector('img'),qrc=el.querySelector('canvas'); var src=img?img.src:(qrc?qrc.toDataURL():null); if(!src){T('Error','Genera el QR primero','r');return;} var w=window.open('','_blank'); w.document.write('Camuy Alerta QR'+ '

CAMUY ALERTA

'+ '
camuyalerta.pages.dev
'+ '

Escanea este código con la cámara de tu teléfono para reportar vehículos abandonados en Camuy.

'+ '
Municipio de Camuy · +1 787-898-2160
'+ '
'+ ''); w.document.close(); setTimeout(function(){w.print();},500); } // ════════════════════════════════════════════════════════════ .fillRect(0,0,300,380); ctx.fillStyle='#0a1628';ctx.fillRect(0,0,300,60); ctx.fillStyle='#e8a020';ctx.font='bold 20px Arial';ctx.textAlign='center';ctx.fillText('CAMUY ALERTA',150,26); ctx.fillStyle='#ffffff';ctx.font='11px Arial';ctx.fillText('Reporta un vehiculo abandonado',150,46); var qi=new Image();qi.onload=function(){ ctx.drawImage(qi,40,68,220,220); ctx.fillStyle='#0a1628';ctx.font='bold 13px monospace';ctx.fillText('camuyalerta.pages.dev',150,312); ctx.fillStyle='#8a9bb0';ctx.font='11px Arial';ctx.fillText('Municipio de Camuy, Puerto Rico',150,334); ctx.fillText('Escanea con la camara de tu telefono',150,352); ctx.fillStyle='#e8a020';ctx.fillRect(30,365,240,3); var lnk=document.createElement('a');lnk.download='CamuyAlerta_QR.png';lnk.href=pc.toDataURL('image/png');lnk.click(); T('QR descargado','','g'); };qi.src=src; } function printQR(){ var el=document.getElementById('qrCanvas');if(!el)return; var img=el.querySelector('img'),qrc=el.querySelector('canvas'); var src=img?img.src:(qrc?qrc.toDataURL():null); if(!src){T('Error','Genera el QR primero','r');return;} var w=window.open('','_blank'); w.document.write('Camuy Alerta QR'+ '

CAMUY ALERTA

'+ '
camuyalerta.pages.dev
'+ '

Escanea este código con la cámara de tu teléfono para reportar vehículos abandonados en Camuy.

'+ '
Municipio de Camuy · +1 787-898-2160
'+ '
'+ ''); w.document.close(); setTimeout(function(){w.print();},500); } // ════════════════════════════════════════════════════════════