Prueba pico y placa

Pureba blog pio y placa ciudadades json.
`; root.innerHTML = tpl; const $ = (id) => root.querySelector('#' + id); // ---------- util ---------- function esc(v){ if (v === null || v === undefined) return ''; if (typeof v === 'string') return v.trim(); if (typeof v === 'number' || typeof v === 'boolean') return String(v); try { return JSON.stringify(v); } catch(e) { return String(v); } } function dayKey(d){ return ['sun','mon','tue','wed','thu','fri','sat'][d.getDay()]; } function fmtDate(d){ const dias = ['Domingo','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado']; const meses = ['enero','febrero','marzo','abril','mayo','junio','julio','agosto','septiembre','octubre','noviembre','diciembre']; return `${dias[d.getDay()]}, ${d.getDate()} de ${meses[d.getMonth()]} ${d.getFullYear()}`; } function fmtTime12(d){ return d.toLocaleTimeString('es-CO', { hour: 'numeric', minute: '2-digit', hour12: true }); } function time24to12(t){ if (!t || typeof t !== 'string') return ''; const [hh, mm] = t.split(':').map(n => parseInt(n, 10)); if (Number.isNaN(hh) || Number.isNaN(mm)) return t; const h12 = ((hh + 11) % 12) + 1; const suf = hh >= 12 ? 'p. m.' : 'a. m.'; return `${h12}:${String(mm).padStart(2,'0')} ${suf}`; } function fmtScheduleLabel(scheduleArr){ const parts = (scheduleArr || []) .filter(s => s && s.start && s.end) .map(s => `${time24to12(String(s.start))} – ${time24to12(String(s.end))}`); if (!parts.length) return 'Sin horario'; return `Aplica: ${parts.join(' · ')}`; } function toMinutes_(hhmm){ if (!hhmm) return null; const [h, m] = String(hhmm).split(':').map(n => parseInt(n, 10)); if (Number.isNaN(h) || Number.isNaN(m)) return null; return h * 60 + m; } function getEstadoHorario_(scheduleArr, now){ const dayMin = now.getHours() * 60 + now.getMinutes(); const slots = (scheduleArr || []) .map(s => ({ start: toMinutes_(s.start), end: toMinutes_(s.end), raw: s })) .filter(s => s.start !== null && s.end !== null) .sort((a,b) => a.start - b.start); if (!slots.length) return { label: 'Sin horario', kind: 'none' }; for (const s of slots){ if (dayMin >= s.start && dayMin < s.end){ return { label: `Aplica ahora · termina a las ${time24to12(s.raw.end)}`, kind: 'active' }; } } const next = slots.find(s => dayMin < s.start); if (next){ return { label: `No aplica ahora · empieza a las ${time24to12(next.raw.start)}`, kind: 'waiting' }; } const last = slots[slots.length - 1]; return { label: `Finalizó hoy · terminó a las ${time24to12(last.raw.end)}`, kind: 'finished' }; } // ---------- iconos (SVG inline) ---------- function iconSvg_(id){ // SVG simples, limpios, “parecen” vehículo sin verse infantiles. // Usa currentColor para heredar color. const common = `width="22" height="22" viewBox="0 0 24 24" fill="none" aria-hidden="true"`; const car = ``; const moto = ``; const taxi = ``; const truck = ``; const bus = ``; const shield = ``; const map = { particulares: car, motos: moto, taxis: taxi, carga: truck, transporte_publico: bus, especial: shield }; return map[id] || car; } // ---------- reglas ---------- function normalizarValor_(raw){ const v = esc(raw); if (!v) return 'Sin dato'; if (v === 'NO_APLICA') return 'No aplica'; if (v === 'PENDIENTE') return 'Sin dato'; if (v === 'TODOS') return 'Todos'; return v; // "6-7", "0-1", "9-0", etc. } function obtenerValor_(cat, k){ const ruleType = cat?.rules?.type || 'none'; if (ruleType === 'none') return 'No aplica'; if (ruleType === 'weekday_rotation') return normalizarValor_(cat?.rules?.days?.[k]); return 'Sin dato'; } function badge_(kind, text){ const cls = kind === 'active' ? 'pz-badge pz-badge--active' : kind === 'waiting' ? 'pz-badge pz-badge--wait' : kind === 'finished' ? 'pz-badge pz-badge--done' : kind === 'none' ? 'pz-badge pz-badge--none' : 'pz-badge'; return `${esc(text)}`; } function renderCity(city){ const now = new Date(); $('pzTitle').textContent = `Pico y Placa ${esc(city.name)}`; $('pzMeta').textContent = `${fmtDate(now)} · ${fmtTime12(now)}`; const srcName = esc(city.source?.name); const srcUrl = esc(city.source?.url); const verified = esc(city.last_verified); $('pzFoot').innerHTML = srcUrl ? `Basado en ${srcName ? `${srcName}` : `fuente oficial` }${verified ? ` · Verificado: ${verified}` : ''}` : `${verified ? `Verificado: ${verified}` : ''}`; const k = dayKey(now); const cats = (city.categories || []) .slice() .sort((a,b) => (Number(a.priority||99) - Number(b.priority||99)) || esc(a.name).localeCompare(esc(b.name), 'es')); $('pzGrid').innerHTML = cats.map(cat => { const val = obtenerValor_(cat, k); // Estado horario solo si aplica (si "No aplica", no tiene sentido) const estado = (val === 'No aplica') ? { kind: 'na', label: 'No aplica' } : getEstadoHorario_(cat.schedule, now); const scheduleText = fmtScheduleLabel(cat.schedule); // Texto de franja operativa: // - si no aplica: no mostramos franja // - si sin horario: mostramos "Sin horario" // - si tiene horario: mostramos label estado (aplica ahora / empieza / finalizó) let bandText = ''; if (val !== 'No aplica') { bandText = (estado.kind === 'none') ? scheduleText : estado.label; } // Badge (estado corto) para UX let badgeHtml = ''; if (val === 'No aplica') { badgeHtml = badge_('none', 'No aplica'); } else if (estado.kind === 'active') { badgeHtml = badge_('active', 'Aplica ahora'); } else if (estado.kind === 'waiting') { badgeHtml = badge_('waiting', 'No aplica ahora'); } else if (estado.kind === 'finished') { badgeHtml = badge_('finished', 'Finalizó'); } else { badgeHtml = badge_('none', 'Sin horario'); } const icon = iconSvg_(esc(cat.id)); const muted = (val === 'No aplica'); return `
${icon}
${esc(cat.name)}
${esc(val)}
${badgeHtml}
${bandText ? `
${esc(bandText)}
` : ''}
`; }).join(''); } async function init(){ const res = await fetch(DATA_URL, { cache: 'no-store' }); if (!res.ok) throw new Error('No se pudo cargar el dataset'); const data = await res.json(); const cities = (data.cities || []).slice(); if (!cities.length) throw new Error('No hay ciudades publicadas en el JSON'); const select = $('pzCitySelect'); select.innerHTML = cities .slice() .sort((a,b) => esc(a.name).localeCompare(esc(b.name), 'es')) .map(c => ``) .join(''); const preferred = CITY_ID; const initial = cities.find(c => c.id === preferred) ? preferred : cities[0].id; select.value = initial; let currentCity = cities.find(c => c.id === select.value) || cities[0]; renderCity(currentCity); select.addEventListener('change', () => { currentCity = cities.find(c => c.id === select.value) || cities[0]; renderCity(currentCity); }); // reloj + refresco de estados cada minuto (suficiente y liviano) if (window.pzPypClock) clearInterval(window.pzPypClock); window.pzPypClock = setInterval(() => { const t = new Date(); $('pzMeta').textContent = `${fmtDate(t)} · ${fmtTime12(t)}`; renderCity(currentCity); }, 60 * 1000); } init().catch(err => { $('pzTitle').textContent = 'Pico y Placa'; $('pzMeta').textContent = ''; $('pzGrid').innerHTML = `
No se pudo cargar la información. Revisa la URL del JSON.
`; $('pzFoot').textContent = ''; console.error(err); }); })();
miguel

Recent Posts

Guía para elegir los seguros ideales y vivir con tranquilidad en el 2026

¡Asegure su tranquilidad! Encuentre el seguro ideal para sus planes y disfrute sin preocupaciones con Seguros Bolívar.

29 December, 2025

¡Bienvenido 2026! Prepare su hogar para recibir el Año Nuevo

¿Ya tiene todo listo para recibir el 2026? Una de las mejores formas de recibir el año nuevo es hacerlo…

29 December, 2025

85 años construyendo un negocio sostenible: la historia de Seguros Bolívar

El ranking CSA 2025 confirma nuestro compromiso con la sostenibilidad, conozca cómo integramos acciones responsables que generan bienestar y valor…

24 December, 2025

¿Qué es el débito automático y cómo funciona? Todo lo que debe saber

El débito automático es una herramienta muy útil para realizar sus pagos a tiempo y evitar acumulaciones que generen preocupación.

24 December, 2025

Cree su lista de propósitos laborales para el 2026 y cumpla lo que se promete

Cumpla esos propósitos laborales que aspira para el año nuevo con estas claves que le permitirán llegar a otro nivel.

24 December, 2025

Historias que marcaron este 2025: un ‘recap’ de Seguro Nos Pasa

Resiliencia, maternidad real, duelo y amor propio marcaron el 2025 en Seguro Nos Pasa. Descubra más en este recap del…

23 December, 2025