// ── Dashboard Page ── registerPage('dashboard', { async init() { // Einstellungen-Link document.getElementById('dashboardSettingsLink').addEventListener('click', (e) => { e.preventDefault(); navigateTo('settings'); }); // Profil-Link document.getElementById('dashboardProfileLink').addEventListener('click', (e) => { e.preventDefault(); navigateTo('profile'); }); // Admin-Links (nur für Admins sichtbar) var adminLink = document.getElementById('dashboardAdminLink'); var serverSettingsLink = document.getElementById('dashboardServerSettingsLink'); if (currentUser && currentUser.isAdmin) { adminLink.hidden = false; adminLink.addEventListener('click', (e) => { e.preventDefault(); navigateTo('admin'); }); serverSettingsLink.hidden = false; serverSettingsLink.addEventListener('click', (e) => { e.preventDefault(); navigateTo('server-settings'); }); } // Globaler Click-Handler zum Schließen aller Card-Menüs document.addEventListener('click', this._closeCardMenus = () => closeAllCardMenus()); // Telegram-Config parallel pro Session laden (sessions werden gleich geladen) const [agents, sessions, cronjobs, shares] = await Promise.all([ apiGetAgents(), apiGetSessions(), apiGetCronjobs(), apiListShares() ]); const agentMap = {}; for (const a of agents) agentMap[a.agentId] = a; // Sessions const grid = document.getElementById('sessionCards'); grid.innerHTML = ''; for (const s of sessions) { const agent = s.agentId ? agentMap[s.agentId] : null; const card = buildSessionCard(s, agent); // Git-Status aus der Session-Response rendern (inline vom Backend) if (s.gitStatus) { const gs = s.gitStatus; const metaEl = card.querySelector('.card-meta'); if (metaEl) { const parts = []; if (gs.add > 0) { const tip = `${gs.add} ${gs.add === 1 ? 'Datei' : 'Dateien'} hinzugefügt`; parts.push(`+${gs.add}`); } if (gs.edit > 0) { const tip = `${gs.edit} ${gs.edit === 1 ? 'Datei' : 'Dateien'} geändert`; parts.push(`~${gs.edit}`); } if (gs.delete > 0) { const tip = `${gs.delete} ${gs.delete === 1 ? 'Datei' : 'Dateien'} gelöscht`; parts.push(`-${gs.delete}`); } if (gs.outgoing > 0) { const tip = `${gs.outgoing} ${gs.outgoing === 1 ? 'Commit muss' : 'Commits müssen'} noch gepusht werden`; parts.push(` ${gs.outgoing}`); } if (parts.length) { const gitEl = document.createElement('span'); gitEl.className = 'card-meta-item card-git-status'; gitEl.innerHTML = parts.join(' '); metaEl.appendChild(gitEl); } } } // Menü zum title-row hinzufügen const titleRow = card.querySelector('.card-title-row'); titleRow.insertAdjacentHTML('beforeend', `
`); // Chat deaktivieren wenn kein Agent gesetzt const chatBtn = card.querySelector('[data-action="chat"]'); if (!agent) { chatBtn.disabled = true; chatBtn.title = 'Kein Agent zugewiesen'; } // Card selbst klickbar wenn Agent zugewiesen if (agent) { card.classList.add('clickable'); card.addEventListener('click', () => navigateTo('chat', { id: s.sessionId })); } const menuTrigger = card.querySelector('.card-menu-trigger'); const menuDropdown = card.querySelector('.card-menu-dropdown'); menuTrigger.addEventListener('click', (e) => { e.stopPropagation(); _openCardMenu(menuTrigger, menuDropdown); }); chatBtn.addEventListener('click', (e) => { e.stopPropagation(); if (chatBtn.disabled) return; menuDropdown.hidden = true; navigateTo('chat', { id: s.sessionId }); }); menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => { e.stopPropagation(); menuDropdown.hidden = true; navigateTo('session', { id: s.sessionId }); }); grid.appendChild(card); } // Neue Session Button document.getElementById('newSessionBtn').addEventListener('click', () => navigateTo('session')); // Agents const agentGrid = document.getElementById('agentCards'); agentGrid.innerHTML = ''; for (const a of agents) { const skills = a.skills || []; const card = buildAgentCard(a, skills); // Menü zum title-row hinzufügen const titleRow = card.querySelector('.card-title-row'); const menuHtml = `
`; titleRow.insertAdjacentHTML('beforeend', menuHtml); const menuTrigger = card.querySelector('.card-menu-trigger'); const menuDropdown = card.querySelector('.card-menu-dropdown'); menuTrigger.addEventListener('click', (e) => { e.stopPropagation(); _openCardMenu(menuTrigger, menuDropdown); }); menuDropdown.querySelector('[data-action="files"]').addEventListener('click', (e) => { e.stopPropagation(); menuDropdown.hidden = true; openFileExplorer({ agentId: a.agentId }); }); menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => { e.stopPropagation(); menuDropdown.hidden = true; navigateTo('agent', { id: a.agentId }); }); agentGrid.appendChild(card); } // Neuer Agent Button document.getElementById('newAgentBtn').addEventListener('click', () => navigateTo('agent')); // Cronjobs const cronjobGrid = document.getElementById('cronjobCards'); cronjobGrid.innerHTML = ''; for (const cj of cronjobs) { const card = document.createElement('div'); card.className = 'card card-compact'; const statusBadge = cj.isEnabled ? ' Aktiv' : ' Pausiert'; const lastRunIcon = cj.lastRunStatus === 'success' ? '✅' : cj.lastRunStatus === 'error' ? '❌' : ''; const cronDesc = describeCron(cj.cronExpression) || cj.cronExpression; card.innerHTML = `
${esc(cj.name)} ${statusBadge}
${esc(cronDesc)} ${cj.sessionName ? ` ${esc(cj.sessionName)}` : ''} ${lastRunIcon ? `${lastRunIcon} ${cj.lastRunAt ? formatDate(cj.lastRunAt) : ''}` : ''}
`; const menuTrigger = card.querySelector('.card-menu-trigger'); const menuDropdown = card.querySelector('.card-menu-dropdown'); menuTrigger.addEventListener('click', (e) => { e.stopPropagation(); _openCardMenu(menuTrigger, menuDropdown); }); menuDropdown.querySelector('[data-action="edit"]').addEventListener('click', (e) => { e.stopPropagation(); menuDropdown.hidden = true; navigateTo('cronjob', { id: cj.cronjobId }); }); menuDropdown.querySelector('[data-action="toggle"]').addEventListener('click', async (e) => { e.stopPropagation(); menuDropdown.hidden = true; await apiToggleCronjob(cj.cronjobId); showToast(cj.isEnabled ? 'Cronjob pausiert' : 'Cronjob aktiviert'); navigateTo('dashboard', {}, { replace: true }); }); menuDropdown.querySelector('[data-action="run"]').addEventListener('click', async (e) => { e.stopPropagation(); menuDropdown.hidden = true; showToast('Cronjob wird ausgeführt...'); const result = await apiRunCronjob(cj.cronjobId); if (result.status === 'success') { showToast('Cronjob erfolgreich ausgeführt'); } else { showToast('Fehler: ' + (result.error || 'Unbekannt'), 'error'); } navigateTo('dashboard', {}, { replace: true }); }); cronjobGrid.appendChild(card); } // Neuer Cronjob Button document.getElementById('newCronjobBtn').addEventListener('click', () => navigateTo('cronjob')); // Shared Links const sharedSection = document.getElementById('sharedLinksSection'); const sharedGrid = document.getElementById('sharedLinksCards'); if (shares.length > 0) { sharedSection.hidden = false; sharedGrid.innerHTML = ''; for (const s of shares) { const card = document.createElement('div'); card.className = 'card card-compact'; const shareUrl = `${location.origin}/#shared/${s.shareToken}`; const modeLabel = s.includeContext ? 'Thread' : 'Einzeln'; const roleIcon = s.role === 'user' ? 'fa-user' : 'fa-cog'; card.innerHTML = `
${esc(s.preview || '(leer)')}
${s.sessionName ? ` ${esc(s.sessionName)}` : ''} ${modeLabel} ${formatDate(s.createdAt)}
`; const menuTrigger = card.querySelector('.card-menu-trigger'); const menuDropdown = card.querySelector('.card-menu-dropdown'); menuTrigger.addEventListener('click', (e) => { e.stopPropagation(); _openCardMenu(menuTrigger, menuDropdown); }); menuDropdown.querySelector('[data-action="copy"]').addEventListener('click', async (e) => { e.stopPropagation(); menuDropdown.hidden = true; await navigator.clipboard.writeText(shareUrl); showToast('Link kopiert!'); }); menuDropdown.querySelector('[data-action="open"]').addEventListener('click', (e) => { e.stopPropagation(); menuDropdown.hidden = true; window.open(shareUrl, '_blank'); }); menuDropdown.querySelector('[data-action="delete"]').addEventListener('click', async (e) => { e.stopPropagation(); menuDropdown.hidden = true; await apiDeleteShare(s.shareToken); card.remove(); if (sharedGrid.children.length === 0) sharedSection.hidden = true; showToast('Link gelöscht'); }); sharedGrid.appendChild(card); } } }, destroy() { if (this._closeCardMenus) { document.removeEventListener('click', this._closeCardMenus); this._closeCardMenus = null; } } }); function closeAllCardMenus() { document.querySelectorAll('.card-menu-dropdown').forEach(d => d.hidden = true); } function _openCardMenu(trigger, dropdown) { closeAllCardMenus(); const rect = trigger.getBoundingClientRect(); dropdown.hidden = false; const dropRect = dropdown.getBoundingClientRect(); dropdown.style.top = `${rect.bottom + 4}px`; dropdown.style.left = `${rect.right - dropRect.width}px`; if (rect.bottom + 4 + dropRect.height > window.innerHeight) { dropdown.style.top = `${rect.top - dropRect.height - 4}px`; } const finalRect = dropdown.getBoundingClientRect(); if (finalRect.left < 0) dropdown.style.left = '8px'; }