Files
gott/wwwroot/js/pages/cronjob-page.js
Meleki ce50633e33 refactor: Inline-Styles durch CSS-Klassen ersetzt
- 40+ Utility-Klassen in form.css (clickable, text-danger, flex-row, loading-state, etc.)
- share-menu position/z-index in chat.css statt inline
- index.html: 0 Inline-Styles (hidden-Attribut, CSS-Klassen)
- 12 JS-Dateien bereinigt: style.cssText/style.* durch classList ersetzt
- Bugfix: var(--danger) → var(--dangerous) (nicht existierende Variable)
- display:none Toggles → hidden-Attribut wo passend
- Hardcoded font-sizes in chat.css → var(--font-size)

15 Dateien, nur Frontend (CSS/JS/HTML), kein Backend
2026-03-14 17:14:30 +01:00

264 lines
9.6 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// ── Cronjob Page ──
registerPage('cronjob', {
async init(params) {
const isEdit = params && params.id;
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 + Agents laden
const sessions = await apiGetSessions();
const agents = await apiGetAvailableAgents();
const agentMap = {};
for (const a of agents) agentMap[a.agentId] = a;
const sessionMap = {};
for (const s of sessions) sessionMap[s.sessionId] = s;
// Session-Card rendern
function renderSessionSelectCard(cardEl, session, emptyIcon, emptyText) {
const inner = session ? buildSessionCard(session, session.agentId ? agentMap[session.agentId] : null) : null;
_renderCardPreview(cardEl, inner, emptyIcon, emptyText, 'session-select-change');
}
// 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, 'fa-comment-o', 'Keine Session ausgewählt');
}, { 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, 'fa-bell-o', 'Keine (Antwort bleibt in Arbeits-Session)');
}, { filterWithAgent: false, title: 'Benachrichtigungs-Session auswählen', noneLabel: 'Keine Benachrichtigung', noneIcon: 'fa-bell-slash' });
});
// Cron-Expression Vorschau
cronInput.addEventListener('input', () => updateCronPreview(cronInput, cronPreview));
if (isEdit) {
title.textContent = 'Cronjob bearbeiten';
runBtn.hidden = false;
deleteBtn.hidden = false;
const job = await apiGetCronjob(params.id);
if (!job) {
showToast('Cronjob nicht gefunden', 'error');
navigateTo('dashboard');
return;
}
nameInput.value = job.name || '';
sessionSelect.value = job.sessionId || '';
renderSessionSelectCard(sessionCard, job.sessionId ? sessionMap[job.sessionId] : null, 'fa-comment-o', 'Keine Session ausgewählt');
cronInput.value = job.cronExpression || '';
promptInput.value = job.prompt || '';
notifySelect.value = job.notifySessionId || '';
renderSessionSelectCard(notifyCard, job.notifySessionId ? sessionMap[job.notifySessionId] : null, 'fa-bell-o', 'Keine (Antwort bleibt in Arbeits-Session)');
enabledCheckbox.checked = job.isEnabled;
updateCronPreview(cronInput, cronPreview);
showCronjobStatus(statusBox, job);
// Löschen
deleteBtn.addEventListener('click', async () => {
if (!confirm('Cronjob wirklich löschen?')) return;
await apiDeleteCronjob(params.id);
showToast('Cronjob gelöscht');
navigateTo('dashboard');
});
// Jetzt ausführen
runBtn.addEventListener('click', () => withButtonLoading(runBtn, 'Läuft...', async () => {
try {
const result = await apiRunCronjob(params.id);
if (result.status === 'success') {
showToast('Cronjob erfolgreich ausgeführt');
} else {
showToast('Fehler: ' + (result.error || 'Unbekannt'), 'error');
}
const updated = await apiGetCronjob(params.id);
if (updated) showCronjobStatus(statusBox, updated);
} catch (e) {
showToast('Fehler: ' + e.message, 'error');
}
}));
}
// Speichern
saveBtn.addEventListener('click', async () => {
const data = {
name: nameInput.value.trim(),
sessionId: sessionSelect.value,
cronExpression: cronInput.value.trim(),
prompt: promptInput.value.trim(),
notifySessionId: notifySelect.value,
isEnabled: enabledCheckbox.checked
};
if (!data.name || !data.sessionId || !data.cronExpression || !data.prompt) {
showToast('Bitte alle Felder ausfüllen', 'error');
return;
}
await withButtonLoading(saveBtn, 'Speichern...', async () => {
try {
if (isEdit) {
await apiUpdateCronjob(params.id, data);
showToast('Cronjob gespeichert');
const updated = await apiGetCronjob(params.id);
if (updated) showCronjobStatus(statusBox, updated);
} else {
const created = await apiCreateCronjob(data);
showToast('Cronjob erstellt');
navigateTo('cronjob', { id: created.cronjobId });
}
} catch (e) {
showToast('Fehler: ' + e.message, 'error');
}
});
});
}
});
function openSessionPickerModal(sessions, agentMap, currentId, onSelect, options) {
options = options || {};
const filterWithAgent = options.filterWithAgent || false;
const filtered = filterWithAgent ? sessions.filter(s => s.agentId) : sessions;
const items = filtered.map(s => {
s._pickerId = s.sessionId;
return s;
});
_openPickerModal(options.title || 'Session auswählen', items, currentId, onSelect, {
noneLabel: options.noneLabel || 'Keine Session',
noneIcon: options.noneIcon || 'fa-ban',
buildCard: (s) => buildSessionCard(s, s.agentId ? agentMap[s.agentId] : null)
});
}
function updateCronPreview(input, preview) {
const expr = input.value.trim();
if (!expr) {
preview.textContent = '';
return;
}
const desc = describeCron(expr);
if (desc) {
preview.textContent = desc;
preview.classList.remove('text-error');
} else {
preview.textContent = 'Ungültige Cron-Expression';
preview.classList.add('text-error');
}
}
function describeCron(expr) {
try {
const parts = expr.split(/\s+/);
if (parts.length < 5 || parts.length > 5) return null;
const [min, hour, dom, month, dow] = parts;
// Einfache Fälle beschreiben
const dowNames = { '0': 'So', '1': 'Mo', '2': 'Di', '3': 'Mi', '4': 'Do', '5': 'Fr', '6': 'Sa', '7': 'So' };
let desc = '';
const tz = 'Europe/Berlin';
if (dom === '*' && month === '*' && dow === '*') {
if (min !== '*' && hour !== '*') {
desc = `Täglich um ${hour.padStart(2, '0')}:${min.padStart(2, '0')} Uhr`;
} else if (hour === '*' && min === '0') {
desc = 'Jede Stunde';
} else if (hour === '*' && min === '*') {
desc = 'Jede Minute';
} else if (hour !== '*') {
desc = `Täglich, stündlich um ${hour} Uhr`;
}
} else if (dom === '*' && month === '*' && dow !== '*') {
// Ranges wie 1-5 auflösen
let dayParts = [];
for (const part of dow.split(',')) {
if (part.includes('-')) {
const [from, to] = part.split('-');
dayParts.push(`${dowNames[from] || from}–${dowNames[to] || to}`);
} else {
dayParts.push(dowNames[part] || part);
}
}
const days = dayParts.join(', ');
if (min !== '*' && hour !== '*') {
desc = `${days} um ${hour.padStart(2, '0')}:${min.padStart(2, '0')} Uhr`;
} else {
desc = `An: ${days}`;
}
} else if (dow === '*' && month === '*' && dom !== '*') {
desc = `Am ${dom}. jedes Monats`;
if (min !== '*' && hour !== '*') {
desc += ` um ${hour.padStart(2, '0')}:${min.padStart(2, '0')} Uhr`;
}
}
if (!desc) {
desc = `${expr} (5-Feld Cron)`;
}
return desc;
} catch {
return null;
}
}
function showCronjobStatus(box, job) {
box.hidden = false;
const parts = [];
if (job.isEnabled) {
parts.push('<span class="status-active"><i class="fa fa-check-circle"></i> Aktiv</span>');
} else {
parts.push('<span class="status-inactive"><i class="fa fa-pause-circle"></i> Pausiert</span>');
}
if (job.nextRunAt) {
const next = new Date(job.nextRunAt);
parts.push(`<span><i class="fa fa-clock-o"></i> Nächste Ausführung: ${next.toLocaleString('de-AT')}</span>`);
}
if (job.lastRunAt) {
const last = new Date(job.lastRunAt);
const statusIcon = job.lastRunStatus === 'success' ? '✅' : '❌';
parts.push(`<span>${statusIcon} Letzte Ausführung: ${last.toLocaleString('de-AT')}</span>`);
if (job.lastRunStatus === 'error' && job.lastRunError) {
parts.push(`<span class="status-error"><i class="fa fa-exclamation-triangle"></i> ${esc(job.lastRunError)}</span>`);
}
}
if (job.sessionName) {
parts.push(`<span><i class="fa fa-comment"></i> Session: ${esc(job.sessionName)}</span>`);
}
if (job.notifySessionName) {
parts.push(`<span><i class="fa fa-bell"></i> Benachrichtigung → ${esc(job.notifySessionName)}</span>`);
}
box.innerHTML = parts.join('');
}