(function () { let active = false; let initialized = false; const API_URL = (() => { if (window.API_URL) return window.API_URL; try { return `${window.location.origin}/api`; } catch (error) { return 'https://fb.srv.medeba-media.de/api'; } })(); const LOGIN_PAGE = 'login.html'; const state = { traces: [], selectedTraceId: null, loading: false }; const tableBody = document.getElementById('aiDebugTableBody'); const statusEl = document.getElementById('aiDebugStatus'); const detailMeta = document.getElementById('aiDebugDetailMeta'); const detailJson = document.getElementById('aiDebugDetailJson'); const refreshBtn = document.getElementById('aiDebugRefreshBtn'); const statusFilter = document.getElementById('aiDebugStatusFilter'); const limitFilter = document.getElementById('aiDebugLimitFilter'); function handleUnauthorized(response) { if (response && response.status === 401) { if (typeof redirectToLogin === 'function') { redirectToLogin(); } else { window.location.href = LOGIN_PAGE; } return true; } return false; } async function apiFetchJSON(path, options = {}) { const response = await fetch(`${API_URL}${path}`, { credentials: 'include', ...options }); if (handleUnauthorized(response)) { throw new Error('Nicht angemeldet'); } if (!response.ok) { const payload = await response.json().catch(() => ({})); throw new Error(payload.error || 'Unbekannter Fehler'); } return response.json(); } function setStatus(message, isError = false) { if (!statusEl) return; statusEl.textContent = message || ''; statusEl.classList.toggle('ai-debug-status--error', !!isError); } function formatDate(value) { if (!value) return '—'; const date = new Date(value); if (Number.isNaN(date.getTime())) return '—'; return date.toLocaleString('de-DE', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit', second: '2-digit' }); } function formatMs(value) { const numeric = Number(value); if (!Number.isFinite(numeric) || numeric < 0) { return '—'; } return `${Math.round(numeric)} ms`; } function compactId(value) { if (!value || typeof value !== 'string') return '—'; if (value.length <= 16) return value; return `${value.slice(0, 8)}…${value.slice(-6)}`; } function getBackendTotalMs(item) { return item && item.backend_timings && typeof item.backend_timings === 'object' ? item.backend_timings.totalMs : null; } function getAiRequestMs(item) { return item && item.frontend_timings && typeof item.frontend_timings === 'object' ? item.frontend_timings.aiRequestMs : null; } function renderTable() { if (!tableBody) return; tableBody.innerHTML = ''; if (!state.traces.length) { const tr = document.createElement('tr'); tr.innerHTML = 'Keine Debug-Läufe gefunden.'; tableBody.appendChild(tr); return; } state.traces.forEach((item) => { const tr = document.createElement('tr'); tr.dataset.traceId = item.trace_id; if (item.trace_id === state.selectedTraceId) { tr.classList.add('is-selected'); } tr.innerHTML = ` ${formatDate(item.created_at)} ${item.status || '—'} ${formatMs(item.total_duration_ms)} ${formatMs(getBackendTotalMs(item))} ${formatMs(getAiRequestMs(item))} ${compactId(item.flow_id)} / ${compactId(item.trace_id)} `; tableBody.appendChild(tr); }); } function renderDetail(trace) { if (!detailMeta || !detailJson) { return; } if (!trace) { detailMeta.textContent = 'Bitte einen Eintrag auswählen.'; detailJson.textContent = ''; return; } detailMeta.textContent = `${trace.status || '—'} · ${formatDate(trace.created_at)} · Trace ${trace.trace_id || '—'}`; detailJson.textContent = JSON.stringify(trace, null, 2); } async function loadTraceDetail(traceId) { if (!traceId) { renderDetail(null); return; } try { const trace = await apiFetchJSON(`/ai/debug-traces/${encodeURIComponent(traceId)}`); if (!active) return; renderDetail(trace); } catch (error) { if (!active) return; setStatus(`Details konnten nicht geladen werden: ${error.message}`, true); } } async function loadTraces() { if (!active || state.loading) { return; } state.loading = true; setStatus('Lade Debug-Läufe...'); try { const params = new URLSearchParams(); const status = statusFilter ? statusFilter.value.trim() : ''; const limit = limitFilter ? parseInt(limitFilter.value, 10) : 50; if (status) { params.set('status', status); } if (Number.isFinite(limit) && limit > 0) { params.set('limit', String(limit)); } const data = await apiFetchJSON(`/ai/debug-traces?${params.toString()}`); if (!active) return; state.traces = Array.isArray(data.items) ? data.items : []; if (!state.selectedTraceId || !state.traces.some((item) => item.trace_id === state.selectedTraceId)) { state.selectedTraceId = state.traces.length ? state.traces[0].trace_id : null; } renderTable(); await loadTraceDetail(state.selectedTraceId); setStatus(`${state.traces.length} Lauf/Läufe geladen.`); } catch (error) { if (!active) return; state.traces = []; renderTable(); renderDetail(null); setStatus(`Debug-Läufe konnten nicht geladen werden: ${error.message}`, true); } finally { state.loading = false; } } function setupEvents() { if (refreshBtn) { refreshBtn.addEventListener('click', () => loadTraces()); } if (statusFilter) { statusFilter.addEventListener('change', () => loadTraces()); } if (limitFilter) { limitFilter.addEventListener('change', () => loadTraces()); } if (tableBody) { tableBody.addEventListener('click', (event) => { const row = event.target.closest('tr[data-trace-id]'); if (!row) return; const traceId = row.dataset.traceId; if (!traceId) return; state.selectedTraceId = traceId; renderTable(); loadTraceDetail(traceId); }); } } function init() { if (initialized) return; setupEvents(); initialized = true; } function activate() { init(); active = true; loadTraces(); } function deactivate() { active = false; } window.AIDebugPage = { activate, deactivate }; const section = document.querySelector('[data-view="ai-debug"]'); if (section && section.classList.contains('app-view--active')) { activate(); } })();