import { apiGet, isAuthRedirectError } from '../api.js'; import { guard } from '../core/auth-guard.js'; import { ticketStatusLabel, ticketPriorityLabel, eventTypeLabel, eventTypeBadgeClass, statusBadgeClass, priorityBadgeClass, } from '../core/constants.js'; import { esc, formatDateTime, extrasName } from '../core/utils.js'; import { bindAttachmentPreview } from '../core/attachment-preview.js'; import { eventInhaltHtml, sortEventsChronologicalWithAttachmentsLast, } from '../core/ticket-events.js'; const loadingEl = document.getElementById('page-loading'); const mainEl = document.getElementById('page-main'); const errEl = document.getElementById('page-error'); const listEl = document.getElementById('home-ticket-list'); const listMineEl = document.getElementById('home-ticket-list-mine'); const emptyEl = document.getElementById('home-empty'); const tpl = document.getElementById('tpl-home-ticket'); function formatAssigneeLabel(ticket) { const u = ticket.assignedTo; if (!u) return '—'; const name = [u.firstName, u.lastName].filter(Boolean).join(' ').trim(); return name || u.username || u.id; } function showError(msg) { loadingEl.hidden = true; mainEl.hidden = true; errEl.hidden = false; errEl.textContent = msg; } function renderEventBoxes(events) { const evChrono = sortEventsChronologicalWithAttachmentsLast(events); if (evChrono.length === 0) { return '

Noch keine Ereignisse in der Historie.

'; } return evChrono .map( (ev) => `
${esc(eventTypeLabel[ev.type] || ev.type)}
${eventInhaltHtml(ev)}
`, ) .join(''); } /** @param {'open' | 'mine'} listKind */ function fillTicketCard(node, t, events, listKind) { const id = t.id; const detailId = `home-ticket-detail-${id}`; node.dataset.ticketId = id; const btn = node.querySelector('.home-ticket-collapse-btn'); const panel = node.querySelector('.home-ticket-collapsible'); panel.id = detailId; btn.setAttribute('aria-controls', detailId); const mn = t.machine ? extrasName(t.machine) : ''; const machineLabel = t.machine ? `${esc(t.machine.seriennummer)}${mn ? ` · ${esc(mn)}` : ''}` : ''; const standort = t.machine ? esc(t.machine.standort) : '—'; const headEl = node.querySelector('.home-ticket-head'); headEl.classList.toggle('home-ticket-head-overdue', Boolean(t.isOverdue)); const titleA = node.querySelector('.js-ticket-link'); titleA.href = `/ticket.html?id=${encodeURIComponent(id)}`; titleA.textContent = t.title; const st = node.querySelector('.js-status'); st.textContent = ticketStatusLabel[t.status]; st.className = `badge js-status ${statusBadgeClass[t.status] || ''}`; const pr = node.querySelector('.js-priority'); pr.textContent = ticketPriorityLabel[t.priority]; pr.className = `badge js-priority ${priorityBadgeClass[t.priority] || ''}`; const assignTag = node.querySelector('.js-assignee-tag'); if (assignTag) { const name = formatAssigneeLabel(t); if (listKind === 'mine') { assignTag.textContent = name; assignTag.hidden = false; assignTag.className = 'badge js-assignee-tag badge-assignee'; assignTag.title = `Zugewiesen: ${name}`; } else { const hasOther = Boolean(t.assignedTo); assignTag.textContent = hasOther ? name : 'Nicht zugewiesen'; assignTag.hidden = false; assignTag.className = hasOther ? 'badge js-assignee-tag badge-assignee' : 'badge js-assignee-tag badge-assignee badge-assignee--none'; assignTag.title = hasOther ? `Zugewiesen: ${name}` : 'Noch niemandem zugewiesen'; } } const metaM = node.querySelector('.js-meta-machine'); metaM.innerHTML = t.machine ? `Maschine: ${machineLabel}` : 'Keine Maschine'; node.querySelector('.js-meta-standort').textContent = standort; node.querySelector('.js-meta-created').textContent = formatDateTime(t.createdAt); node.querySelector('.js-meta-updated').textContent = formatDateTime(t.updatedAt); const assigneeEl = node.querySelector('.js-meta-assignee'); if (assigneeEl) assigneeEl.textContent = formatAssigneeLabel(t); const openA = node.querySelector('.js-ticket-open'); openA.href = `/ticket.html?id=${encodeURIComponent(id)}`; node.querySelector('.js-desc').textContent = t.description; const mBlock = node.querySelector('.js-machine-block'); if (t.machine) { const m = t.machine; mBlock.innerHTML = `

Maschine: ${esc(m.seriennummer)}${mn ? ` · ${esc(mn)}` : ''}  ·  Typ: ${esc(m.typ)}

`; } else { mBlock.innerHTML = ''; } node.querySelector('.js-events').innerHTML = renderEventBoxes(events); const chev = btn.querySelector('.home-ticket-chevron'); btn.onclick = () => { const willShow = panel.hidden; panel.hidden = !willShow; btn.setAttribute('aria-expanded', String(willShow)); chev.textContent = willShow ? '▲' : '▼'; }; } function renderTicketListInto(container, tickets, eventsLists, listKind) { container.innerHTML = ''; tickets.forEach((t, i) => { const frag = tpl.content.cloneNode(true); const article = frag.querySelector('.home-ticket-card'); fillTicketCard(article, t, eventsLists[i] || [], listKind); container.appendChild(article); }); } async function run() { const [ticketsAll, ticketsMine] = await Promise.all([ apiGet('/tickets?open=1&assignedTo=not_me'), apiGet('/tickets?open=1&assignedTo=me'), ]); const eventsAll = ticketsAll.length === 0 ? [] : await Promise.all(ticketsAll.map((t) => apiGet(`/tickets/${t.id}/events`))); const eventsMine = ticketsMine.length === 0 ? [] : await Promise.all(ticketsMine.map((t) => apiGet(`/tickets/${t.id}/events`))); const openCount = ticketsAll.filter((t) => t.status === 'OPEN').length; const waitingCount = ticketsAll.filter((t) => t.status === 'WAITING').length; document.getElementById('kpi-open').textContent = `${openCount} Offen`; document.getElementById('kpi-waiting').textContent = `${waitingCount} Wartend`; document.getElementById('kpi-total').textContent = `gesamt: ${ticketsAll.length}`; const mineOpen = ticketsMine.filter((t) => t.status === 'OPEN').length; const mineWaiting = ticketsMine.filter((t) => t.status === 'WAITING').length; document.getElementById('kpi-mine-open').textContent = `${mineOpen} Offen`; document.getElementById('kpi-mine-waiting').textContent = `${mineWaiting} Wartend`; document.getElementById('kpi-mine-total').textContent = `gesamt: ${ticketsMine.length}`; if (ticketsAll.length === 0) { emptyEl.hidden = false; emptyEl.textContent = 'Keine offenen Tickets.'; listEl.innerHTML = ''; } else { emptyEl.hidden = true; renderTicketListInto(listEl, ticketsAll, eventsAll, 'open'); } renderTicketListInto(listMineEl, ticketsMine, eventsMine, 'mine'); } async function init() { const st = await guard({ activeNav: 'start' }); if (!st) return; loadingEl.hidden = true; mainEl.hidden = false; bindAttachmentPreview(document.body); try { await run(); } catch (e) { if (isAuthRedirectError(e)) return; showError(e.message || 'Fehler'); } } init();