Operaciones
del dΓ­a, en orden
Plataforma operativa para el registro de paqueterΓ­a, cierres diarios e inventario de agencias.
Bienvenido
Ingresa tu cΓ³digo de kiosco (ej: KN001) o tu nΓΊmero de ficha para continuar.
β€”
Usuario o PIN incorrecto. Verifica e intenta nuevamente.
accesos de prueba
Kiosco Β· Norte
KN001 Β· JP2
Kiosco Β· CS
KS001 Β· Mamachepa
Personal
Ficha 270010
Personal
Ficha 270002
00:00
β€”
πŸͺ
β€”
β€”
β€”
Inicio
Resumen del dΓ­a
Agencia
β€”
Estado del dΓ­a
0
Paquetes recibidos
0
Entregados
0
Pendientes
Accesos rΓ‘pidos
Registrar paquetes del dΓ­a
Ir β†’
Completar cierre diario
Pendiente
Ver rol semanal
Ver β†’
Kiosco QR Β· marcar asistencia
Activo
Actividad reciente
Sin actividad registrada aΓΊn hoy
πŸ“¦ Registro de Entrega
β–Ό

πŸ’‘ La fecha es automΓ‘tica. El horario de entrega es 8:00–10:00 AM.

βšͺ Sin conexiΓ³n
πŸ“’ Registrar TransacciΓ³n
πŸ“¬ Bandeja de mensajes
Indicaciones y comunicados de coordinaciΓ³n
πŸ“­
Sin mensajes aΓΊn
β€”
β€”
β€”
πŸ“± Escanea con tu celular para marcar asistencia
00:00:00
β€”
principal // Paths: ec/cierre, ec/asistencia, ec/solicitudes_v2, // ec/rol_publicado, ec/mensajes, ec/bandeja_admin, ec/felicitaciones // ══════════════════════════════════════════════════════════════════ (function ecIntegration() { 'use strict'; // ── Esperar a que Firebase y sesion estΓ©n listos ────────────── function waitReady(cb, tries) { tries = tries || 0; if (tries > 60) return; if (typeof fbDB !== 'undefined' && fbDB && typeof sesion !== 'undefined' && sesion && sesion.data && sesion.data.nombre) { cb(); } else { setTimeout(() => waitReady(cb, tries + 1), 500); } } // Exponer funciΓ³n de inicio para que enterApp() la llame despuΓ©s del login window._ecIntegrationInit = function() { waitReady(runIntegration); }; // ── Helpers ─────────────────────────────────────────────────── // Cache global de mensajes ya procesados (declarado aquΓ­ para estar disponible en todos los listeners) const _bandejaCache = new Set(); const _bandejaMemoria = []; // Array en memoria β€” no depende de localStorage let _bandejaLastRead = 0; // timestamp de ΓΊltimo leΓ­do function fechaHoy() { return new Date().toISOString().slice(0, 10); } function nombreKey(nombre) { // ⚠️ Debe ser IDΓ‰NTICA a la del Admin para que los paths de Firebase coincidan return (nombre || '').replace(/\s+/g, '_').replace(/[^a-zA-Z0-9_\-]/g, ''); } // ══════════════════════════════════════════════════════════════ // 1. CIERRE/PAQUETERÍA β†’ migrar path a ec/cierre/{agencia}/{fecha} // Parchamos cdFirebaseInit para que use ec/cierre/ ademΓ‘s del path legacy // ══════════════════════════════════════════════════════════════ function patchCierreFirebase() { const origInit = window.cdFirebaseInit; if (!origInit) return; window.cdFirebaseInit = function() { origInit(); try { const agencia = cdFirebaseGetAgencia ? cdFirebaseGetAgencia() : 'agencia'; const mes = new Date().toISOString().slice(0, 7); const newPath = `ec/cierre/${agencia}/${mes}/historico`; // Escribir mirror en nuevo path cuando se guarde un registro const origGuardar = window.cdFbGuardarRegistro; if (origGuardar && !window._ecCierreMirrored) { window._ecCierreMirrored = true; window.cdFbGuardarRegistro = function(reg) { origGuardar(reg); try { fbDB.ref(`ec/cierre/${agencia}/${mes}/historico/${reg.id}`).set(reg); } catch(e) {} }; } const origEliminar = window.cdFbEliminarRegistro; if (origEliminar && !window._ecCierreElimMirrored) { window._ecCierreElimMirrored = true; window.cdFbEliminarRegistro = function(id) { origEliminar(id); try { fbDB.ref(`ec/cierre/${agencia}/${mes}/historico/${id}`).remove(); } catch(e) {} }; } } catch(e) {} }; window.cdFirebaseInit(); } // ══════════════════════════════════════════════════════════════ // 2. ASISTENCIA β†’ escribir en ec/asistencia/{nombre}/{fecha} // Parchamos el bloque de guardado en kqProcesarValidacion // ══════════════════════════════════════════════════════════════ function patchAsistenciaFirebase(nombre) { const origProcesar = window.kqProcesarValidacion; if (!origProcesar || window._ecAsistMirrored) return; window._ecAsistMirrored = true; window.kqProcesarValidacion = function(encontrado, kioskoId, codigo, hoy, nombreCol) { // Snapshot previo para detectar cambio real const keyAS = 'ec_asistencia'; let dbBefore = {}; try { dbBefore = JSON.parse(localStorage.getItem(keyAS) || '{}'); } catch(e) {} const fechaKey = new Date().toISOString().slice(0, 10); const regBefore = JSON.stringify((dbBefore[nombreCol] || {})[fechaKey] || {}); origProcesar(encontrado, kioskoId, codigo, hoy, nombreCol); try { const dbAfter = JSON.parse(localStorage.getItem(keyAS) || '{}'); const reg = (dbAfter[nombreCol] || {})[fechaKey] || {}; const regAfter = JSON.stringify(reg); // Solo subir a Firebase si hubo un cambio real en el registro if (regAfter !== regBefore && Object.keys(reg).length > 0) { const nk = nombreKey(nombreCol); fbDB.ref(`ec/asistencia/${nk}/${fechaKey}`).set(reg); } } catch(e) {} }; } // ══════════════════════════════════════════════════════════════ // 3. SOLICITUDES β†’ escribir en ec/solicitudes_v2/{id} // Parchamos rrhhSaveSolicitudes // ══════════════════════════════════════════════════════════════ function patchSolicitudesFirebase(nombre) { const origSave = window.rrhhSaveSolicitudes; if (!origSave || window._ecSolMirrored) return; window._ecSolMirrored = true; window.rrhhSaveSolicitudes = function(arr) { origSave(arr); try { // Subir/actualizar cada solicitud del colaborador actual al path ec/solicitudes_v2 arr.filter(s => s.colaborador === nombre).forEach(sol => { fbDB.ref(`ec/solicitudes_v2/${sol.id}`).set(sol); }); } catch(e) {} }; // Escuchar respuestas del admin en tiempo real listenSolicitudesRespuestas(nombre); } function listenSolicitudesRespuestas(nombre) { // Fix 7: Leer UNA VEZ al inicio para aplicar respuestas previas del admin // antes de que el listener en tiempo real tome el relevo try { fbDB.ref('ec/solicitudes_v2').once('value', snap => { const data = snap.val() || {}; const sols = Object.values(data).filter(s => s && s.colaborador === nombre); if (!sols.length) return; const keyLocal = 'ec_solicitudes_op'; let local = []; try { local = JSON.parse(localStorage.getItem(keyLocal) || '[]'); } catch(e) {} let updated = false; sols.forEach(fbSol => { const idx = local.findIndex(s => s.id === fbSol.id); if (idx >= 0) { if (local[idx].estado !== fbSol.estado || local[idx].respuesta !== fbSol.respuesta) { local[idx] = { ...local[idx], ...fbSol }; updated = true; } } else { local.push(fbSol); updated = true; } }); if (updated) { localStorage.setItem(keyLocal, JSON.stringify(local)); const el = document.getElementById('rrhh-sol-lista'); if (el) { try { rrhhSolRender(); } catch(e) {} } console.log('[EC Personal] Solicitudes sincronizadas desde Firebase (once):', sols.length); } }); } catch(e) {} // Listener en tiempo real para respuestas mientras la app estΓ‘ abierta try { fbDB.ref('ec/solicitudes_v2').on('value', snap => { const data = snap.val() || {}; const sols = Object.values(data).filter(s => s && s.colaborador === nombre); if (!sols.length) return; // Actualizar localStorage con respuestas del admin const keyLocal = 'ec_solicitudes_op'; let local = []; try { local = JSON.parse(localStorage.getItem(keyLocal) || '[]'); } catch(e) {} let updated = false; sols.forEach(fbSol => { const idx = local.findIndex(s => s.id === fbSol.id); if (idx >= 0) { if (local[idx].estado !== fbSol.estado || local[idx].respuesta !== fbSol.respuesta) { local[idx] = { ...local[idx], ...fbSol }; updated = true; } } else { local.push(fbSol); updated = true; } }); if (updated) { localStorage.setItem(keyLocal, JSON.stringify(local)); // Re-renderizar si la secciΓ³n estΓ‘ abierta const el = document.getElementById('rrhh-sol-lista'); if (el) { try { rrhhSolRender(); } catch(e) {} } // Toast si hay una respuesta nueva de Admin sols.filter(s => s.estado !== 'Pendiente').forEach(s => { const local2 = JSON.parse(localStorage.getItem('ec_sol_notificadas') || '[]'); if (!local2.includes(s.id)) { const nuevas = [...local2, s.id]; localStorage.setItem('ec_sol_notificadas', JSON.stringify(nuevas)); const ico = s.estado === 'Aprobada' ? 'βœ…' : '❌'; showEcToast(`${ico} Tu solicitud fue ${s.estado.toLowerCase()} por coordinaciΓ³n`); } }); } }); } catch(e) {} } // ══════════════════════════════════════════════════════════════ // 4. ROL PUBLICADO β†’ escuchar ec/rol_publicado/{zona} // ══════════════════════════════════════════════════════════════ function listenRolPublicado(zona) { try { const zonaKey = zona === 'ZONA CENTRO SUR' ? 'cs' : 'norte'; fbDB.ref(`ec/rol_publicado/${zonaKey}`).on('value', snap => { const rol = snap.val(); if (!rol) return; const lsKey = zona === 'ZONA CENTRO SUR' ? 'ec_rol_actual_cs' : 'ec_rol_actual_norte'; const current = localStorage.getItem(lsKey); const newStr = JSON.stringify(rol); if (current !== newStr) { localStorage.setItem(lsKey, newStr); // Notificar si el panel de rol estΓ‘ activo const elRol = document.getElementById('p-rol'); if (elRol && elRol.classList.contains('active')) { try { rrhhPanelInit('rol'); } catch(e) {} } // Toast notificaciΓ³n showEcToast('πŸ“… Rol semanal actualizado por coordinaciΓ³n'); } }); } catch(e) {} } // ══════════════════════════════════════════════════════════════ // 5. MENSAJES INDIVIDUALES β†’ ec/mensajes/{nombreKey} // ══════════════════════════════════════════════════════════════ function listenMensajesIndividuales(nombre) { try { const nk = nombreKey(nombre); const ref = fbDB.ref(`ec/mensajes/${nk}`); // βœ… PASO 1: Cargar mensajes YA existentes en Firebase (sin toast, solo memoria) ref.once('value', snap => { console.log('[EC Mensajes Individuales] snapshot para', nk, ':', snap.val()); const data = snap.val() || {}; Object.values(data).forEach((msg, idx) => { if (!msg) return; if (!msg.id) msg.id = 'msg_' + idx; // fallback si no tiene id if (!_bandejaCache.has(msg.id)) { _bandejaCache.add(msg.id); _bandejaMemoria.push(msg); } }); renderBandejaPanel(); updateBandejaBadge(); }); // βœ… PASO 2: Escuchar mensajes NUEVOS en tiempo real (con toast) ref.on('child_added', snap => { const msg = snap.val(); if (!msg) return; if (!msg.id) msg.id = snap.key; // usar key de Firebase como fallback if (_bandejaCache.has(msg.id)) return; addMensajeBandeja(msg); }); ref.on('child_changed', snap => { const msg = snap.val(); if (!msg) return; addMensajeBandeja(msg); }); } catch(e) { console.error('[EC Mensajes individuales]', e); } } // ══════════════════════════════════════════════════════════════ // 6. BANDEJA ADMIN β†’ ec/bandeja_admin (mensajes a todos) // ══════════════════════════════════════════════════════════════ function listenBandejaAdmin() { try { const ref = fbDB.ref('ec/bandeja_admin'); // βœ… PASO 1: Cargar mensajes de bandeja ya existentes (sin toast, solo memoria) ref.once('value', snap => { console.log('[EC Bandeja Admin] snapshot recibido:', snap.val()); const data = snap.val() || {}; Object.values(data).forEach((msg, idx) => { if (!msg) return; if (!msg.id) msg.id = 'admin_' + idx; // fallback si no tiene id if (!_bandejaCache.has(msg.id)) { _bandejaCache.add(msg.id); _bandejaMemoria.push(msg); } }); renderBandejaPanel(); updateBandejaBadge(); }); // βœ… PASO 2: Escuchar mensajes NUEVOS en tiempo real (con toast) ref.on('child_added', snap => { const msg = snap.val(); if (!msg) return; if (!msg.id) msg.id = snap.key; // usar key de Firebase como fallback if (_bandejaCache.has(msg.id)) return; addMensajeBandeja(msg); }); } catch(e) { console.error('[EC Bandeja Admin]', e); } } // ══════════════════════════════════════════════════════════════ // 7. FELICITACIONES β†’ ec/felicitaciones/{nombreKey} // ══════════════════════════════════════════════════════════════ function listenFelicitaciones(nombre) { try { const nk = nombreKey(nombre); const ref = fbDB.ref(`ec/felicitaciones/${nk}`); // βœ… PASO 1: Cargar felicitaciones ya existentes (sin toast, solo memoria) ref.once('value', snap => { const data = snap.val() || {}; Object.values(data).forEach((msg, idx) => { if (!msg) return; if (!msg.id) msg.id = 'felic_' + idx; if (!_bandejaCache.has(msg.id)) { const m = { ...msg, tipo: 'felicitacion', icono: 'πŸŽ‰' }; _bandejaCache.add(m.id); _bandejaMemoria.push(m); } }); renderBandejaPanel(); updateBandejaBadge(); }); // βœ… PASO 2: Escuchar felicitaciones NUEVAS en tiempo real (con toast) ref.on('child_added', snap => { const msg = snap.val(); if (!msg) return; if (!msg.id) msg.id = snap.key; if (_bandejaCache.has(msg.id)) return; addMensajeBandeja({ ...msg, tipo: 'felicitacion', icono: 'πŸŽ‰' }); }); } catch(e) { console.error('[EC Felicitaciones]', e); } } // ══════════════════════════════════════════════════════════════ // BANDEJA: Agregar mensaje al panel p-bandeja // ══════════════════════════════════════════════════════════════ // _bandejaCache ya declarado arriba function addMensajeBandeja(msg) { if (!msg || !msg.id) return; if (_bandejaCache.has(msg.id)) return; _bandejaCache.add(msg.id); // Guardar en memoria (sin localStorage β€” compatible con cualquier navegador) if (!_bandejaMemoria.find(m => m.id === msg.id)) { _bandejaMemoria.unshift(msg); if (_bandejaMemoria.length > 50) _bandejaMemoria.length = 50; } renderBandejaPanel(); updateBandejaBadge(); if (msg.tipo !== 'bandeja_leida') { showEcToast(`${msg.icono || 'πŸ“©'} ${(msg.texto || msg.mensaje || '').slice(0, 60)}`); } } function renderBandejaPanel() { const lista = document.getElementById('bandeja-lista'); if (!lista) return; const msgs = _bandejaMemoria; if (!msgs.length) { lista.innerHTML = `
πŸ“­
Sin mensajes aΓΊn
`; return; } lista.innerHTML = msgs.slice(0, 20).map(msg => { const ico = msg.icono || (msg.tipo === 'felicitacion' ? 'πŸŽ‰' : msg.tipo === 'bandeja_admin' ? 'πŸ“’' : 'πŸ“©'); const texto = msg.texto || msg.mensaje || ''; const hora = msg.hora || msg.ts || ''; const bgColor = msg.tipo === 'felicitacion' ? 'linear-gradient(135deg,#fdf4ff,#fce7f3)' : msg.tipo === 'bandeja_admin' ? 'linear-gradient(135deg,#eff6ff,#dbeafe)' : 'white'; const borderColor = msg.tipo === 'felicitacion' ? '#e879f9' : msg.tipo === 'bandeja_admin' ? '#3b82f6' : 'var(--cream3)'; return `
${ico}
${texto}
${hora ? `
${hora}
` : ''}
`; }).join(''); } function updateBandejaBadge() { const msgs = _bandejaMemoria; const lastRead = _bandejaLastRead; const unread = msgs.filter(m => { const ts = m.tsNum || new Date(m.ts || 0).getTime() || 0; return ts > lastRead; }).length; const badge = document.getElementById('badge-bandeja'); const cnt = document.getElementById('bandeja-badge-cnt'); if (badge) { badge.style.display = unread > 0 ? 'flex' : 'none'; } if (cnt) { cnt.style.display = unread > 0 ? 'inline-block' : 'none'; cnt.textContent = unread + ' nuevo' + (unread !== 1 ? 's' : ''); } } // Marcar bandeja como leΓ­da cuando se abre el panel (function patchNavBandeja() { // Parchar navTo para interceptar cuando se abre 'bandeja' const origNavTo = window.navTo; if (origNavTo && !window._ecBandejaPatch) { window._ecBandejaPatch = true; window.navTo = function(id, el) { origNavTo(id, el); if (id === 'bandeja') { // Marcar todos como leΓ­dos _bandejaLastRead = Date.now(); // Limpiar badge inmediatamente const badge = document.getElementById('badge-bandeja'); const cnt = document.getElementById('bandeja-badge-cnt'); if (badge) badge.style.display = 'none'; if (cnt) cnt.style.display = 'none'; // Renderizar panel con mensajes + recargar desde Firebase setTimeout(() => { renderBandejaPanel(); updateBandejaBadge(); if (typeof window._ecRecargarBandeja === 'function') window._ecRecargarBandeja(); }, 100); } }; } // TambiΓ©n parchar el botΓ³n nav como fallback const navBtn = document.getElementById('nav-bandeja'); if (navBtn) { const orig = navBtn.onclick; navBtn.onclick = function(e) { if (orig) orig.call(this, e); _bandejaLastRead = Date.now(); const badge = document.getElementById('badge-bandeja'); const cnt = document.getElementById('bandeja-badge-cnt'); if (badge) badge.style.display = 'none'; if (cnt) cnt.style.display = 'none'; setTimeout(updateBandejaBadge, 200); renderBandejaPanel(); }; } })(); // ══════════════════════════════════════════════════════════════ // TOAST notificaciΓ³n flotante // ══════════════════════════════════════════════════════════════ function showEcToast(msg) { let t = document.getElementById('ec-toast-notif'); if (!t) { t = document.createElement('div'); t.id = 'ec-toast-notif'; t.style.cssText = 'position:fixed;bottom:80px;left:50%;transform:translateX(-50%);background:#1A0E08;color:#fff;font-family:"DM Sans",sans-serif;font-size:13px;font-weight:600;padding:11px 20px;border-radius:20px;z-index:99999;box-shadow:0 4px 20px rgba(0,0,0,0.35);max-width:90vw;text-align:center;transition:opacity 0.3s;pointer-events:none'; document.body.appendChild(t); } t.textContent = msg; t.style.opacity = '1'; clearTimeout(t._tid); t._tid = setTimeout(() => { t.style.opacity = '0'; }, 3500); } // ══════════════════════════════════════════════════════════════ // INICIO β€” esperar sesiΓ³n y Firebase // ══════════════════════════════════════════════════════════════ function runIntegration() { const nombre = sesion.data.nombre; const zona = sesion.data.zona || 'ZONA NORTE'; // 1. Cierre/PaqueterΓ­a patchCierreFirebase(); // 2. Asistencia mirror patchAsistenciaFirebase(nombre); // 3. Solicitudes bidireccional patchSolicitudesFirebase(nombre); // 4. Rol publicado (tiempo real) listenRolPublicado(zona); // 5. Mensajes individuales listenMensajesIndividuales(nombre); // 6. Bandeja general admin listenBandejaAdmin(); // 7. Felicitaciones listenFelicitaciones(nombre); // Inicializar bandeja con mensajes guardados localmente renderBandejaPanel(); updateBandejaBadge(); // Exponer funciΓ³n de recarga manual de bandeja globalmente window._ecRecargarBandeja = function() { const nk = nombreKey(nombre); fbDB.ref('ec/mensajes/' + nk).once('value', function(snap) { const data = snap.val() || {}; const lista = document.getElementById('bandeja-lista'); if (!lista) return; const msgs = Object.values(data); if (!msgs.length) return; msgs.sort(function(a,b){ return (b.tsNum||0)-(a.tsNum||0); }); lista.innerHTML = msgs.slice(0,20).map(function(m) { const ico = m.icono || 'πŸ“©'; const bgColor = m.tipo === 'felicitacion' ? 'linear-gradient(135deg,#fdf4ff,#fce7f3)' : m.tipo === 'bandeja_admin' ? 'linear-gradient(135deg,#eff6ff,#dbeafe)' : 'white'; const borderColor = m.tipo === 'felicitacion' ? '#e879f9' : m.tipo === 'bandeja_admin' ? '#3b82f6' : 'var(--cream3)'; return '
'+ '
'+ '
'+ico+'
'+ '
'+ '
'+(m.texto||m.mensaje||'')+'
'+ (m.hora ? '
'+m.hora+'
' : '')+ '
'; }).join(''); }); // TambiΓ©n bandeja admin fbDB.ref('ec/bandeja_admin').once('value', function(snap) { const data = snap.val() || {}; const bndMsgs = Object.values(data); if (!bndMsgs.length) return; const lista = document.getElementById('bandeja-lista'); if (!lista) return; const existing = lista.innerHTML; bndMsgs.sort(function(a,b){ return (b.tsNum||0)-(a.tsNum||0); }); const newHtml = bndMsgs.slice(0,10).map(function(m) { return '
'+ '
'+ '
'+(m.icono||'πŸ“’')+'
'+ '
'+ '
'+(m.texto||m.mensaje||'')+'
'+ (m.hora ? '
'+m.hora+'
' : '')+ '
'; }).join(''); lista.innerHTML = newHtml + existing; }); }; console.log('βœ… EC Integration Personal cargado para:', nombre); } // Auto-arrancar si sesion ya existe al cargar waitReady(runIntegration); })();