// ══════════════════════════════════════════════════════════
// REDIL — Sistema de Gestión v5.1
// ══════════════════════════════════════════════════════════
var APP={user:null,mod:'',charts:{},hermanos:[],contactos:[],formUrl:'',formUrlPublic:'',config:{}};
const TIPO_REUNION=['Mixta (Reunión Regular)','Niños','Jóvenes','Mujeres','Profesionales'];
const METODO_REPORTE=['Digital','Físico'];
var SESSION_KEY='ir_sess_v51',SESSION_USER_KEY='ir_user_v51',SESSION_TIME_KEY='ir_time_v51';
var LOGO_KEY='ir_logo_v5',TOTALAPP_LOGO_KEY='ir_totalapp_logo_v5';
// ── DICCIONARIO DE TÍTULOS DE MÓDULOS ──────────────────────
var TITLES = {
dashboard: 'Dashboard General',
reportes: 'Reporte de Grupos',
reporteDigital: 'Reporte Digital',
formulario: 'Formulario Externo',
generador: 'Generador de Reportes',
hermanos: 'Hermanos Líderes',
cargaMasiva: 'Carga Masiva',
supervisores: 'Supervisores de Sector',
pastores: 'Pastores de Zona',
ayudapastor: 'Ayuda de Pastor',
seguimientos: 'Seguimientos',
privilegios: 'Privilegios',
diezmos: 'Control de Diezmos',
gastos: 'Control de Gastos',
inventario: 'Inventario',
insumos: 'Insumos',
envio: 'Centro de Envíos',
contactos: 'Tabla de Contactos',
usuarios: 'Usuarios',
bitacora: 'Bitácora de Accesos',
configuracion: 'Configuración',
cronograma: 'Cronograma',
bautizos: 'Bautizos en Agua'
};
function saveSession(token,user,inactMin){[localStorage,sessionStorage].forEach(function(s){try{s.setItem(SESSION_KEY,token);s.setItem(SESSION_USER_KEY,JSON.stringify(user));s.setItem(SESSION_TIME_KEY,Date.now().toString());s.setItem('ir_inact_min',String(inactMin||60));}catch(e){}});}
function getToken(){return localStorage.getItem(SESSION_KEY)||sessionStorage.getItem(SESSION_KEY);}
function permisoEditar(){return APP.user&&APP.user.PuedeEditar!=='NO';}
function clearSession(){var token=localStorage.getItem(SESSION_KEY)||sessionStorage.getItem(SESSION_KEY);if(token)try{call('destroySession',{token:token},function(){});}catch(e){}[SESSION_KEY,SESSION_USER_KEY,SESSION_TIME_KEY,'ir_inact_min'].forEach(function(k){try{localStorage.removeItem(k);sessionStorage.removeItem(k);}catch(e){}});}
function getInactMin(){return parseInt(localStorage.getItem('ir_inact_min')||'60',10)||60;}
function call(action,payload,cb){
payload=payload||{};
var token=localStorage.getItem(SESSION_KEY)||sessionStorage.getItem(SESSION_KEY);
if(token && !payload.token) payload.token=token;
try{
var url=window.location.origin+'/api/dispatch';
fetch(url,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({action:action,payload:payload,token:token})})
.then(function(r){return r.json();})
.then(function(r){if(cb)cb(null,r);})
.catch(function(e){if(cb)cb(e,null);try{toast('Error: '+(e.message||e),'er');}catch(ex){}});
}catch(e){if(cb)cb(e,null);}
}
function g(id){var el=document.getElementById(id);return el?(el.value!==undefined?el.value:el.textContent):'';}
function esc(s){return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"').replace(/'/g,''');}
function num(v){return parseFloat(String(v).replace(/[^0-9.\-]/g,''))||0;}
function fmtQ(v){return 'Q '+Number(v||0).toLocaleString('es-GT',{minimumFractionDigits:2,maximumFractionDigits:2});}
function fmtD(s){if(!s)return'--';try{var d=new Date(s+'T12:00:00');return d.toLocaleDateString('es-GT');}catch(e){return s;}}
function fmtF(s){if(!s)return'';try{var d=new Date(s+'T12:00:00');return d.toLocaleDateString('es-GT');}catch(e){return s;}}
function today(){var d=new Date();return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-'+String(d.getDate()).padStart(2,'0');}
function todayStr(){return today();}
function fom(){var d=new Date();return d.getFullYear()+'-'+String(d.getMonth()+1).padStart(2,'0')+'-01';}
function mesNombre(s){if(!s)return'';var m=['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'];try{return m[new Date(s+'T12:00:00').getMonth()];}catch(e){return s;}}
function exportXLS(data,name,file){loading(true);call('exportExcel',{data:data,nombreHoja:name,nombreArchivo:file},function(_,res){loading(false);if(res&&res.ok){var url=res.xlsxUrl||res.url;window.open(url,'_blank');}else toast((res&&res.msg)||'Error al exportar','er');});}
function toast(msg,type,ms){type=type||'in';ms=ms||3400;var w=document.getElementById('toastWrap');if(!w)return;var t=document.createElement('div');var icons={ok:'fa-check-circle',er:'fa-times-circle',wa:'fa-exclamation-triangle',in:'fa-info-circle'};var colors={ok:'#27ae60',er:'#e74c3c',wa:'#e8a020',in:'#2563a8'};t.style.cssText='display:flex;align-items:center;gap:10px;background:#fff;border-radius:10px;padding:12px 16px;margin-bottom:8px;box-shadow:0 4px 20px rgba(0,0,0,.12);border-left:4px solid '+colors[type]+';font-size:13px;max-width:380px';t.innerHTML=''+esc(msg)+'';w.appendChild(t);setTimeout(function(){if(t.parentNode)t.parentNode.removeChild(t);},ms);}
function loading(s){var el=document.getElementById('gLoad');if(el)el.classList.toggle('show',!!s);}
// ── DIÁLOGO PERSONALIZADO (reemplaza confirm() nativo) ────────
let _confirmCb=null;
function customConfirm(msg,cb,opts){
opts=opts||{};
const title=opts.title||(APP&&APP.config&&APP.config.nombre?APP.config.nombre:'REDIL');
const icon=opts.icon||'fa-question-circle';
const color=opts.color||'var(--pr,#1a3a5c)';
let ov=document.getElementById('customConfirmOv');
if(!ov){
ov=document.createElement('div');
ov.id='customConfirmOv';
ov.style.cssText='display:none;position:fixed;inset:0;z-index:99999;background:rgba(0,0,0,.55);align-items:center;justify-content:center;';
ov.innerHTML=`
`;
document.body.appendChild(ov);
}
_confirmCb=cb;
document.getElementById('cfd_msg').textContent=msg;
document.getElementById('cfd_title').textContent=title;
const iWrap=document.getElementById('cfd_iconWrap');
if(iWrap){iWrap.style.background=opts.danger?'rgba(231,76,60,.12)':'rgba(26,58,92,.1)';iWrap.innerHTML=``;}
ov.style.display='flex';
}
function _resolveConfirm(val){
const ov=document.getElementById('customConfirmOv');
if(ov)ov.style.display='none';
if(_confirmCb){const cb=_confirmCb;_confirmCb=null;cb(val);}
}
function applyLogoToUI(url){if(!url)return;['loginLogoWrap','sidebarLogoWrap'].forEach(function(id){var el=document.getElementById(id);if(el)el.innerHTML='';});}
function applyBrandLogoToUI(url){if(!url)return;var el=document.getElementById('sidebarLogoWrap');if(el)el.innerHTML='';}
function applyTotalAppLogoToUI(url){if(!url)return;var el=document.getElementById('totalAppLogoWrap');if(el)el.innerHTML='';}
function initLogo(){var s=localStorage.getItem(LOGO_KEY);if(s)applyLogoToUI(s);var b=localStorage.getItem(TOTALAPP_LOGO_KEY);if(b)applyTotalAppLogoToUI(b);}
function uploadLogo(){openModal('Cambiar Logo','
'+(localStorage.getItem(LOGO_KEY)?'':'')+'
Clic para subir logo
','');}
function previewLg(input){var file=input.files[0];if(!file)return;var reader=new FileReader();reader.onload=function(e){window._pendLogo=e.target.result;var p=document.getElementById('lgPrev');if(p)p.innerHTML='';};reader.readAsDataURL(file);}
function saveLogo(){if(!window._pendLogo){closeModal();return;}localStorage.setItem(LOGO_KEY,window._pendLogo);applyLogoToUI(window._pendLogo);call('saveConfig',{logo_url:window._pendLogo},function(_,r){if(!r||!r.ok)toast('Logo local, no se pudo guardar en servidor','wa');});window._pendLogo=null;closeModal();toast('Logo actualizado','ok');}
function clearLogo(){localStorage.removeItem(LOGO_KEY);['loginLogoWrap','sidebarLogoWrap'].forEach(function(id){var el=document.getElementById(id);if(el)el.innerHTML='';});call('saveConfig',{logo_url:''},function(_,r){});closeModal();toast('Logo removido','wa');}
function uploadBrandLogo(){openModal('Logo Empresa','
Subir logo empresa
','');}
function previewBrandLg(input){var file=input.files[0];if(!file)return;var reader=new FileReader();reader.onload=function(e){window._pendBrandLogo=e.target.result;var p=document.getElementById('bLgPreview');if(p)p.innerHTML='';};reader.readAsDataURL(file);}
function saveBrandLogo(){if(!window._pendBrandLogo){closeModal();return;}localStorage.setItem(TOTALAPP_LOGO_KEY,window._pendBrandLogo);applyTotalAppLogoToUI(window._pendBrandLogo);window._pendBrandLogo=null;closeModal();toast('Logo actualizado','ok');}
function clearBrandLogo(){localStorage.removeItem(TOTALAPP_LOGO_KEY);var el=document.getElementById('totalAppLogoWrap');if(el)el.innerHTML='Total App GT';closeModal();toast('Logo removido','wa');}
function openModal(title,body,foot,size){var ov=document.getElementById('modalOv'),box=document.getElementById('modalBox'),tEl=document.getElementById('modalTitle'),bEl=document.getElementById('modalBody'),fEl=document.getElementById('modalFoot');if(tEl)tEl.textContent=title;if(bEl)bEl.innerHTML=body||'';if(fEl)fEl.innerHTML=foot||'';if(box)box.style.maxWidth=size==='xl'?'920px':'';if(ov)ov.classList.add('open');}
function closeModal(){var ov=document.getElementById('modalOv');if(ov)ov.classList.remove('open');}
function onOvClick(e){if(e.target===document.getElementById('modalOv'))closeModal();}
function openSidebar(){var s=document.getElementById('sidebar'),o=document.getElementById('sbOv');if(s)s.classList.add('open');if(o)o.classList.add('show');}
function closeSidebar(){var s=document.getElementById('sidebar'),o=document.getElementById('sbOv');if(s)s.classList.remove('open');if(o)o.classList.remove('show');}
function togglePw(){var i=document.getElementById('loginPass'),e=document.getElementById('eyeIco');if(!i||!e)return;i.type=(i.type==='password')?'text':'password';e.className=(i.type==='password')?'fas fa-eye':'fas fa-eye-slash';}
function aplicarNombreSistema(nombre){
if(!nombre)return;
var lt=document.getElementById('loginTitle');if(lt)lt.textContent=nombre;
var at=document.getElementById('sbAppTitle'),sub=document.getElementById('sbAppSub');
if(at){
var parts=nombre.split(' ');
if(parts.length>=2){
at.innerHTML=''+esc(parts[0])+''+esc(parts.slice(1).join(' '))+'';
} else {
at.innerHTML=''+esc(nombre)+'';
}
}
document.title=nombre+' - REDIL';
if(!APP.config)APP.config={};APP.config.nombre=nombre;
}
var _actTimer=null, _actListenersSet=false;
function resetActivityTimer(){if(_actTimer)clearTimeout(_actTimer);if(!APP.user)return;_actTimer=setTimeout(function(){logout(true);},getInactMin()*60*1000);}
function setupActivityTracking(){if(_actListenersSet)return;_actListenersSet=true;['click','keydown','mousemove','touchstart','scroll'].forEach(function(ev){document.addEventListener(ev,resetActivityTimer,{passive:true});});}
setupActivityTracking();
function handleLogin(e){
if(e)e.preventDefault();
var _sh=document.getElementById('appShell');if(_sh)_sh.style.display='none';
var emailEl=document.getElementById('loginEmail'),passEl=document.getElementById('loginPass');
var errEl=document.getElementById('loginErr'),txtEl=document.getElementById('loginTxt'),loadEl=document.getElementById('loginLoad');
var email=emailEl?emailEl.value.trim():'',pass=passEl?passEl.value:'';
if(!email||!pass){if(errEl)errEl.textContent='Ingresa correo y contrasena';return;}
if(txtEl)txtEl.style.display='none';
if(loadEl)loadEl.style.display='inline';
if(errEl)errEl.textContent='';
call('login',{email:email,password:pass},function(err,res){
if(txtEl)txtEl.style.display='inline';
if(loadEl)loadEl.style.display='none';
if(err){if(errEl)errEl.textContent='Error: '+(err.message||String(err));return;}
if(!res||!res.ok){if(errEl)errEl.textContent=(res&&res.msg)||'Credenciales incorrectas';return;}
APP.user=res.user;
saveSession(res.token,res.user,(res.user&&res.user.inactMin)||60);
// Registrar acceso en bitácora
try{
call('registrarAcceso',{
usuario:res.user.nombre||res.user.email,
email:res.user.email,
rol:res.user.isPropietario?'Propietario':(res.user.rol||'Usuario'),
accion:'Login',
ip:'',
detalles:navigator.userAgent.substring(0,120)
},function(){});
}catch(e){}
try{localStorage.removeItem('ir_last_mod');}catch(e){}
mostrarApp(res.user);
});
}
function mostrarApp(user){
var loginEl=document.getElementById('loginScreen');
if(loginEl){loginEl.classList.remove('active');loginEl.style.display='none';}
var shellEl=document.getElementById('appShell');
if(shellEl){shellEl.style.display='flex';}
var sbN=document.getElementById('sbName'),sbR=document.getElementById('sbRol'),tbU=document.getElementById('tbUser');
if(sbN)sbN.textContent=(user&&user.nombre)||'Usuario';
if(sbR)sbR.textContent=(user&&user.isPropietario)?'Propietario':((user&&user.rol)||'');
if(tbU)tbU.textContent=(user&&user.nombre)||'Usuario';
try{buildSidebarMenu((user&&user.menu)||[],!!(user&&user.isPropietario));}catch(e){console.error('menu:',e);}
call('getConfig',{},function(_,cfg){if(!cfg||!cfg.ok)return;APP.config=cfg;APP.formUrl=cfg.formUrl||'';APP.formUrlPublic=cfg.formUrlPublic||'';
if(cfg.nombre){aplicarNombreSistema(cfg.nombre);}
if(cfg.logo_url||cfg.logoUrl){
var _lu=cfg.logo_url||cfg.logoUrl;
var _imgT='';
var _lw=document.getElementById('loginLogoWrap');if(_lw)_lw.innerHTML=_imgT;
var _sw=document.getElementById('sidebarLogoWrap');if(_sw)_sw.innerHTML=_imgT;
}if(cfg.nombre)try{if(window.aplicarNombreSistema)aplicarNombreSistema(cfg.nombre);}catch(e){}try{if(cfg.menuConfig)initMenuState(cfg.menuConfig);}catch(e){}try{applyGlobalMenuConfig(cfg.menuConfig,user&&user.isPropietario);}catch(e){}aplicarTema(cfg);});
call('getHermanos',{},function(_,h){APP.hermanos=Array.isArray(h)?h:[];});
call('getContactos',{},function(_,c){APP.contactos=Array.isArray(c)?c:[];});
setTimeout(function(){
try{var hm=(window.location.hash||'').replace('#','').trim();var lm='';try{lm=localStorage.getItem('ir_last_mod')||'';}catch(ex){}
var tgt=hm||lm||'dashboard';var menu=(user&&user.menu)||[];
if(RENDERS[tgt]&&(tgt==='dashboard'||menu.indexOf(tgt)>=0))nav(tgt,true);else nav('dashboard',true);
}catch(ex){nav('dashboard',true);}
},300);
if(!window._hlSet){window._hlSet=true;window.addEventListener('hashchange',function(){var h=(window.location.hash||'').replace('#','').trim();if(h&&RENDERS[h]&&APP.mod!==h)nav(h,true);});}
resetActivityTimer();
setTimeout(initAIBot, 800);
}
function showApp(user){mostrarApp(user);}
function iniciarApp(user){mostrarApp(user);}
function logout(auto){
if(!auto){
customConfirm('¿Desea cerrar la sesión actual?',function(ok){if(ok)_doLogout(false);},{icon:'fa-sign-out-alt'});
return;
}
_doLogout(true);
}
function _doLogout(auto){
if(_actTimer)clearTimeout(_actTimer);
clearSession();APP.user=null;
var shell=document.getElementById('appShell'),login=document.getElementById('loginScreen'),cnt=document.getElementById('content');
if(shell)shell.style.display='none';
if(login){login.style.display='flex';login.classList.add('active');}
try{document.querySelectorAll('.mod').forEach(function(m){m.parentNode.removeChild(m);});}catch(ex){}
if(cnt)cnt.innerHTML='';
APP.mod='';APP.charts={};
try{if(window.history&&window.history.replaceState)window.history.replaceState(null,'',window.location.pathname);}catch(ex){}
if(auto)toast('Sesion expirada. Vuelve a ingresar.','wa',4000);
}
// A. ELIMINAR PARPADEO: Oculta el login de forma inmediata si hay sesión activa en la caché
function tryRestoreSession(){
var token=localStorage.getItem(SESSION_KEY)||sessionStorage.getItem(SESSION_KEY);
var userRaw=localStorage.getItem(SESSION_USER_KEY)||sessionStorage.getItem(SESSION_USER_KEY);
var lastTime=parseInt(localStorage.getItem(SESSION_TIME_KEY)||'0');
if(!token||!userRaw)return false;
if(Date.now()-lastTime>getInactMin()*60*1000){clearSession();return false;}
// Ocultamos la tarjeta de login instantáneamente antes de que parpadee
var s=document.getElementById('appShell'), l=document.getElementById('loginScreen');
if(s) s.style.display='flex';
if(l){ l.style.display='none'; l.classList.remove('active'); }
try{var user=JSON.parse(userRaw);
call('validateSession',{token:token},function(err,res){
if(err||!res||!res.ok){
clearSession();
if(s) s.style.display='none';
if(l){ l.style.display='flex'; l.classList.add('active'); }
return;
}
localStorage.setItem(SESSION_TIME_KEY,Date.now().toString());localStorage.setItem(SESSION_USER_KEY,JSON.stringify(res.user));APP.user=res.user;mostrarApp(res.user);
});return true;
}catch(ex){clearSession();return false;}
}
function nav(name,forceRefresh){
if(!name)name='dashboard';
if(!RENDERS[name]){if(RENDERS['dashboard'])name='dashboard';else return;}
APP.mod=name;
try{localStorage.setItem('ir_last_mod',name);if(window.history&&window.history.replaceState)window.history.replaceState(null,'','#'+name);}catch(ex){}
var tEl=document.getElementById('tbTitle');if(tEl)tEl.textContent=TITLES[name]||name;
document.querySelectorAll('.ni').forEach(function(el){el.classList.toggle('active',el.dataset.mod===name);});
if(window.innerWidth<=880)closeSidebar();
var cnt=document.getElementById('content');if(!cnt)return;
if(forceRefresh){var od=document.getElementById('mod_'+name);if(od)od.parentNode.removeChild(od);}
var el=document.getElementById('mod_'+name);var isNew=!el;
if(isNew){
el=document.createElement('div');el.id='mod_'+name;el.className='mod';
el.innerHTML='
';console.error('[nav]',name,err);}
});});}
}
function reloadMod(name){
var el=document.getElementById('mod_'+name);
if(el){el.innerHTML='';if(RENDERS&&RENDERS[name])try{RENDERS[name](el);}catch(e){el.innerHTML='
Error: '+e.message+'
';}}
}
// ── MENÚ ──────────────────────────────────────────────────
var ALL_MENU=[
{id:'dashboard',label:'Dashboard',icon:'fa-tachometer-alt',section:'PRINCIPAL'},
{id:'reportes',label:'Reporte de Grupos',icon:'fa-file-alt',section:'REPORTES'},
{id:'reporteDigital',label:'Reporte Digital',icon:'fa-mobile-alt',section:'REPORTES'},
{id:'formulario',label:'Formulario Externo',icon:'fa-clipboard-list',section:'REPORTES'},
{id:'generador',label:'Generador Reportes',icon:'fa-file-invoice',section:'REPORTES'},
{id:'hermanos',label:'Hermanos Lideres',icon:'fa-user-tie',section:'GESTION'},
{id:'cargaMasiva',label:'Carga Masiva',icon:'fa-file-upload',section:'GESTION'},
{id:'supervisores',label:'Supervisores de Sector',icon:'fa-user-shield',section:'GESTION'},
{id:'pastores',label:'Pastores de Zona',icon:'fa-church',section:'GESTION'},
{id:'ayudapastor',label:'Ayuda de Pastor',icon:'fa-hands-helping',section:'GESTION'},
{id:'seguimientos',label:'Seguimientos',icon:'fa-hands-helping',section:'GESTION'},
{id:'privilegios',label:'Privilegios',icon:'fa-crown',section:'GESTION'},
{id:'diezmos',label:'Control Diezmos',icon:'fa-coins',section:'FINANZAS'},
{id:'gastos',label:'Control de Gastos',icon:'fa-receipt',section:'FINANZAS'},
{id:'inventario',label:'Inventario',icon:'fa-boxes',section:'RECURSOS'},
{id:'insumos',label:'Insumos',icon:'fa-spray-can',section:'RECURSOS'},
{id:'envio',label:'Centro de Envios',icon:'fa-paper-plane',section:'COMUNICACIONES'},
{id:'contactos',label:'Tabla de Contactos',icon:'fa-address-book',section:'COMUNICACIONES'},
{id:'bautizos',label:'Bautizos en Agua',icon:'fa-water',section:'GESTION'},
{id:'cuadre',label:'Cuadre Dominical',icon:'fa-calculator',section:'FINANZAS'},
{id:'usuarios',label:'Usuarios',icon:'fa-user-cog',section:'ADMIN'},
{id:'bitacora',label:'Bitacora Accesos',icon:'fa-clipboard-list',section:'ADMIN'},
{id:'configuracion',label:'Configuracion',icon:'fa-cog',section:'ADMIN'},
];
// 2. CORRECCIÓN DEL MENÚ: Evita que los módulos se oculten por arrays vacíos
function buildSidebarMenu(allowedIds,isPropietario){
const nav=document.getElementById('sbNav');if(!nav)return;
// Corrección lógica de cortocircuito para arreglar el menú oculto
const allowed=new Set((allowedIds && allowedIds.length > 0) ? allowedIds : ALL_MENU.map(m=>m.id));
if(!isPropietario){
allowed.delete('configuracion');
if(!APP.user||!(APP.user.isPropietario||APP.user.puedeVerBitacora)){
allowed.delete('bitacora');
}
}
let lastSection='';let html='';
ALL_MENU.forEach(m=>{
if(!allowed.has(m.id)) return;
if(m.section!==lastSection){html+=`
`;
recargarDash();
}
function recargarDash(){
var rangoEl=document.getElementById('dashRango');
var rango=(rangoEl?rangoEl.value:'')||'mes';
const lbl=document.getElementById('dashRangoLbl');
if(lbl)lbl.textContent='('+{hoy:'Hoy',semana:'Esta semana',mes:'Este mes',trimestre:'Trimestre'}[rango]+')';
call('getDashboard',{rango},(err,d)=>{
if(!d||!d.ok)return;
_dashData=d;
buildDashKPIs(d);
buildDashCharts(d);
buildDashRecientes(d);
// Stock bajo
const stEl=document.getElementById('dashStock');
if(stEl){
var sb=d.stockBajo||[];
stEl.innerHTML=sb.length?sb.map(function(x){return '
'+esc(x.articulo)+'🔴 '+x.cantidad+'/'+x.minimo+'
'+esc(x.categoria||'')+' · '+esc(x.unidad)+'
';}).join(''):'
Stock normal
';
}
// Cronogramas
const crEl=document.getElementById('dashCron');
if(crEl){
var cron=d.proxCron||[];
var dias=['Lunes','Jueves','Domingo_Mañana','Domingo_Tarde'];
var diasLbl={Lunes:'Lun','Jueves':'Jue','Domingo_Mañana':'Dom 🕐','Domingo_Tarde':'Dom 🕥'};
crEl.innerHTML=cron.length?cron.map(function(c){
var parts=dias.filter(function(d){return c[d]==='SI'||c[d]==='1';});
var prv=c['Privilegio']||c['Servicio']||'';
return '
`;
});
}
function buildDashKPIs(d){
const sg=document.getElementById('dashStats');if(!sg)return;
var metaG=parseInt((APP.config&&APP.config.metaGrupos)||d.metaGrupos||407);
var pct=metaG>0?Math.round(d.gruposRealizados/metaG*100):0;
// Actualizar título gráfica con meta real
const titleMeta=document.getElementById('titleGruposMeta');
if(titleMeta)titleMeta.innerHTML=' Grupos Realizados vs Meta ('+metaG+') ';
sg.innerHTML=[
{l:'Líderes',v:d.lideres,i:'fa-user-tie',c:''},
{l:'Grupos '+({hoy:'Hoy',semana:'Semana',mes:'Mes',trimestre:'Trim.'}[document.getElementById('dashRango')?.value||'mes']),v:d.reportesMes,i:'fa-home',c:'o'},
{l:'Meta: '+metaG,v:pct+'%',i:'fa-bullseye',c:pct>=100?'g':'t'},
{l:'Asistencia',v:d.asistencia,i:'fa-users',c:'g'},
{l:'Ofrenda',v:fmtQ(d.ofTotal),i:'fa-hand-holding-usd',c:'p'},
{l:'Convertidos',v:d.convertidos||0,i:'fa-heart',c:'i'},
{l:'Reconciliados',v:d.reconciliados||0,i:'fa-handshake',c:'t'},
{l:'Seguimientos',v:d.segTotal||0,i:'fa-tasks',c:'p'},
{l:'Pendientes',v:d.pendientes||0,i:'fa-exclamation-triangle',c:'r'},
{l:'Cronogramas',v:(d.proxCron||[]).length,i:'fa-calendar-alt',c:'t'},
].map(s=>`
${s.v}
${s.l}
`).join('');
}
function buildDashCharts(d){
const g=d.grafica||[];
const labels=g.map(x=>x.mes);
const col1='#6366f1',col2='#f59e0b',col3='#10b981',col4='#8b5cf6',col5='#ef4444',col6='#06b6d4',col7='#ec4899',col8='#84cc16';
const colsReu=['#6366f1','#f59e0b','#10b981','#ec4899','#06b6d4'];
['chAsist','chGrupos','chConv','chTipo','chSeg','chDistAsist','chDistHnos','chReuniones'].forEach(id=>{
if(APP.charts[id]){APP.charts[id].destroy();delete APP.charts[id];}
});
// Helper: datalabels plugin config
var dlBar={id:'datalabels',afterDraw:function(chart){var ctx=chart.ctx;chart.data.datasets.forEach(function(ds,i){var meta=chart.getDatasetMeta(i);meta.data.forEach(function(bar,idx){var val=ds.data[idx];if(val>0){ctx.fillStyle='#1a3a5c';ctx.font='10px Arial';ctx.textAlign='center';ctx.textBaseline='bottom';ctx.fillText(val,bar.x,bar.y-4);}});});}};
var dlPie={id:'datalabels',afterDraw:function(chart){var ctx=chart.ctx;chart.data.datasets.forEach(function(ds){var total=ds.data.reduce(function(a,b){return a+b;},0);var meta=chart.getDatasetMeta(0);meta.data.forEach(function(arc,i){var val=ds.data[i];if(val>0){var angle=(arc.startAngle+arc.endAngle)/2;var x=arc.x+Math.cos(angle)*arc.outerRadius*0.65;var y=arc.y+Math.sin(angle)*arc.outerRadius*0.65;ctx.fillStyle='#fff';ctx.font='bold 12px Arial';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillText(val,x,y);}});});}};
// 1. Asistencia & Ofrendas
const c1=document.getElementById('chAsist');
if(c1)APP.charts.chAsist=new Chart(c1,{type:'bar',data:{labels,datasets:[
{label:'Asistencia',data:g.map(x=>x.asistencia),backgroundColor:col1,borderRadius:5,yAxisID:'y'},
{label:'Ofrendas Q',data:g.map(x=>x.ofrendas),type:'line',borderColor:col2,backgroundColor:'rgba(232,160,32,.08)',tension:.4,fill:true,pointRadius:4,pointBackgroundColor:col2,yAxisID:'y1'},
]},options:dashOpts('y','y1'),plugins:[dlBar]});
// 2. Grupos realizados vs meta
const c2=document.getElementById('chGrupos');
if(c2)APP.charts.chGrupos=new Chart(c2,{type:'bar',data:{labels,datasets:[
{label:'Grupos Realizados',data:g.map(x=>x.grupos),backgroundColor:col1,borderRadius:5},
{label:'Meta ('+parseInt((APP.config&&APP.config.metaGrupos)||d.metaGrupos||407)+')',data:g.map(function(){return parseInt((APP.config&&APP.config.metaGrupos)||d.metaGrupos||407);}),type:'line',borderColor:col5,borderDash:[6,3],borderWidth:2,pointRadius:0,fill:false},
]},options:dashOpts(),plugins:[dlBar]});
// 3. Convertidos & Reconciliados (desde Seguimientos)
const c3=document.getElementById('chConv');
if(c3)APP.charts.chConv=new Chart(c3,{type:'bar',data:{labels,datasets:[
{label:'Convertidos',data:g.map(x=>x.convertidos||0),backgroundColor:col3,borderRadius:5},
{label:'Reconciliados',data:g.map(x=>x.reconciliados||0),backgroundColor:col4,borderRadius:5},
]},options:dashOpts(),plugins:[dlBar]});
// 4. Manuales vs Digitales
const c4=document.getElementById('chTipo');
if(c4)APP.charts.chTipo=new Chart(c4,{type:'bar',data:{labels,datasets:[
{label:'Manuales',data:g.map(x=>x.manuales||0),backgroundColor:col1,borderRadius:5},
{label:'Digitales',data:g.map(x=>x.digitales||0),backgroundColor:col6,borderRadius:5},
]},options:dashOpts(),plugins:[dlBar]});
// 5. Seguimientos por estado (dona) — TODOS los estados
const c5=document.getElementById('chSeg');
if(c5){
var segData=[d.segPendientes||0,d.segEnProceso||0,d.segCompletados||0,d.segCancelados||0];
var segTotal=segData.reduce(function(a,b){return a+b;},0);
if(segTotal>0){
c5.style.display='block';
APP.charts.chSeg=new Chart(c5,{type:'doughnut',data:{labels:['⚠ Pendientes','🔄 En Proceso','✓ Completados','✕ Cancelados'],datasets:[{data:segData,backgroundColor:['#f59e0b','#3b82f6','#10b981','#ef4444'],borderWidth:0}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{font:{size:10},boxWidth:12}}}},plugins:[dlPie]});
} else {
c5.style.display='none';
var p=c5.parentElement;if(p){var m=p.querySelector('.empty-r');if(!m){m=document.createElement('div');m.style.cssText='text-align:center;padding:20px;color:var(--tx3);font-size:13px';p.style.position='relative';p.appendChild(m);}m.innerHTML='Sin seguimientos';m.className='empty-r';}
}
}
// 6. Asistencia por Distrito
const c6=document.getElementById('chDistAsist');
var da=(d.asistenciaDistrito||[]);
if(c6&&da.length){
APP.charts.chDistAsist=new Chart(c6,{type:'bar',data:{labels:da.map(function(x){return 'D'+x.distrito;}),datasets:[{label:'Asistencia',data:da.map(function(x){return x.asistencia;}),backgroundColor:['#6366f1','#f59e0b','#10b981','#8b5cf6','#06b6d4','#ef4444','#ec4899','#84cc16'],borderRadius:5}]},options:dashOpts(),plugins:[dlBar]});
}
// 7. Hermanos por Distrito (dona)
const c7=document.getElementById('chDistHnos');
var hd=(d.hermanosDistrito||[]);
if(c7&&hd.length){
var colores=['#6366f1','#f59e0b','#10b981','#8b5cf6','#06b6d4','#ef4444','#ec4899','#84cc16'];
APP.charts.chDistHnos=new Chart(c7,{type:'doughnut',data:{labels:hd.map(function(x){return 'Distrito '+x.distrito;}),datasets:[{data:hd.map(function(x){return x.count;}),backgroundColor:hd.map(function(_,i){return colores[i%colores.length];}),borderWidth:0}]},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{font:{size:9},boxWidth:10}}}},plugins:[dlPie]});
} else if(c7){
c7.parentElement.innerHTML='
Sin datos
';
}
// 8. Tipo de Reunion por Mes (barras agrupadas)
const c8=document.getElementById('chReuniones');
var gr=(d.graficaReuniones||[]);
if(c8&&gr.length){
var reuLabels=gr.map(function(x){return x.mes;});
var reuTipos=['Mixta (Reunión Regular)','Niños','Jóvenes','Mujeres','Profesionales'];
APP.charts.chReuniones=new Chart(c8,{type:'bar',data:{labels:reuLabels,datasets:reuTipos.map(function(t,i){return{label:t,data:gr.map(function(x){return x[t]||0;}),backgroundColor:colsReu[i],borderRadius:3,barPercentage:0.85};})},
options:{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{font:{size:9},boxWidth:10}}},scales:{x:{stacked:false,grid:{display:false}},y:{stacked:false,beginAtZero:true,grid:{color:'rgba(0,0,0,.04)'}},}},plugins:[dlBar]});
} else if(c8){
c8.parentElement.innerHTML='
Sin datos de reuniones
';
}
}
function dashOpts(y1id,y2id){
const scales={x:{grid:{display:false}}};
if(y1id)scales[y1id]={beginAtZero:true,grid:{color:'rgba(0,0,0,.04)'}};
else scales.y={beginAtZero:true,grid:{color:'rgba(0,0,0,.04)'}};
if(y2id)scales[y2id]={beginAtZero:true,position:'right',grid:{display:false}};
return{responsive:true,maintainAspectRatio:false,plugins:{legend:{position:'bottom',labels:{font:{size:10},boxWidth:12}}},scales};
}
function buildDashRecientes(d){
const re=document.getElementById('dashRec');if(!re)return;
const recs=d.recientes||[];
re.innerHTML=recs.length?recs.map(r=>{
var met=r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':'');
var tr=r['Tipo de Reporte']||'';
if(tr==='Reporte Digital'||tr==='Reporte Físico')tr='—';
return `
`);
w.document.close();
}
// Imprimir todas las gráficas juntas
function imprimirDash(){
if(!_dashData){toast('Carga el dashboard primero','wa');return;}
const ids=[['chAsist','Asistencia & Ofrendas'],['chGrupos','Grupos vs Meta'],['chConv','Convertidos & Reconciliados'],['chTipo','Manuales vs Digitales'],['chSeg','Seguimientos'],['chDistAsist','Asistencia x Distrito'],['chDistHnos','Hermanos x Distrito'],['chReuniones','Tipo de Reunión por Mes']];
const imgs=ids.map(([id,t])=>{const c=document.getElementById(id);return c?{img:c.toDataURL('image/png'),t}:null;}).filter(Boolean);
const d=_dashData;const rango=document.getElementById('dashRango')?.value||'mes';
const w=window.open('','_blank','width=1100,height=800');
if(!w){toast('Activa ventanas emergentes para imprimir','wa',5000);return;}
w.document.write(`Dashboard — Iglesia Restauración
`);
w.document.close();
setTimeout(function(){w.print();w.onafterprint=function(){w.close();};},500);
}
// ══════════════════════════════════════════════════════════
// REPORTE DE GRUPOS
// ══════════════════════════════════════════════════════════
// REPORTE DE GRUPOS (con tabs Todos / Pendientes)
// ══════════════════════════════════════════════════════════
let _rep=[];let _repTab='todos';let _qRep='';
function renderReportes(el){
el.innerHTML=`
Reporte de Grupos
${permisoEditar()?``:''}
Filtro Rápido — ¿Quién ya entregó su reporte?
Selecciona una fecha y filtros, luego presiona Consultar
Fecha
Código
Líder
Sup.Sector
Anfitrión
H.Ini
H.Fin
AGF
H
Am
N
OfIg
OfBus
OfTot
Total
Reporte
Ofrenda
Acciones
`;
_repTab='todos';loadRep();
}
function switchRepTab(tab){
_repTab=tab;
['todos','pendientes'].forEach(t=>{
const btn=document.getElementById('tab_'+t);if(!btn)return;
const active=t===tab;
btn.style.background=active?'#fff':'var(--bg3)';
btn.style.color=active?'var(--pr)':'var(--tx2)';
btn.style.borderBottom=active?'2px solid var(--pr)':'2px solid transparent';
});
renderRepTable(_rep);
}
function abrirFiltroAsistencia(){
const panel=document.getElementById('panelAsistencia');
if(!panel) return;
const isOpen = panel.style.display==='block';
panel.style.display = isOpen ? 'none' : 'block';
if(!isOpen){
// Poblar selects con datos frescos
if(!APP.hermanos||!APP.hermanos.length){
call('getHermanos',{},function(_,h){
APP.hermanos=Array.isArray(h)?h:[];
poblarSelectsFiltro();
});
} else {
poblarSelectsFiltro();
}
}
}
function poblarSelectsFiltro(){
var h=APP.hermanos||[];
var pzEl=document.getElementById('faPastorZona');
var saEl=document.getElementById('faArea');
var ssEl=document.getElementById('faSector');
var lEl=document.getElementById('faLider');
var curPZ=pzEl?pzEl.value:'';
var curSA=saEl?saEl.value:'';
var curSS=ssEl?ssEl.value:'';
var curL=lEl?lEl.value:'';
// Pastor de Zona (nivel más alto)
var pzList=[...new Set(h.map(function(x){return x['Pastor Zona']||'';}).filter(Boolean))].sort();
if(pzEl) pzEl.innerHTML=''+pzList.map(function(p){return '';}).join('');
// Ayuda de Pastor / Sup Área (filtra por PZ si está seleccionado)
var hSA=curPZ?h.filter(function(x){return x['Pastor Zona']===curPZ;}):h;
var saList=[...new Set(hSA.map(function(x){return x['Sup AreaL']||'';}).filter(Boolean))].sort();
if(saEl) saEl.innerHTML=''+saList.map(function(s){return '';}).join('');
// Sup Sector (filtra por PZ y SA)
var hSS=h;
if(curPZ) hSS=hSS.filter(function(x){return x['Pastor Zona']===curPZ;});
if(curSA) hSS=hSS.filter(function(x){return x['Sup AreaL']===curSA;});
var ssList=[...new Set(hSS.map(function(x){return x['Sup SectorL']||'';}).filter(Boolean))].sort();
if(ssEl) ssEl.innerHTML=''+ssList.map(function(s){return '';}).join('');
// Líderes (filtra por todos los niveles)
var hL=h;
if(curPZ) hL=hL.filter(function(x){return x['Pastor Zona']===curPZ;});
if(curSA) hL=hL.filter(function(x){return x['Sup AreaL']===curSA;});
if(curSS) hL=hL.filter(function(x){return x['Sup SectorL']===curSS;});
hL=hL.slice().sort(function(a,b){return (a['NombreL']||'').localeCompare(b['NombreL']||'');});
if(lEl) lEl.innerHTML=''+hL.map(function(x){return '';}).join('');
}
// ── BÚSQUEDA EN ASISTENCIA HOY ──
function buscarAsistencia(q){
q = q.trim().toLowerCase();
const el = document.getElementById('faSearchRes');
if(!q || q.length < 2){ if(el)el.style.display='none'; return; }
const h = APP.hermanos||[];
var pzSet={}, areaSet={}, sectorSet={}, lids=[];
const seen = {};
h.forEach(function(x){
var pz = (x['Pastor Zona']||'').toLowerCase();
var area = (x['Sup AreaL']||'').toLowerCase();
var sector = (x['Sup SectorL']||'').toLowerCase();
var nom = (x['NombreL']||'').toLowerCase();
var cod = (x['CodigoL']||'').toLowerCase();
if(seen[x['CodigoL']]) return;
if(pz.includes(q) && !pzSet[x['Pastor Zona']]){ pzSet[x['Pastor Zona']]=true; seen[x['CodigoL']]=true; }
if(area.includes(q) && !areaSet[x['Sup AreaL']]){ areaSet[x['Sup AreaL']]=true; seen[x['CodigoL']]=true; }
if(sector.includes(q) && !sectorSet[x['Sup SectorL']]){ sectorSet[x['Sup SectorL']]=true; seen[x['CodigoL']]=true; }
if(nom.includes(q) || cod.includes(q)){ lids.push(x); seen[x['CodigoL']]=true; }
});
var html = '';
Object.keys(pzSet).forEach(function(n){
var c = h.filter(function(x){ return x['Pastor Zona']===n; }).length;
html += '
🏛 '+esc(n)+'('+c+' líderes)
';
});
Object.keys(areaSet).forEach(function(n){
var c = h.filter(function(x){ return x['Sup AreaL']===n; }).length;
html += '
👤 '+esc(n)+'('+c+' líderes)
';
});
Object.keys(sectorSet).forEach(function(n){
var c = h.filter(function(x){ return x['Sup SectorL']===n; }).length;
html += '
📋 '+esc(n)+'('+c+' líderes)
';
});
lids.forEach(function(x){
html += '
🙋 '+esc(x['NombreL'])+'('+esc(x['CodigoL'])+')
';
});
if(!html){ el.style.display='none'; return; }
el.innerHTML = html;
el.style.display = 'block';
}
function seleccionarAsistencia(type, value){
document.getElementById('faSearchRes').style.display='none';
document.getElementById('faSearch').value = value;
document.getElementById('faPastorZona').value = '';
document.getElementById('faArea').value = '';
document.getElementById('faSector').value = '';
document.getElementById('faLider').value = '';
var h = APP.hermanos||[];
if(type === 'pz'){
document.getElementById('faPastorZona').value = value;
} else if(type === 'area'){
var her = h.find(function(x){ return x['Sup AreaL']===value; });
if(her && her['Pastor Zona']) document.getElementById('faPastorZona').value = her['Pastor Zona'];
document.getElementById('faArea').value = value;
} else if(type === 'sector'){
var her = h.find(function(x){ return x['Sup SectorL']===value; });
if(her){
if(her['Pastor Zona']) document.getElementById('faPastorZona').value = her['Pastor Zona'];
if(her['Sup AreaL']) document.getElementById('faArea').value = her['Sup AreaL'];
}
document.getElementById('faSector').value = value;
} else if(type === 'lider'){
var her = h.find(function(x){ return x['CodigoL']===value; });
if(her){
if(her['Pastor Zona']) document.getElementById('faPastorZona').value = her['Pastor Zona'];
if(her['Sup AreaL']) document.getElementById('faArea').value = her['Sup AreaL'];
if(her['Sup SectorL']) document.getElementById('faSector').value = her['Sup SectorL'];
}
document.getElementById('faLider').value = value;
}
cascadeFiltros();
aplicarFiltroAsistencia();
}
// Cerrar búsqueda al hacer clic fuera
document.addEventListener('click', function(e){
var el = document.getElementById('faSearchRes');
if(el && !e.target.closest('#faSearch') && !e.target.closest('#faSearchRes')) el.style.display='none';
});
// ── RENDER JERARQUÍA (PZ → Área → Líder) ──
function renderJerarquiaAsistencia(repDia, pendientes, hermanos){
// Construir lista plana: cada reporte es una fila, cada líder sin reporte es otra
var items=[];
// 1. Agregar todos los reportes del ciclo
repDia.forEach(function(r){
var h=hermanos.find(function(x){return String(x['CodigoL'])===String(r['Codigo']||r['CodigoL']||'')||String(x['NombreL']||'').trim().toLowerCase()===String(r['Lider']||r['Datos de Lider']||'').trim().toLowerCase();});
if(!h)h={};
var ofR=(r['Ofrenda Recibida']||'').trim();
var ok=ofR==='SI'||ofR==='Recibida';
items.push({h:h,r:r,ok:ok,tipo:'rep'});
});
// 2. Agregar líderes sin reporte
pendientes.forEach(function(h){
items.push({h:h,r:null,ok:false,tipo:'pend'});
});
// Ordenar por PZ → Area → Fecha (reportes) → Nombre (pendientes)
items.sort(function(a,b){
var pzA=a.h['Pastor Zona']||'',pzB=b.h['Pastor Zona']||'';
if(pzA!==pzB)return pzA.localeCompare(pzB);
var arA=a.h['Sup AreaL']||'',arB=b.h['Sup AreaL']||'';
if(arA!==arB)return arA.localeCompare(arB);
if(a.tipo!==b.tipo)return a.tipo==='rep'?-1:1;
if(a.tipo==='rep'&&b.tipo==='rep'){
var dA=a.r['Fecha']||'',dB=b.r['Fecha']||'';
if(dA!==dB)return dA.localeCompare(dB);
var hA=a.r['Hora Inicio']||'',hB=b.r['Hora Inicio']||'';
return hA.localeCompare(hB);
}
return (a.h['NombreL']||'').localeCompare(b.h['NombreL']||'');
});
var html='',curPZ='',curArea='';
var pzOK=0,pzTotal=0,areaOK=0,areaTotal=0;
function cerrarPZ(){
if(curPZ==='')return '';
var p=pzTotal>0?Math.round(pzOK/pzTotal*100):0;
var pc=p>=50?'var(--ok)':'var(--err)';
return '
';
}
if(it.tipo==='rep'){
var r=it.r,agf=r['Asistencia Grupo Familiar']||0,ofVal=r['Ofrenda Total']||'';
var ofR=(r['Ofrenda Recibida']||'').trim();
var metodo=r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':'');
var reunionTipo=r['Tipo de Reporte']||'';
if(reunionTipo==='Reporte Digital'||reunionTipo==='Reporte Físico')reunionTipo='Mixta';
var fechaCorta=r['Fecha']?r['Fecha'].substring(5):'';
var bg=it.ok?'#f0fff4':'#fffbe6';
var ico=it.ok?'':'';
var ofBadge=ofR==='SI'||ofR==='Recibida'
?'Ofrenda ✓'
:'Ofrenda ⏳';
var etiqMetodo=metodo==='Digital'?'D':'F';
html+='
'
+res.innerHTML
+'setTimeout(()=>{window.print();setTimeout(()=>window.close(),400);},350);<'+'/scr'+'ipt>';
abrirVentanaImpresion(h);
}
function loadRep(){
var f={desde:g('rD')||fom(),hasta:g('rH')||today()};
var l=g('rL'),ss=g('rSS'),pz=g('rPZ'),di=g('rDist'),zo=g('rZona');
if(l)f.lider=l;if(ss)f.supSector=ss;if(pz)f.pastorZona=pz;
if(di)f.distrito=di;if(zo)f.zona=zo;if(g('rPend'))f.pendientes=true;
var ck='rpt_'+f.desde+'_'+f.hasta+(f.pastorZona?'_pz:'+f.pastorZona:'')+(f.supSector?'_ss:'+f.supSector:'')+(f.distrito?'_di:'+f.distrito:'')+(f.lider?'_ld:'+f.lider:'');
var cached=sessionStorage.getItem(ck);
var v=parseInt(sessionStorage.getItem('rpt_ver')||'0');
if(cached&&Date.now()-parseInt(sessionStorage.getItem(ck+'_t')||'0')<30000){
try{var cd=JSON.parse(cached);if(cd.v===v){_rep=cd.d;renderRepTable(_rep);renderRepRes(_rep);return;}}catch(e){}
}
loading(true);call('getReportes',f,function(_,d){loading(false);_rep=Array.isArray(d)?d:[];try{sessionStorage.setItem(ck,JSON.stringify({v:d.v||v,d:_rep}));sessionStorage.setItem(ck+'_t',Date.now());}catch(e){}var pend=_rep.filter(function(r){return r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']==='';});var bt=document.getElementById('badge_todos'),bp=document.getElementById('badge_pendientes');if(bt)bt.textContent=_rep.length;if(bp)bp.textContent=pend.length;renderRepTable(_rep);renderRepRes(_rep);});
}
function limpiarFiltrosRep(){
['rDist','rPZ','rSA','rSS','rL'].forEach(id=>{
const el=document.getElementById(id);
if(el)el.value='';
});
const fi=document.getElementById('filtrosActivos');
if(fi)fi.style.display='none';
loadRep();
}
function actualizarSelectores(){loadRep();}
function getVisibleRep(){
let d=_repTab==='pendientes'?_rep.filter(r=>r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']===''):_rep;
if(_qRep)d=d.filter(r=>JSON.stringify(r).toLowerCase().includes(_qRep.toLowerCase()));
// Filtros adicionales
var rReu=document.getElementById('rReu');if(rReu&&rReu.value)d=d.filter(function(r){var t=r['Tipo de Reporte']||'';if(t==='Reporte Digital'||t==='Reporte Físico')t='Mixta (Reunión Regular)';return t===rReu.value;});
var rMet=document.getElementById('rMetodo');if(rMet&&rMet.value)d=d.filter(function(r){var m=r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':'');return m===rMet.value;});
return d;
}
function renderRepRes(d){
const el=document.getElementById('repRes');if(!el)return;
const pend=d.filter(r=>r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']==='').length;
const res={total:d.length,asist:d.reduce((s,r)=>s+num(r['Asistencia Grupo Familiar']),0),ofIg:d.reduce((s,r)=>s+num(r['Ofrenda Iglesia']),0),ofBus:d.reduce((s,r)=>s+num(r['Ofrenda Bus']),0),ofTot:d.reduce((s,r)=>s+num(r['Ofrenda Total']),0),tot:d.reduce((s,r)=>s+num(r['Total']),0)};
el.innerHTML=[{l:'Reportes',v:res.total,c:''},{l:'Asist. GF',v:res.asist,c:'g'},{l:'Ofr. Iglesia',v:fmtQ(res.ofIg),c:'o'},{l:'Ofr. Bus',v:fmtQ(res.ofBus),c:'p'},{l:'Ofr. Total',v:fmtQ(res.ofTot),c:'r'},{l:'Total Cultos',v:res.tot,c:'i'},{l:'⚠ Pendientes',v:pend,c:'r'}].map(s=>`
${s.v}
${s.l}
`).join('');
}
function renderRepTable(){
const data=getVisibleRep();
const tb=document.getElementById('repTb');if(!tb)return;
if(!data.length){
const msg=_repTab==='pendientes'?'Sin reportes con ofrenda pendiente 👍':'No hay reportes en este período';
tb.innerHTML=`
${msg}
`;
return;
}
tb.innerHTML=data.map(r=>{
const isPend=r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']==='';
var metodo=r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':'');
var reunionTipo=r['Tipo de Reporte']||'';
if(reunionTipo==='Reporte Digital'||reunionTipo==='Reporte Físico')reunionTipo='Mixta (Reunión Regular)';
return `
${fmtD(r['Fecha'])}
${esc(r['Codigo'])}
${esc(r['Lider'])}
${esc(r['Sup Sector'])}
${esc(r['Anfitrion'])}
${esc(r['Hora Inicio'])}
${esc(r['Hora Final'])}
${r['Asistencia Grupo Familiar']||0}
${r['Hnos']||0}
${r['Amigos']||0}
${r['Niños']||0}
${r['Ofrenda Iglesia']||''}
${r['Ofrenda Bus']||''}
${r['Ofrenda Total']||''}
${r['Total']||0}
${metodo||'—'} ${esc(reunionTipo)}
${isPend
?``
:`✓ Recibida`
}
${permisoEditar()?`
`:''}
`;
}).join('');
}
function filterRep(q){_qRep=q;renderRepTable();}
// CONFIRMAR OFRENDA — UN BOTÓN, SIN PEDIR MONTO
function confirmarOfrendaRapido(id,lider){
openModal('Confirmar Ofrenda Recibida',`
💰
${esc(lider)}
¿Confirmas que recibiste físicamente la ofrenda de este grupo familiar?
El monto ya fue registrado en el reporte digital. Solo confirma la recepción física.
`,
`
`);
}
function ejecutarConfirmacion(id){
loading(true);
call('guardarOfrendaRecibida',{id},(_,res)=>{
loading(false);
if(res?.ok){toast('✓ Ofrenda marcada como recibida','ok');closeModal();loadRep();}
else toast('Error: '+(res?.msg||''),'er');
});
}
function repForm(r){
r=r||{};
const hOpts=APP.hermanos.map(h=>``).join('');
const tSeg=['Convertido','Reconciliación','Visita Programada','Bautizo en Agua','Sanidad','Oración','Otro'];
const tReu=TIPO_REUNION; const tMetodo=METODO_REPORTE;
// Seguimientos dinámicos — solo 1 inicial, se agregan con botón +
let segsHTML = '
';
// Calcular cuántos seguimientos editables mostrar (si editando con datos, mostrar todos los que tengan valores)
let initialSeg = 1;
for (let i = 10; i >= 2; i--) {
let nk = i === 1 ? 'Nombre Persona' : 'Nombre Persona ' + i;
if (r[nk]) { initialSeg = i; break; }
}
let tSegOpts = tSeg.map(t=>``).join('');
for (let i = 1; i <= initialSeg; i++) {
let nk = i === 1 ? 'Nombre Persona' : 'Nombre Persona ' + i;
let tk = i === 1 ? 'Tipo de Seguimiento' : 'Tipo de Seguimiento ' + i;
let nv = esc(r[nk]||'');
let tv = r[tk]||'';
let selOpts = '' + tSeg.map(t=>``).join('');
segsHTML += `
`;
}
segsHTML += '
';
_segRepCount = initialSeg; return `
Búsqueda por Código de Líder
Info del Líder (auto-completado)
Datos de la Reunión
Asistencia
Asistencia a cultos en la iglesia
Ofrendas
Seguimientos (Agregar manualmente · máx 10)
${segsHTML}
`;
}
// ── BÚSQUEDA DE LÍDER POR TECLADO (para rf_codigo_input) ──────
function filtrarLiderInput(val){
var sel=document.getElementById('rf_codigo');
if(!sel)return;
var q=val.toLowerCase().trim();
if(!q){sel.value='';return;}
var opts=sel.options;
var bestMatch = null;
for(var i=1;i=2) buscarLider(val);
}
function buscarLiderSel(val){
document.getElementById('rf_codigo_input').value=val;
buscarLider(val);
}
function buscarLider(codigo){
const st=document.getElementById('codStatus');if(!codigo){if(st)st.textContent='';return;}
// Buscar primero en APP.hermanos local
const h=APP.hermanos.find(x=>String(x['CodigoL'])===String(codigo));
const fill=(hd)=>{
if(st){st.className='cod-status cod-ok';st.innerHTML=' '+esc(hd['NombreL']);}
// Llenar TODOS los campos del formulario con datos del líder
[['rf_lider',hd['NombreL']||''],['rf_supSector',hd['Sup SectorL']||hd['SupSector']||''],['rf_supArea',hd['Sup AreaL']||hd['SupArea']||''],['rf_pastorZona',hd['Pastor Zona']||hd['PastorZona']||''],['rf_anfitrion',hd['Anfitrion']||hd['Anfitrion']||''],['rf_dir',hd['Direccion']||hd['Direccion']||'']].forEach(function(pair){
var id=pair[0],val=pair[1];
var el=document.getElementById(id);
if(el)el.value=val||'';
});
};
if(h){fill(h);return;}
// Si no está en local, buscar en el servidor
call('getHermanoByCodigo',{codigo:codigo},function(_,res){
if(res&&res.ok&&res.data)fill(res.data);
else if(st){st.className='cod-status cod-err';st.innerHTML=' Código no encontrado';}
});
}
function calcAsist(){const h=num(g('rf_hnos')),a=num(g('rf_amigos')),n=num(g('rf_ninos'));const el=document.getElementById('rf_agf');if(el)el.value=h+a+n;}
function calcTotal(){const m=num(g('rf_martes')),j=num(g('rf_jueves')),d=num(g('rf_domingo')),o=num(g('rf_otros'));const el=document.getElementById('rf_total');if(el)el.value=m+j+d+o;}
function calcOfrenda(){const i=num(g('rf_ofIg')),b=num(g('rf_ofBus'));const el=document.getElementById('rf_ofTot');if(el)el.value=(i+b).toFixed(2);}
function mFoot(saveFn,label){
label=label||'Guardar';
return ''
+'';
}
function mFootXL(saveFn,label){
label=label||'Guardar';
return ''
+'';
}
function newReporte(){openModal('Nuevo Reporte de Grupo',repForm(),"",'xl');}
function editRep(id){
const r=_rep.find(x=>x['ID']===id);
if(!r)return;
openModal(
'Editar Reporte',
repForm(r),
``,
'xl'
);
setTimeout(()=>buscarLider(r['Codigo']),100);
}
function viewRep(id){
const r=_rep.find(x=>x['ID']===id);if(!r)return;
const isPend=r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']==='';
var metodo=r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':'');
var reunionTipo=r['Tipo de Reporte']||'';
if(reunionTipo==='Reporte Digital'||reunionTipo==='Reporte Físico')reunionTipo='Mixta (Reunión Regular)';
const isDigital=metodo==='Digital';
const campos=[
['📅 Fecha',fmtD(r['Fecha'])],['⏰ Hora',r['Hora Inicio']+' → '+r['Hora Final']],
['🔑 Código',r['Codigo']],['👤 Líder',r['Lider']],
['🏘 Sup. Sector',r['Sup Sector']],['🗺 Ayuda de Pastor',r['Sup Area']],
['🏛 Pastor de Zona',r['Pastor Zona']],
['🏠 Anfitrión',r['Anfitrion']],['📍 Dirección',r['Direccion']],
['📋 Reporte',metodo],['📋 Tipo Reunión',reunionTipo],
];
const asist=[
['Total AGF',r['Asistencia Grupo Familiar']],['Hermanos',r['Hnos']],
['Amigos',r['Amigos']],['Niños',r['Niños']],
['Total Cultos',r['Total']],['Martes',r['Martes']],
['Jueves',r['Jueves']],['Domingo',r['Domingo']],
];
const ofr=[
['Ofrenda Iglesia','Q '+r['Ofrenda Iglesia']],
['Ofrenda Bus','Q '+r['Ofrenda Bus']],
['Ofrenda Total','Q '+r['Ofrenda Total']],
['Ofrenda Recibida',isPend?'⚠ PENDIENTE':r['Ofrenda Recibida']],
];
const segs=[[r['Nombre Persona'],r['Tipo de Seguimiento']],[r['Nombre Persona 2'],r['Tipo de Seguimiento 2']],[r['Nombre Persona 3'],r['Tipo de Seguimiento 3']],[r['Nombre Persona 4'],r['Tipo de Seguimiento 4']],[r['Nombre Persona 5'],r['Tipo de Seguimiento 5']],[r['Nombre Persona 6'],r['Tipo de Seguimiento 6']],[r['Nombre Persona 7'],r['Tipo de Seguimiento 7']],[r['Nombre Persona 8'],r['Tipo de Seguimiento 8']],[r['Nombre Persona 9'],r['Tipo de Seguimiento 9']],[r['Nombre Persona 10'],r['Tipo de Seguimiento 10']]].filter(s=>s[0]);
openModal('Detalle del Reporte — '+esc(r['Lider']),
`
${isDigital?'
Reporte Digital — enviado por el líder desde el formulario externo
':''}
${isPend?'
Ofrenda pendiente de entrega física
':''}
Información General
${campos.map(([k,v])=>`
${k}
${esc(v)||'—'}
`).join('')}
Asistencia
${asist.map(([k,v])=>`
${esc(v)||0}
${k}
`).join('')}
Ofrendas
${ofr.map(([k,v])=>`
${k}
${esc(v)||'—'}
`).join('')}
${segs.length?`
Seguimientos (${segs.length})
${segs.map(([nom,tipo])=>`
${esc(nom)}
${esc(tipo)||'—'}
`).join('')}
`:''}
${permisoEditar()?`
Editar info del líder
`:''}
`,
`
${isPend?``:''}
${permisoEditar()?``:''}`
,'xl');
}
function toggleEditLider(id){
const p=document.getElementById('editLiderPanel_'+id);
const ico=document.getElementById('editLiderIco_'+id);
if(p){const open=p.style.display==='none';p.style.display=open?'block':'none';if(ico)ico.className=open?'fas fa-chevron-up':'fas fa-chevron-down';}
}
function guardarEditLider(id){
const r=_rep.find(x=>x['ID']===id);if(!r)return;
const updates={...r,Anfitrion:document.getElementById('el_anf_'+id)?.value||r['Anfitrion'],Direccion:document.getElementById('el_dir_'+id)?.value||r['Direccion'],'Hora Inicio':document.getElementById('el_hi_'+id)?.value||r['Hora Inicio'],'Hora Final':document.getElementById('el_hf_'+id)?.value||r['Hora Final'],token:getToken()};
loading(true);call('saveReporte',updates,(_,res)=>{loading(false);if(res?.ok){toast('Cambios guardados','ok');loadRep();}else toast('Error guardando','er');});
}
// ── Seguimientos dinámicos para repForm ──────────────
let _segRepCount=1;
function addSegRep(){
if(_segRepCount>=10){toast('Máximo 10 seguimientos','wa');return;}
_segRepCount++;
const c=document.getElementById('segRepContainer');
if(!c)return;
const i=_segRepCount;
const d=document.createElement('div');
d.id='sri_'+i;
d.style.cssText='background:var(--bg3);border-radius:8px;padding:10px;margin-bottom:8px;position:relative;display:flex;flex-wrap:wrap;gap:8px;align-items:flex-end';
d.innerHTML=''
+''
+'';
c.appendChild(d);
if(_segRepCount>=10)document.getElementById('segAddBtn')?.style.setProperty('display','none');
}
function removeSegRep(i){
const d=document.getElementById('sri_'+i);
if(d){d.remove();_segRepCount--;}
const addBtn=document.getElementById('segAddBtn');
if(addBtn)addBtn.style.display='';
}
function saveRepForm(id){
const data={'ID':id||'','Datos de Lider':g('rf_lider'),'Codigo':g('rf_codigo'),'Lider':g('rf_lider'),'Sup Sector':g('rf_supSector'),'Sup Area':g('rf_supArea'),'Pastor Zona':g('rf_pastorZona'),'Fecha':g('rf_fecha'),'Anfitrion':g('rf_anfitrion'),'Direccion':g('rf_dir'),'Hora Inicio':g('rf_hInicio'),'Hora Final':g('rf_hFinal'),'Hnos':g('rf_hnos'),'Amigos':g('rf_amigos'),'Niños':g('rf_ninos'),'Asistencia Grupo Familiar':g('rf_agf'),'Ofrenda Iglesia':g('rf_ofIg'),'Ofrenda Bus':g('rf_ofBus'),'Ofrenda Total':g('rf_ofTot'),'Martes':g('rf_martes'),'Jueves':g('rf_jueves'),'Domingo':g('rf_domingo'),'Otros':g('rf_otros'),'Total':g('rf_total'),'Reporte':g('rf_metodo'),'Tipo de Reporte':g('rf_tipo')};
// Recolectar seguimientos dinámicamente (los que estén visibles)
const segItems=document.querySelectorAll('#segRepContainer > div');
let idx=1;
segItems.forEach(function(el){
const num=parseInt(el.id.replace('sri_',''));
const nom=document.getElementById('rf_np'+num)?.value||'';
const tipo=document.getElementById('rf_ts'+num)?.value||'';
if(idx===1){
data['Nombre Persona']=nom;
data['Tipo de Seguimiento']=tipo;
} else {
data['Nombre Persona '+idx]=nom;
data['Tipo de Seguimiento '+idx]=tipo;
}
idx++;
});
// Limpiar seguimientos no usados
for(let i=idx;i<=10;i++){
if(i===1){if(!data['Nombre Persona']&&!data['Tipo de Seguimiento'])continue;}
else {data['Nombre Persona '+i]='';data['Tipo de Seguimiento '+i]='';}
}
if(!data['Codigo']){toast('Selecciona un código de líder','wa');return;}
if(!data['Fecha']){toast('La fecha es requerida','wa');return;}
data.token=getToken();
loading(true);call('saveReporte',data,(_,res)=>{loading(false);if(res?.ok){toast('Reporte guardado','ok');closeModal();loadRep();}else toast('Error: '+(res?.msg||''),'er');});
}
function delRep(id){customConfirm('¿Estás seguro de eliminar este reporte? Esta acción no se puede deshacer.',function(ok){if(ok){loading(true);call('deleteReporte',{id},(_,res)=>{loading(false);if(res?.ok){toast('Eliminado','ok');loadRep();}else toast('Error al eliminar','er');});}},{icon:'fa-trash-alt',color:'#e74c3c',danger:true});}
function printRep(id){
const r=_rep.find(x=>x['ID']===id);if(!r)return;
const isPend=r['Ofrenda Recibida']==='Pendiente'||r['Ofrenda Recibida']==='';
var reunionTipo=r['Tipo de Reporte']||'';
if(reunionTipo==='Reporte Digital'||reunionTipo==='Reporte Físico')reunionTipo='Mixta (Reunión Regular)';
const h=`
${[['Líder',r['Lider'],''],['Sup. Sector',r['Sup Sector'],''],['Anfitrión',r['Anfitrion'],''],['Fecha',fmtD(r['Fecha']),''],['Hora',r['Hora Inicio']+' – '+r['Hora Final'],''],['AGF',r['Asistencia Grupo Familiar'],''],['H/Am/N',r['Hnos']+'/'+r['Amigos']+'/'+r['Niños'],''],['Total Cultos',r['Total'],''],['M/J/D/Otros',r['Martes']+'/'+r['Jueves']+'/'+r['Domingo']+'/'+r['Otros'],''],['Ofr.Iglesia','Q '+r['Ofrenda Iglesia'],''],['Ofr.Bus','Q '+r['Ofrenda Bus'],''],['Ofr.Total','Q '+r['Ofrenda Total'],''],['Seg.1',(r['Nombre Persona']||''),''],['Seg.2',(r['Nombre Persona 2']||''),''],['Seg.3',(r['Nombre Persona 3']||''),''],['Seg.4',(r['Nombre Persona 4']||''),''],['Seg.5',(r['Nombre Persona 5']||''),''],['Seg.6',(r['Nombre Persona 6']||''),''],['Seg.7',(r['Nombre Persona 7']||''),''],['Seg.8',(r['Nombre Persona 8']||''),''],['Seg.9',(r['Nombre Persona 9']||''),''],['Seg.10',(r['Nombre Persona 10']||''),''],['Reporte',r['Reporte']||(r['Tipo de Reporte']==='Reporte Digital'?'Digital':r['Tipo de Reporte']==='Reporte Físico'?'Físico':''),''],['Tipo Reunión',reunionTipo,''],['Ofrenda Recibida',isPend?'⚠ PENDIENTE':r['Ofrenda Recibida'],isPend?'pend':'']].map(([k,v,cl])=>`
${k}
${esc(v)||'—'}
`).join('')}
`;
const w=abrirVentanaImpresion(h);
}
function imprimirReportes(){loading(true);call('generarReporte',{desde:g('rD'),hasta:g('rH'),lider:g('rL')},(_,res)=>{loading(false);if(!res?.ok){toast('Error','er');return;}abrirVentanaImpresion(res.html);if(res.pdfUrl)toast('PDF guardado en Drive','ok',4000);});}
// Función central de impresión — compatible web y ejecutable
function abrirVentanaImpresion(html){
// Inyectar script de impresión robusto que no cierra la ventana prematuramente
const printScript='window.addEventListener(\'load\',function(){setTimeout(function(){try{window.print();}catch(e){}},600);});<'+'/scr'+'ipt>';
const htmlConPrint=html.replace('
⛪ Cronograma de Servicios — ${mes} ${hoy.getFullYear()}
',printScript+'');
const w=window.open('','_blank','width=1200,height=800,scrollbars=yes,resizable=yes');
if(!w){
// Fallback: blob URL para ejecutable/entornos sin popups
try{
const blob=new Blob([htmlConPrint],{type:'text/html;charset=utf-8'});
const url=URL.createObjectURL(blob);
window.open(url,'_blank');
}catch(e){toast('Habilita ventanas emergentes para imprimir','wa',6000);}
return;
}
w.document.open();w.document.write(htmlConPrint);w.document.close();
}
// ══════════════════════════════════════════════════════════
// REPORTE DIGITAL
// ══════════════════════════════════════════════════════════
function renderReporteDigital(el){
el.innerHTML=`
Reporte Digital
📱
Link del Formulario Digital
Comparte este enlace con tus líderes para que llenen el reporte desde su celular. Los datos llegan directamente al sistema marcados como ⚠ Pendiente hasta que confirmes la ofrenda.
`;
call('getFormHtml',{},function(_,res){
var cont=document.getElementById('formEmbed');if(!cont)return;
if(!res||!res.ok){cont.innerHTML='
Error al cargar: '+(res&&res.msg||'')+'
';return;}
var ifr=document.createElement('iframe');ifr.style='width:100%;height:calc(100vh - 200px);min-height:500px;border:none;border-radius:8px';
cont.innerHTML='';cont.appendChild(ifr);
ifr.contentDocument.open();ifr.contentDocument.write(res.html);ifr.contentDocument.close();
});
}
// ══════════════════════════════════════════════════════════
// GENERADOR DE REPORTES
// ══════════════════════════════════════════════════════════
let _genData=null;
function renderGenerador(el){
const supSects=[...new Set(APP.hermanos.map(h=>h['Sup SectorL']).filter(Boolean))].sort();
const supAreas=[...new Set(APP.hermanos.map(h=>h['Sup AreaL']).filter(Boolean))].sort();
var dists=[...new Set(APP.hermanos.map(function(h){return h['Distrito']}).filter(Boolean))].sort();
var zonas=[...new Set(APP.hermanos.map(function(h){return h['Zona']}).filter(Boolean))].sort();
el.innerHTML=`
Generador de Reportes e Informe Exprés
Configuración del Reporte
Repositorio de Reportes
Panel de Previsualización y Control Contable
Configure los filtros o presione "Ver Reporte Exprés de Cuadre"
`;
loadRepo();
}
// ── CARGA EL REPOSITORIO DE REPORTES GENERADOS ────────────────
function toggleRepoSort(){
window._repoAsc=!window._repoAsc;
var btn=document.getElementById('repoSortBtn');
if(btn)btn.innerHTML=(window._repoAsc?' Asc':' Desc');
loadRepo();
}
function loadRepo(){
var el2=document.getElementById('genRepo');
if(!el2) return;
call('getGeneradores',{},function(_,d){
var data=Array.isArray(d)?d:[];
if(!data.length){el2.innerHTML='
Sin reportes generados
';return;}
// Ordenar por No Serie descendente (ultimo creado primero)
window._repoAsc=window._repoAsc||false;
data.sort(function(a,b){var va=a['No Serie']||'',vb=b['No Serie']||'';return window._repoAsc?va.localeCompare(vb):vb.localeCompare(va);});
// Aplicar filtro por fecha exacta
var ff=document.getElementById('repoFiltroFecha');
if(ff&&ff.value) data=data.filter(function(r){return r['Fecha Inicio']===ff.value||r['Fecha Fin']===ff.value;});
if(!data.length){el2.innerHTML='
Sin reportes para los filtros seleccionados
';return;}
el2.style.maxHeight='calc(100vh - 420px)';el2.style.overflowY='auto';
el2.innerHTML=data.map(function(r){
var hasPdf=r['Archivo Generado']&&(r['Archivo Generado'].startsWith('http')||r['Archivo Generado'].includes('drive'));
var id='ri_'+String(r['No Serie']||'').replace(/[^A-Z0-9]/gi,'');
var sid=esc(r['No Serie']);var sdesde=esc(r['Fecha Inicio']);var shasta=esc(r['Fecha Fin']);
var slid=esc(r['Filtro Lider']||'');var ssec=esc(r['Filtro Sup Sector']||'');var sarea=esc(r['Filtro Sup Area']||'');
var stitu=esc(r['Titulo de Reporte']);var spdf=hasPdf?esc(r['Archivo Generado']):'';
return '
'+esc(r['Titulo de Reporte'])+' \u00b7 '+fmtD(r['Fecha Inicio'])+' \u2192 '+fmtD(r['Fecha Fin'])+'
'
+'
'
+'
'
+(hasPdf?'':'')
+''
+'
'
+'
';
}).join('');
});
}
function clickRepo(el){
previewRepoItem(el.dataset.serie,el.dataset.desde,el.dataset.hasta,el.dataset.lider,el.dataset.supsec,el.dataset.suparea,el.dataset.titulo,el.dataset.pdf);
}
function clickRepoPrint(el){
var p=el.parentElement.parentElement;
regenImprimir(p.dataset.desde,p.dataset.hasta,p.dataset.lider,p.dataset.titulo);
}
function previewRepoItem(serie,desde,hasta,lider,supSector,supArea,titulo,pdfUrl){
var prev=document.getElementById('genPreview');if(!prev)return;
var acts=document.getElementById('genPrevActions');
// Si ya existe PDF guardado: mostrarlo directamente sin generar nada nuevo
if(pdfUrl&&pdfUrl.length>10){
if(acts)acts.innerHTML=
' Ver PDF en Drive';
var viewUrl=pdfUrl;
if(pdfUrl.includes('drive.google.com/file/d/')){
var fileId=pdfUrl.match(/\/d\/([a-zA-Z0-9_-]+)/);
if(fileId)viewUrl='https://drive.google.com/file/d/'+fileId[1]+'/preview';
} else if(pdfUrl.includes('id=')){
var idMatch=pdfUrl.match(/id=([a-zA-Z0-9_-]+)/);
if(idMatch)viewUrl='https://drive.google.com/file/d/'+idMatch[1]+'/preview';
}
prev.innerHTML='';
toast('PDF de '+serie,'in');
return;
}
// Sin PDF guardado: generar preview HTML (sin crear archivo ni registro)
prev.innerHTML='
';return;}
_lastGenHtml=res.html;_lastGenSerie=serie;
var ifr=document.createElement('iframe');ifr.style='width:100%;height:560px;border:none;border-radius:8px';
prev.innerHTML='';prev.appendChild(ifr);
ifr.contentDocument.open();ifr.contentDocument.write(res.html);ifr.contentDocument.close();
toast('Vista previa de '+serie,'in');
});
}
// ── VISTA EXPRÉS DE CUADRE (Modal con checkboxes multi-select) ───────────
function cargarCuadreExpresDistrital(){
var distritos = [...new Set(APP.hermanos.map(function(h){return h["Distrito"]}).filter(Boolean))].sort();
var zonas = [...new Set(APP.hermanos.map(function(h){return h["Zona"]}).filter(Boolean))].sort();
if(!distritos.length){toast('No hay hermanos cargados','wa');return;}
var hoy = new Date();
var diaSem = hoy.getDay();
var diffLun = hoy.getDate() - diaSem + (diaSem === 0 ? -6 : 1);
var lun = new Date(hoy); lun.setDate(diffLun);
var dom = new Date(lun); dom.setDate(lun.getDate() + 6);
var fDesde = lun.toISOString().split('T')[0];
var fHasta = dom.toISOString().split('T')[0];
var checkDist = distritos.map(function(d){
return '';
}).join('');
var checkZonas = zonas.map(function(z){
return '';
}).join('');
openModal('📊 Reporte Exprés de Cuadre Dominical',
'
'
+'
'
+'
Ciclo Semanal Actual
'
+'
'+fmtD(fDesde)+' → '+fmtD(fHasta)+' (Lun-Dom)
'
+'
⏰ Cierre semanal: Domingo 11:59 PM · Esta vista es informativa para cuadre de ofrendas, no genera PDF.
'
+'
'
+'
'
+'
'+checkDist+'
'
+'
'
+'
'+checkZonas+'
'
+'
'
+'
Selecciona distrito(s) y zona(s), luego presiona Calcular
'
+'
',
''
+''
,'xl');
}
function ejecutarCuadreExpres(){
var distritos = [];
var zonas = [];
document.querySelectorAll('.cdDist:checked').forEach(function(el){distritos.push(el.value);});
document.querySelectorAll('.cdZona:checked').forEach(function(el){zonas.push(el.value);});
if(!distritos.length && !zonas.length){toast('Selecciona al menos un distrito o zona','wa');return;}
var res = document.getElementById('cuadreExpresResult');
if(!res) return;
res.innerHTML = '
Calculando...
';
var hoy = new Date();
var diaSem = hoy.getDay();
var diffLun = hoy.getDate() - diaSem + (diaSem === 0 ? -6 : 1);
var lun = new Date(hoy); lun.setDate(diffLun);
var dom = new Date(lun); dom.setDate(lun.getDate() + 6);
var fDesde = lun.toISOString().split('T')[0];
var fHasta = dom.toISOString().split('T')[0];
var payload = {desde:fDesde, hasta:fHasta};
if(distritos.length === 1) payload.distrito = distritos[0];
else if(distritos.length > 1) payload.distritos = distritos;
if(zonas.length === 1) payload.zona = zonas[0];
else if(zonas.length > 1) payload.zonas = zonas;
call('obtenerVistazoDistritalDominical', payload, function(err, resData){
if(err || !resData || !resData.ok){
res.innerHTML = '
No se pudo procesar. Error: '+(err?.message||resData?.msg||'desconocido')+'
'
+'✔ Cuadrado: Digitales con Ofrenda + Sobres Físicos = Q '+totalGen.toFixed(2)+' · '
+'✘ Pendiente: Q '+tMPend.toFixed(2)+' en ofrendas digitales no recibidas físicamente aún'
+'
';
});
}
function guardarRepoPdf(noSerie,desde,hasta,lider,titulo,btn){
if(btn){btn.disabled=true;btn.innerHTML='';}
loading(true);
call('generarReporte',{desde:desde,hasta:hasta,lider:lider,supSector:supSector||'',supArea:supArea||'',pastorZona:g('gPZ')||'',tipo:titulo},function(_,res){
loading(false);
if(!res||!res.ok){
if(btn){btn.disabled=false;btn.innerHTML='';}
toast('Error generando: '+(res&&res.msg||''),'er');return;
}
if(res.pdfUrl){
toast('PDF guardado: '+noSerie,'ok',5000);loadRepo();
if(btn)btn.outerHTML='';
} else {
call('guardarPdfAction',{html:res.html,noSerie:res.noSerie},function(_,r2){
if(r2&&r2.ok&&r2.pdfUrl){
toast('PDF guardado: '+noSerie,'ok',5000);loadRepo();
if(btn)btn.outerHTML='';
} else {
if(btn){btn.disabled=false;btn.innerHTML='';}
toast('No se pudo guardar PDF','wa',5000);
}
});
}
});
}
function regenImprimir(desde,hasta,lider,titulo){
loading(true);call('generarReporte',{desde,hasta,lider,supSector:g('gSS')||'',supArea:g('gSA')||'',pastorZona:g('gPZ')||'',tipo:titulo,_noGuardar:true},(_,res)=>{loading(false);if(!res?.ok){toast('Error','er');return;}abrirVentanaImpresion(res.html);});
}
let _lastGenHtml='';let _lastGenSerie='';
function genReporte(){
loading(true);
call('generarReporte',{desde:g('gD'),hasta:g('gH'),lider:g('gL'),supSector:g('gSS'),supArea:g('gSA'),pastorZona:g('gPZ')||'',distrito:g('gDist')||'',zona:g('gZona')||'',tipo:g('gTit')},(_,res)=>{
loading(false);
if(!res?.ok){toast('Error: '+(res?.msg||''),'er');return;}
_lastGenHtml=res.html;_lastGenSerie=res.noSerie;
const prev=document.getElementById('genPreview');if(!prev)return;
const ifr=document.createElement('iframe');
ifr.style='width:100%;height:450px;border:none;border-radius:8px';
prev.innerHTML='';prev.appendChild(ifr);
ifr.contentDocument.open();ifr.contentDocument.write(res.html);ifr.contentDocument.close();
// Barra de acciones
const bar=document.createElement('div');
bar.style='display:flex;gap:8px;margin-top:10px;flex-wrap:wrap;align-items:center;padding:10px;background:var(--bg3);border-radius:8px';
if(res.pdfUrl){
bar.innerHTML=' PDF guardado: '+esc(res.noSerie)+'.pdf'
+' Ver en Drive'
+'';
} else {
bar.innerHTML=''
+'';
}
prev.appendChild(bar);
loadRepo();
toast('Reporte generado — '+res.noSerie,'ok');
});
}
function guardarEnDrive(btn){
if(!_lastGenHtml||!_lastGenSerie){toast('Genera un reporte primero','wa');return;}
if(btn){btn.disabled=true;btn.innerHTML=' Guardando...';}
loading(true);
call('guardarPdfAction',{html:_lastGenHtml,noSerie:_lastGenSerie},(_,res)=>{
loading(false);
if(btn)btn.disabled=false;
if(res&&res.ok&&res.pdfUrl){
if(btn)btn.outerHTML=' PDF en Drive';
toast('PDF guardado correctamente','ok',5000);
loadRepo();
} else {
if(btn)btn.innerHTML=' Reintentar';
toast('Error: '+(res&&res.msg?res.msg:'Drive no configurado'),'er',7000);
}
});
}
function dlExcelGen(){loading(true);call('getReportes',{desde:g('gD'),hasta:g('gH'),lider:g('gL'),supSector:g('gSS'),supArea:g('gSA')},(_,d)=>{loading(false);exportXLS(Array.isArray(d)?d:[],'Reportes','reporte_'+today());});}
function imprimirGen(){
if(_lastGenHtml){abrirVentanaImpresion(_lastGenHtml);return;}
loading(true);
call('generarReporte',{desde:g('gD'),hasta:g('gH'),lider:g('gL'),supSector:g('gSS'),supArea:g('gSA'),pastorZona:g('gPZ')||'',tipo:g('gTit'),_noGuardar:true},(_,res)=>{
loading(false);if(!res?.ok){toast('Error','er');return;}
_lastGenHtml=res.html;_lastGenSerie=res.noSerie;
abrirVentanaImpresion(res.html);
});
}
// === HERMANOS LIDERES v6 ===
var _herm=[];
function renderHermanos(el){
el.innerHTML = `
Hermanos Lideres
Codigo: DnZnALSnGn Ej:11A11
Codigo
Nombre
D
Z
A
S
G
Sup Sector
Pastor Zona
`;
loadHerm();
}
var _hermQ='';
function loadHerm(){call('getHermanos',{},function(_,d){_herm=Array.isArray(d)?d:[];APP.hermanos=_herm;var ds=[...new Set(_herm.map(function(h){return h['Distrito'];}).filter(Boolean))].sort();var zs=[...new Set(_herm.map(function(h){return h['Zona'];}).filter(Boolean))].sort();var dsel=document.getElementById('hFD'),zsel=document.getElementById('hFZ');if(dsel)dsel.innerHTML=''+ds.map(function(d){return '';}).join('');if(zsel)zsel.innerHTML=''+zs.map(function(z){return '';}).join('');renderHermTb(_herm);});}
function filterHerm(q){_hermQ=q||'';filterHermTable();}
function filterHermTable(){var q=_hermQ.toLowerCase(),dist=(document.getElementById('hFD')||{}).value||'',zona=(document.getElementById('hFZ')||{}).value||'';renderHermTb(_herm.filter(function(h){if(dist&&h['Distrito']!==dist)return false;if(zona&&h['Zona']!==zona)return false;if(q&&!JSON.stringify(h).toLowerCase().includes(q))return false;return true;}));}
function renderHermTb(d){var tb=document.getElementById('hermTb');if(!tb)return;if(!d.length){tb.innerHTML='
Sin lideres
';return;}tb.innerHTML=d.map(function(h){return '
'+esc(h['CodigoL'])+'
'+esc(h['NombreL'])+'
'+esc(h['Distrito']||'')+'
'+esc(h['Zona']||'')+'
'+esc(h['Area']||'')+'
'+esc(h['Sector']||'')+'
'+esc(h['Grupo']||'')+'
'+esc(h['Sup SectorL']||'-')+'
'+esc(h['Pastor Zona']||'-')+'
';}).join('');}
function hermForm(h){h=h||{};var svs=[];var pas=[];var aas=[];try{svs=JSON.parse(localStorage.getItem('_svs_c')||'[]');}catch(ex){}try{pas=JSON.parse(localStorage.getItem('_pas_c')||'[]');}catch(ex){}try{aas=JSON.parse(localStorage.getItem('_aas_c')||'[]');}catch(ex){}return '
Jerarquia D(n)Z(n)A(L)S(n)G(n) Ej:11A11
Codigo:'+esc(h['CodigoL']||'??')+'
Datos del Lider
Supervision
';}
function actualizarDatalistsForm() {
try {
var svs = JSON.parse(localStorage.getItem('_svs_c') || '[]');
var pas = JSON.parse(localStorage.getItem('_pas_c') || '[]');
var aas = JSON.parse(localStorage.getItem('_aas_c') || '[]');
var ssl = document.getElementById('hf_ssl');
var pzl = document.getElementById('hf_pzl');
var sal = document.getElementById('hf_sal');
if (ssl && svs.length) {
ssl.innerHTML = svs.map(function(s){ return ''; }).join('');
}
if (pzl && pas.length) {
pzl.innerHTML = pas.map(function(p){ return ''; }).join('');
}
if (sal && aas.length) {
sal.innerHTML = aas.map(function(a){ return ''; }).join('');
}
} catch(e) { console.error("Error al actualizar listas", e); }
}
function newHerm(){
openModal('Nuevo Lider', hermForm(), '','xl');
actualizarDatalistsForm();
call('getSupervisores',{},function(_,r){
if(r&&r.ok&&r.data){
try{localStorage.setItem('_svs_c',JSON.stringify(r.data.map(function(s){return{c:s['CodigoSup'],n:s['NombreSup']};})));}catch(ex){}
actualizarDatalistsForm();
}
});
call('getPastores',{},function(_,rp){
if(rp&&rp.ok&&rp.data){
try{localStorage.setItem('_pas_c',JSON.stringify(rp.data.map(function(p){return{c:p['CodigoPastor'],n:p['NombrePastor']};})));}catch(ex){}
actualizarDatalistsForm();
}
});
call('getAreaSupervisores',{},function(_,ra){
if(ra&&ra.ok&&ra.data){
try{localStorage.setItem('_aas_c',JSON.stringify(ra.data));}catch(ex){}
actualizarDatalistsForm();
}
});
}
function editHerm(id){
var h=_herm.find(function(x){return x['ID']===id;});
if(!h)return;
openModal('Editar Lider', hermForm(h), ``,'xl');
actualizarDatalistsForm();
call('getSupervisores',{},function(_,r){
if(r&&r.ok&&r.data) {
try{localStorage.setItem('_svs_c',JSON.stringify(r.data.map(function(s){return{c:s['CodigoSup'],n:s['NombreSup']};})));}catch(ex){}
actualizarDatalistsForm();
}
call('getPastores',{},function(_,rp){
if(rp&&rp.ok&&rp.data) {
try{localStorage.setItem('_pas_c',JSON.stringify(rp.data.map(function(p){return{c:p['CodigoPastor'],n:p['NombrePastor']};})));}catch(ex){}
actualizarDatalistsForm();
}
call('getAreaSupervisores',{},function(_,ra){
if(ra&&ra.ok&&ra.data){
try{localStorage.setItem('_aas_c',JSON.stringify(ra.data));}catch(ex){}
actualizarDatalistsForm();
}
});
});
});
}
function saveHerm(id){var cod=(document.getElementById('hf_codigo')||{}).value||'',nom=(document.getElementById('hf_nombre')||{}).value||'',pastor=(document.getElementById('hf_pastor')||{}).value||'',supSec=(document.getElementById('hf_supSec')||{}).value||'';if(!nom){toast('Nombre requerido','wa');return;}if(!cod||cod==='??'){previewCodigo();cod=(document.getElementById('hf_codigo')||{}).value||'';}if(!cod){toast('Completa 5 campos','wa');return;}if(!pastor){toast('Pastor Zona requerido','wa');return;}var cp='';var cs='';try{var pz=JSON.parse(localStorage.getItem('_pas_c')||'[]');var pz2=pz.find(function(p){return p.n===pastor;});if(pz2)cp=pz2.c;}catch(ex){}try{var ss=JSON.parse(localStorage.getItem('_svs_c')||'[]');var ss2=ss.find(function(s){return s.n===supSec;});if(ss2)cs=ss2.c;}catch(ex){}loading(true);call('saveHermano',{ID:id||'',CodigoL:cod,NombreL:nom,Distrito:(document.getElementById('hf_dist')||{}).value||'',Zona:(document.getElementById('hf_zona')||{}).value||'',Area:((document.getElementById('hf_area')||{}).value||'').toUpperCase(),Sector:(document.getElementById('hf_sector')||{}).value||'',Grupo:(document.getElementById('hf_grupo')||{}).value||'',Anfitrion:(document.getElementById('hf_anf')||{}).value||'',Direccion:(document.getElementById('hf_dir')||{}).value||'','Sup SectorL':supSec,'Sup AreaL':(document.getElementById('hf_supArea')||{}).value||'','Pastor Zona':pastor,CodigoPastor:cp,CodigoSup:cs},function(_,r){loading(false);if(r&&r.ok){toast('Lider: '+cod,'ok',4000);closeModal();loadHerm();}else toast('Error: '+(r&&r.msg||''),'er');});}
function delHerm(id){customConfirm('Eliminar lider?',function(ok){if(!ok)return;loading(true);call('deleteHermano',{id:id},function(_,r){loading(false);if(r&&r.ok){toast('Eliminado','ok');loadHerm();}});},{icon:'fa-trash',danger:true});}
// ══════════════════════════════════════════════════════════
// PRIVILEGIOS — Lista por área + Cronograma
// ══════════════════════════════════════════════════════════
var PRV_TIPOS_DEFAULT=['Predicador','Maestro de Escuela Dominical','Coordinador de Área','Diácono','Ujier','Ministracion','Protocolo','Líder de Adoración','Tesorero de Grupo','Secretario','Coordinador de Jóvenes','Coordinador de Niños','Evangelista','Consejero'];
var PRV_TIPOS=PRV_TIPOS_DEFAULT; // alias
function getPrvTipos(){
try{
var cfg=APP.config&&APP.config.prvTipos;
if(cfg&&cfg.length)return cfg;
}catch(e){}
return PRV_TIPOS_DEFAULT;
}
function getPrvAreas(){
var areas=[];
if(APP.hermanos&&APP.hermanos.length){
var fromH=[...new Set(APP.hermanos.map(function(h){return h['Area']||'';}).filter(Boolean))].sort();
areas=areas.concat(fromH);
}
try{
var cfgA=APP.config&&APP.config.prvAreas;
if(cfgA&&cfgA.length)cfgA.forEach(function(a){if(!areas.includes(a))areas.push(a);});
}catch(e){}
if(!areas.length)areas=['A','B','C','D','E','General'];
return areas;
}
const PRV_AREAS=['Área A','Área B','Área C','Área D','Área E','General','Sin Área'];
let _prv=[];let _prvTab='lista';
function renderPrivilegios(el){
el.innerHTML=`
Privilegios
`;
loadPrv();
}
function switchPrvTab(tab){
_prvTab=tab;
['lista','area','cron'].forEach(t=>{
const b=document.getElementById('ptab_'+t);if(!b)return;
const active=t===tab;
b.style.background=active?'#fff':'var(--bg3)';
b.style.color=active?'var(--pr)':'var(--tx2)';
b.style.borderBottom=active?'2px solid var(--pr)':'2px solid transparent';
});
renderPrvContent();
}
function loadPrv(){call('getPrivilegios',{},(_, d)=>{_prv=Array.isArray(d)?d:[];renderPrvContent();});}
function renderPrvContent(){
const c=document.getElementById('prvContent');if(!c)return;
if(_prvTab==='lista') renderPrvLista(c);
else if(_prvTab==='area') renderPrvPorArea(c);
else renderPrvCronograma(c);
}
function renderPrvLista(c){
c.innerHTML=`
Hermano
Área
Privilegio
Inicio
Fin
Estado
Observaciones
Acciones
`;
renderPrvTb(_prv);
}
function renderPrvTb(data){
const tb=document.getElementById('prvTb');if(!tb)return;
const tipo=g('prvFiltTipo');const est=g('prvFiltEstado');
let d=data.filter(p=>{
if(tipo&&p['Privilegio']!==tipo)return false;
if(est&&p['Activo']!==est)return false;
return true;
});
if(!d.length){tb.innerHTML=`
Sin privilegios
`;return;}
tb.innerHTML=d.map(p=>`
${esc(p['Hermano'])}
${esc(p['Area']||p['CodigoL']||'—')}
${esc(p['Privilegio'])}
${fmtD(p['FechaInicio'])}
${fmtD(p['FechaFin'])||'—'}
${p['Activo']==='SI'?'Activo':'Inactivo'}
${esc(p['Observaciones']||'—')}
`).join('');
}
function filterPrvLista(q){renderPrvTb(_prv.filter(p=>JSON.stringify(p).toLowerCase().includes(q.toLowerCase())));}
function renderPrvPorArea(c){
const activos=_prv.filter(p=>p['Activo']==='SI');
const byArea={};
activos.forEach(p=>{
const a=p['Area']||'General';
if(!byArea[a])byArea[a]=[];
byArea[a].push(p);
});
// Agrupar también por tipo de privilegio para cada área
const areas=Object.keys(byArea).sort();
c.innerHTML=`
`;
}
function autoFillCronArea(nombre){
if(!nombre)return;
// Buscar en privilegios primero
var prv=_prv.find(function(p){return p['Hermano']===nombre&&p['Activo']==='SI';});
if(prv&&prv['Area']){
var aEl=document.getElementById('cr_area');if(aEl)aEl.value=prv['Area'];
return;
}
// Luego en hermanos
var h=APP.hermanos.find(function(x){return x['NombreL']===nombre;});
if(h&&h['Area']){var aEl2=document.getElementById('cr_area');if(aEl2)aEl2.value=h['Area'];}
}
function cronForm(d){
d=d||{};
var svcs=getServiciosDinamicos();
// Fuente de hermanos: primero los que tienen privilegios registrados, luego los de APP.hermanos
var prvNombres=[];
if(_prv && _prv.length){
prvNombres=[...new Set(_prv.filter(function(p){return p['Activo']==='SI';}).map(function(p){return p['Hermano']||'';}).filter(Boolean))].sort();
}
// También incluir líderes de APP.hermanos que no estén en privilegios
var hermNombres=[...new Set((APP.hermanos||[]).map(function(h){return h['NombreL']||'';}).filter(Boolean))].sort();
// Opciones: primero los de privilegios (marcados), luego líderes no repetidos
var allNombres=prvNombres.slice();
hermNombres.forEach(function(n){if(!allNombres.includes(n))allNombres.push(n);});
var hO=allNombres.map(function(nombre){
var enPriv=prvNombres.includes(nombre);
var sel=d['Hermano']===nombre?' selected':'';
var prefix=enPriv?'[★ P] ':'';
return '';
}).join('');
var SERVS=getPrvTipos().concat(['Predicador','Maestro E.D.','Ujier','Líder Adoración','Servidor A/V','Coordinador','Diácono','Caporal','Secretario','Tesorero','Otro'].filter(function(s){return !getPrvTipos().includes(s);}));
var diasHTML=svcs.map(function(x){
return '
';
}).join('');
return '
'
+'
'
+''
+''
+(prvNombres.length?' Los nombres sugeridos pertenecen a la lista de privilegios y hermanos registrados.':'')
+'
'
+'
'
+''
+'
'
+''
+''
+'
Días de Servicio Asignados
'
+'
Marca los servicios en que participará este hermano.
'
+diasHTML
+''
+''
+'
';
}
function newCronItem(){openModal('Asignar Servicio',cronForm(),mFoot('saveCronItem'));}
function editCronItem(id){
const d=_cronData.find(x=>x['ID']===id);
openModal(
'Editar Asignación',
cronForm(d),
``
);
}
function viewCronItem(id){
const d=_cronData.find(x=>x['ID']===id);if(!d)return;
const svcs=getServiciosDinamicos();
const horBadges=svcs.map(s=>{
const v=d[s.key]==='SI';
return ` ${esc(s.label)}`;
}).join('');
openModal('Detalle de Servicio — '+esc(d['Hermano']),`
`,``);
}
function saveCronItem(id){
var svcs=getServiciosDinamicos();
var data={ID:id||'',Hermano:g('cr_h'),Area:g('cr_area'),Servicio:g('cr_serv'),Privilegio:g('cr_serv'),
FechaAsignacion:g('cr_fecha'),Observaciones:g('cr_obs'),Activo:g('cr_act')};
// Marcar cada servicio dinámico
svcs.forEach(function(s){
var el=document.getElementById('cr_'+s.key);
data[s.key]=el&&el.checked?'SI':'NO';
});
// Asegurar compatibilidad legacy con campos fijos
['Lunes','Jueves','Domingo_Mañana','Domingo_Tarde'].forEach(function(k){
if(data[k]===undefined)data[k]='NO';
});
if(!data.Hermano||!data.Area||!data.Servicio){toast('Hermano, área y servicio son requeridos','wa');return;}
loading(true);call('saveCronograma',data,(_,res)=>{loading(false);if(res?.ok){toast('Guardado','ok');closeModal();loadCronograma();}else toast('Error: '+(res?.msg||''),'er');});
}
function delCronItem(id){
customConfirm('¿Eliminar esta asignación?',function(ok){
if(!ok)return;
loading(true);
call('deleteCronograma',{id:id},function(_,res){
loading(false);
if(res&&res.ok){toast('Eliminado','ok');loadCronograma();}
});
},{icon:'fa-trash',danger:true});
}
function abrirAICronograma(){
var cfg=APP.config||{};
var noKey=(!(cfg.gemini_api_key&&cfg.gemini_api_key.length>10))&&(!(cfg.openrouter_api_key&&cfg.openrouter_api_key.length>10))&&(!(cfg.deepseek_api_key&&cfg.deepseek_api_key.length>10));
var hoy=new Date();
var mes=['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'][hoy.getMonth()];
var body='';
if(noKey)body='
Configura tu API Key en Configuracion > Asistente IA.
';
body+=''
+''
+'
Sugerencias:
'
+'
'
+''
+''
+''
+'
'
+'
';
openModal('IA - Cronograma',body,
''
+'','xl');
}
function setCronQ(btn){var el=document.getElementById('aiCronQ');if(el)el.value=btn.textContent||btn.innerText||'';}
function enviarAICronograma(){
var q=(document.getElementById('aiCronQ')||{}).value||'';
if(!q.trim()){toast('Escribe tu consulta','wa');return;}
var hoy=new Date();
var mes=['Enero','Febrero','Marzo','Abril','Mayo','Junio','Julio','Agosto','Septiembre','Octubre','Noviembre','Diciembre'][hoy.getMonth()];
var respEl=document.getElementById('aiCronResp');
if(respEl){respEl.style.display='block';respEl.innerHTML=' Analizando...';}
call('preguntarAICronograma',{pregunta:q,mes:mes,anio:hoy.getFullYear()},function(err,res){
if(!respEl)return;
if(res&&res.ok){
respEl.innerHTML='