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 @@
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('')}
-
-
- ${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)