/* Interfaz: router por hash + vistas. Solo habla con Api (js/api.js). */ const $=s=>document.querySelector(s); const esc=s=>String(s??'').replace(/[&<>"]/g,c=>({'&':'&','<':'<','>':'>','"':'"'}[c])); const fmt=d=>new Date(d).toLocaleString('es-MX',{day:'2-digit',month:'2-digit',year:'numeric',hour:'2-digit',minute:'2-digit',hour12:false}).replace(',',''); const slug=s=>s.toLowerCase().replace(/ /g,'-'),badge=e=>`${e}`,prio=p=>`${p}`; const title=t=>{$('#title').textContent=t;document.title=t+' · Mesa de ayuda'}; const ROLES={ADMINISTRADOR:'Administrador',TECNICO:'Técnico',SOLICITANTE:'Solicitante'},ALL=Object.keys(ROLES); const NAV=[['dashboard','Dashboard',ALL],['mis','Mis tickets',ALL],['todos','Todos los tickets',['ADMINISTRADOR','TECNICO']],['nuevo','Nuevo ticket',ALL]]; const ABIERTOS=['NUEVO','ASIGNADO','EN PROCESO','ESPERANDO USUARIO']; let me,cat,users; async function init(){ [cat,users]=await Promise.all([Api.obtenerCatalogos(),Api.obtenerUsuarios()]);me=users[0]; $('#who').innerHTML=users.map(u=>``).join(''); $('#who').onchange=e=>{me=users.find(u=>u.id==e.target.value);route()}; $('#burger').onclick=()=>document.body.classList.toggle('menu');$('#scrim').onclick=()=>document.body.classList.remove('menu'); addEventListener('hashchange',route);route(); } async function route(){ document.body.classList.remove('menu'); const [p,id]=(location.hash.slice(2)||'dashboard').split('/'); const views={dashboard,mis:()=>lista('mis'),todos:()=>lista('todos'),nuevo,ticket:()=>detalle(+id)}; const n=NAV.find(x=>x[0]==p); if(!views[p]||(p!='ticket'&&!n[2].includes(me.rol))){location.hash='#/dashboard';return} $('#nav').innerHTML=NAV.filter(x=>x[2].includes(me.rol)).map(x=>`${x[1]}`).join(''); await views[p](); } const tabla=(ts,full)=>!ts.length?'

No hay tickets con estos criterios. Cambia los filtros o crea uno nuevo.

': `
${ts.map(t=>`
FolioAsunto${full?'UsuarioÁreaCategoría':''}Prioridad${full?'Responsable':''}EstadoCreado${full?'Actualizado':''}
${t.folio}${esc(t.asunto)}${full?`${esc(t.solicitante)}${esc(t.area)}${esc(t.categoria)}`:''}${prio(t.prioridad)}${full?`${esc(t.responsable||'Sin asignar')}`:''}${badge(t.estado)}${fmt(t.creado)}${full?`${fmt(t.actualizado)}`:''}`).join('')}
`; async function dashboard(){ title('Dashboard');const t=await Api.obtenerTickets({alcance:'todos',usuario:me}); const n=e=>t.filter(x=>x.estado==e).length; const k=[['Nuevos',n('NUEVO')],['Asignados',n('ASIGNADO')],['En proceso',n('EN PROCESO')],['Esperando usuario',n('ESPERANDO USUARIO')],['Resueltos',n('RESUELTO')],['Cerrados',n('CERRADO')],['Abiertos',t.filter(x=>ABIERTOS.includes(x.estado)).length]]; $('#view').innerHTML=`
${k.map(([l,v],i)=>`${v}${l}`).join('')}

Tickets recientes

${tabla(t.slice(0,6),me.rol!='SOLICITANTE')}
`; } async function lista(al){ title(al=='mis'?'Mis tickets':'Todos los tickets');const all=await Api.obtenerTickets({alcance:al,usuario:me}); const opt=(a,k,l)=>``; $('#view').innerHTML=`
${opt(cat.estados,'estado','Todos los estados')}${opt(cat.prioridades,'prioridad','Todas las prioridades')}${opt(cat.categorias,'categoria','Todas las categorías')}
`; const draw=()=>{const q=$('#q').value.toLowerCase(),f={};document.querySelectorAll('[data-f]').forEach(s=>f[s.dataset.f]=s.value); $('#tb').innerHTML=tabla(all.filter(t=>(!q||(t.folio+t.asunto+t.solicitante).toLowerCase().includes(q))&&Object.keys(f).every(k=>!f[k]||t[k]==f[k])),al=='todos')}; document.querySelector('.filters').oninput=draw;draw(); } async function nuevo(){ title('Nuevo ticket');const sel=(id,a)=>``; $('#view').innerHTML=`
Cancelar
`; $('#pri').value='Media';$('#are').value=me.area; $('#f').onsubmit=async e=>{e.preventDefault(); const t=await Api.crearTicket({asunto:$('#asu').value,descripcion:$('#des').value,categoria:$('#cat').value,prioridad:$('#pri').value,area:$('#are').value,solicitanteId:+$('#sol').value}); for(const f of $('#fil').files)await Api.agregarArchivo(t.id,{nombre:f.name,tamano:f.size},me); location.hash='#/ticket/'+t.id}; } async function detalle(id){ const [t,com,his,arc]=await Promise.all([Api.obtenerTicket(id),Api.obtenerComentarios(id).catch(()=>[]),Api.obtenerHistorial(id).catch(()=>[]),Api.obtenerArchivos(id).catch(()=>[])]); title('Ticket '+(t?t.folio:'')); if(!t||!(me.rol=='ADMINISTRADOR'||t.solicitanteId==me.id||t.responsableId==me.id)){$('#view').innerHTML='

No tienes acceso a este ticket o no existe.

';return} const adm=me.rol=='ADMINISTRADOR',cambia=adm||(me.rol=='TECNICO'&&t.responsableId==me.id),dueno=t.solicitanteId==me.id&&t.estado=='RESUELTO'; const row=(l,v)=>`
${l}
${v}`; $('#view').innerHTML=`

${esc(t.asunto)}

${esc(t.descripcion)}

Conversación

    ${com.map(c=>`
  • ${esc(c.usuario[0])}
    ${esc(c.usuario)}

    ${esc(c.texto)}

    `).join('')||'
  • Aún no hay comentarios.'}

Archivos adjuntos

    ${arc.map(a=>`
  • ${esc(a.nombre)} `).join('')||'
  • Sin archivos. Los archivos se simulan: solo se registra el nombre.'}

Historial

    ${his.map(h=>`
  1. — ${esc(h.usuario)} ${esc(h.accion)}`).join('')}
`; const act=fn=>async()=>{await fn();detalle(id)},on=(i,fn)=>$(i)&&($(i).onclick=act(fn)); on('#bCom',async()=>{const v=$('#ctx').value.trim();if(v)await Api.agregarComentario(id,v,me)}); on('#bAsig',()=>Api.asignarTicket(id,+$('#sAsig').value,me));on('#bEst',()=>Api.actualizarTicket(id,{estado:$('#sEst').value},me)); on('#bCierre',()=>Api.actualizarTicket(id,{estado:'CERRADO'},me));on('#bReab',()=>Api.actualizarTicket(id,{estado:'EN PROCESO'},me)); $('#fil').onchange=act(async()=>{for(const f of $('#fil').files)await Api.agregarArchivo(id,{nombre:f.name,tamano:f.size},me)}); } init();