diff --git a/wwwroot/css/form.css b/wwwroot/css/form.css index a0ec857..abcb465 100644 --- a/wwwroot/css/form.css +++ b/wwwroot/css/form.css @@ -297,9 +297,10 @@ label.skill-item { margin-left: auto; } -/* ── Agent Select Card (in Session-Formular) ── */ +/* ── Select Cards (Agent / Session in Formularen) ── */ -.agent-select-card { +.agent-select-card, +.session-select-card { display: flex; align-items: center; gap: var(--box-spacing); @@ -311,30 +312,35 @@ label.skill-item { transition: border-color var(--transition-duration); } -.agent-select-card:hover { +.agent-select-card:hover, +.session-select-card:hover { border-color: var(--primary); } -.agent-select-card .card-image { +.agent-select-card .card-image, +.session-select-card .card-image { flex: 0 0 4em; height: 4em; } -.agent-select-empty { +.agent-select-empty, +.session-select-empty { opacity: var(--semi-transparent); display: flex; align-items: center; gap: var(--badge-spacing); } -.agent-select-change { +.agent-select-change, +.session-select-change { margin-left: auto; opacity: var(--semi-transparent); flex-shrink: 0; transition: opacity var(--transition-duration); } -.agent-select-card:hover .agent-select-change { +.agent-select-card:hover .agent-select-change, +.session-select-card:hover .session-select-change { opacity: 1; color: var(--primary); } diff --git a/wwwroot/index.html b/wwwroot/index.html index e2121ec..bcb6310 100644 --- a/wwwroot/index.html +++ b/wwwroot/index.html @@ -246,8 +246,11 @@
- - + +
+
Keine Session ausgewählt
+
+
@@ -259,8 +262,11 @@
- - + +
+
Keine (Antwort bleibt in Arbeits-Session)
+
+ Wenn gesetzt, wird eine Zusammenfassung in diese Session geschrieben (z.B. eine mit Telegram).
diff --git a/wwwroot/js/pages/cronjob-page.js b/wwwroot/js/pages/cronjob-page.js index c3627fd..117c1e1 100644 --- a/wwwroot/js/pages/cronjob-page.js +++ b/wwwroot/js/pages/cronjob-page.js @@ -6,38 +6,58 @@ registerPage('cronjob', { const title = document.getElementById('cronjobPageTitle'); const nameInput = document.getElementById('cronjobName'); const sessionSelect = document.getElementById('cronjobSession'); + const sessionCard = document.getElementById('cronjobSessionCard'); const cronInput = document.getElementById('cronjobCron'); const cronPreview = document.getElementById('cronjobCronPreview'); const promptInput = document.getElementById('cronjobPrompt'); const notifySelect = document.getElementById('cronjobNotifySession'); + const notifyCard = document.getElementById('cronjobNotifySessionCard'); const enabledCheckbox = document.getElementById('cronjobEnabled'); const statusBox = document.getElementById('cronjobStatusBox'); const saveBtn = document.getElementById('cronjobSaveBtn'); const runBtn = document.getElementById('cronjobRunBtn'); const deleteBtn = document.getElementById('cronjobDeleteBtn'); - // Sessions laden für Dropdown + // Sessions + Agents laden const sessions = await apiGetSessions(); - sessionSelect.innerHTML = ''; - notifySelect.innerHTML = ''; - for (const s of sessions) { - const opt = document.createElement('option'); - opt.value = s.sessionId; - opt.textContent = s.name || 'Unbenannt'; - if (!s.agentId) { - opt.textContent += ' (kein Agent)'; - opt.disabled = true; - } - sessionSelect.appendChild(opt); + const agents = await apiGetAgents(); + const agentMap = {}; + for (const a of agents) agentMap[a.agentId] = a; + const sessionMap = {}; + for (const s of sessions) sessionMap[s.sessionId] = s; - // Notify-Dropdown: alle Sessions (auch ohne Agent), Telegram-Sessions markieren - const nOpt = document.createElement('option'); - nOpt.value = s.sessionId; - nOpt.textContent = s.name || 'Unbenannt'; - if (s.telegramChatId) nOpt.textContent += ' 📱'; - notifySelect.appendChild(nOpt); + // Session-Card rendern + function renderSessionSelectCard(cardEl, session, emptyHtml) { + cardEl.innerHTML = ''; + if (!session) { + cardEl.innerHTML = emptyHtml; + return; + } + const agent = session.agentId ? agentMap[session.agentId] : null; + const inner = buildSessionCard(session, agent); + while (inner.firstChild) cardEl.appendChild(inner.firstChild); + cardEl.insertAdjacentHTML('beforeend', ''); } + const sessionEmptyHtml = '
Keine Session ausgewählt
'; + const notifyEmptyHtml = '
Keine (Antwort bleibt in Arbeits-Session)
'; + + // Session-Card click → Picker (nur Sessions mit Agent) + sessionCard.addEventListener('click', () => { + openSessionPickerModal(sessions, agentMap, sessionSelect.value, (selectedId) => { + sessionSelect.value = selectedId || ''; + renderSessionSelectCard(sessionCard, selectedId ? sessionMap[selectedId] : null, sessionEmptyHtml); + }, { filterWithAgent: true, title: 'Session auswählen' }); + }); + + // Notify-Session-Card click → Picker (alle Sessions) + notifyCard.addEventListener('click', () => { + openSessionPickerModal(sessions, agentMap, notifySelect.value, (selectedId) => { + notifySelect.value = selectedId || ''; + renderSessionSelectCard(notifyCard, selectedId ? sessionMap[selectedId] : null, notifyEmptyHtml); + }, { filterWithAgent: false, title: 'Benachrichtigungs-Session auswählen', noneLabel: 'Keine Benachrichtigung', noneIcon: 'fa-bell-slash' }); + }); + // Cron-Expression Vorschau cronInput.addEventListener('input', () => updateCronPreview(cronInput, cronPreview)); @@ -55,9 +75,11 @@ registerPage('cronjob', { nameInput.value = job.name || ''; sessionSelect.value = job.sessionId || ''; + renderSessionSelectCard(sessionCard, job.sessionId ? sessionMap[job.sessionId] : null, sessionEmptyHtml); cronInput.value = job.cronExpression || ''; promptInput.value = job.prompt || ''; notifySelect.value = job.notifySessionId || ''; + renderSessionSelectCard(notifyCard, job.notifySessionId ? sessionMap[job.notifySessionId] : null, notifyEmptyHtml); enabledCheckbox.checked = job.isEnabled; updateCronPreview(cronInput, cronPreview); @@ -131,6 +153,74 @@ registerPage('cronjob', { } }); +function openSessionPickerModal(sessions, agentMap, currentId, onSelect, options) { + options = options || {}; + const filterWithAgent = options.filterWithAgent || false; + const title = options.title || 'Session auswählen'; + const noneLabel = options.noneLabel || 'Keine Session'; + const noneIcon = options.noneIcon || 'fa-ban'; + + // Overlay + const overlay = document.createElement('div'); + overlay.className = 'agent-picker-overlay'; + + const modal = document.createElement('div'); + modal.className = 'agent-picker-modal'; + + // Header + const header = document.createElement('div'); + header.className = 'agent-picker-header'; + header.innerHTML = `${escapeHtml(title)}`; + modal.appendChild(header); + + // Body + const body = document.createElement('div'); + body.className = 'agent-picker-body'; + + // "Keine Session" Option + const noneCard = document.createElement('div'); + noneCard.className = 'card agent-picker-item' + (!currentId ? ' selected' : ''); + noneCard.innerHTML = ` +
+
+
+ ${escapeHtml(noneLabel)} +
+
+ `; + noneCard.addEventListener('click', () => { + onSelect(null); + overlay.remove(); + }); + body.appendChild(noneCard); + + // Session Cards via Helper + for (const s of sessions) { + // Filter: nur Sessions mit Agent, wenn gewünscht + if (filterWithAgent && !s.agentId) continue; + + const agent = s.agentId ? agentMap[s.agentId] : null; + const item = buildSessionCard(s, agent); + item.classList.add('agent-picker-item'); + if (s.sessionId === currentId) item.classList.add('selected'); + item.addEventListener('click', () => { + onSelect(s.sessionId); + overlay.remove(); + }); + body.appendChild(item); + } + + modal.appendChild(body); + overlay.appendChild(modal); + document.body.appendChild(overlay); + + // Schließen + header.querySelector('.agent-picker-close').addEventListener('click', () => overlay.remove()); + overlay.addEventListener('click', (e) => { + if (e.target === overlay) overlay.remove(); + }); +} + function updateCronPreview(input, preview) { const expr = input.value.trim(); if (!expr) { diff --git a/wwwroot/js/pages/dashboard.js b/wwwroot/js/pages/dashboard.js index 9577796..3acee1c 100644 --- a/wwwroot/js/pages/dashboard.js +++ b/wwwroot/js/pages/dashboard.js @@ -40,39 +40,18 @@ registerPage('dashboard', { for (const s of sessions) { const agent = s.agentId ? agentMap[s.agentId] : null; - const card = document.createElement('div'); - card.className = 'card'; - const msgLabel1 = s.messageCount ? `${s.messageCount} Nachrichten` : ''; - const msgLabel2 = s.archiveMessageCount ? `${s.archiveMessageCount} Nachrichten archiviert` : ''; - const workspaceLabel = s.workspace ? s.workspace : ''; + const card = buildSessionCard(s, agent); - const sessionBadges = []; - if (agent) sessionBadges.push(`${esc(agent.name)}`); - if (s.context) sessionBadges.push(``); - if (s.telegramConfigured) sessionBadges.push(``); - - const scaleStyle = s.avatarScale && s.avatarScale !== 1 ? ` style="scale: ${s.avatarScale}"` : ''; - card.innerHTML = ` -
${s.image ? `` : ''}
-
-
- ${esc(s.name || 'Unbenannt')} - ${sessionBadges.join('')} -
+ // Menü zum title-row hinzufügen + const titleRow = card.querySelector('.card-title-row'); + titleRow.insertAdjacentHTML('beforeend', `
-
-
-
- ${msgLabel1 ? ` ${esc(msgLabel1)}` : ''} - ${msgLabel2 ? ` ${esc(msgLabel2)}` : ''} - ${workspaceLabel ? ` ${esc(workspaceLabel)}` : ''} -
-
- `; +
`); + // Chat deaktivieren wenn kein Agent gesetzt const chatBtn = card.querySelector('[data-action="chat"]'); if (!agent) { diff --git a/wwwroot/js/ui.js b/wwwroot/js/ui.js index 203dc43..1301b5c 100644 --- a/wwwroot/js/ui.js +++ b/wwwroot/js/ui.js @@ -1,3 +1,42 @@ +// ── Session Card Helper ── +// Erzeugt ein .card-Element für eine Session (wiederverwendbar für Dashboard + Picker). +// session: Session-Objekt, agent: zugehöriges Agent-Objekt (oder null) +// Gibt ein DOM-Element zurück (.card mit .card-image + .card-body inkl. Badges + Meta). + +function buildSessionCard(session, agent) { + const card = document.createElement('div'); + card.className = 'card'; + + const scaleStyle = session.avatarScale && session.avatarScale !== 1 ? ` style="scale: ${session.avatarScale}"` : ''; + const imageHtml = session.image ? `` : ''; + + // Badges + const badges = []; + if (agent) badges.push(`${esc(agent.name)}`); + if (session.context) badges.push(``); + if (session.telegramConfigured) badges.push(``); + + // Meta + const metaParts = []; + if (session.messageCount) metaParts.push(` ${esc(session.messageCount + ' Nachrichten')}`); + if (session.archiveMessageCount) metaParts.push(` ${esc(session.archiveMessageCount + ' Nachrichten archiviert')}`); + if (session.workspace) metaParts.push(` ${esc(session.workspace)}`); + + card.innerHTML = ` +
${imageHtml}
+
+
+ ${esc(session.name || 'Unbenannt')} + ${badges.join('')} +
+ ${metaParts.length ? `
${metaParts.join('')}
` : ''} +
+ `; + + return card; +} + + // ── Agent Card Helper ── // Erzeugt ein .card-Element für einen Agent (wiederverwendbar für Dashboard + Picker). // agent: Agent-Objekt, skills: Array von Skills (optional)