""" Alerts Module - JavaScript. Enthält die JavaScript-Logik für das Alerts Agent Modul: - Inbox management - Topic management - Rule builder - Alert actions """ def get_alerts_js() -> str: """JavaScript fuer das Alerts-Modul.""" return """ /* ========================================== ALERTS MODULE - JavaScript ========================================== */ // API Base URL const ALERTS_API_BASE = '/api/alerts'; // State let alertsData = { items: [], topics: [], rules: [], profile: null, currentFilter: 'all', searchQuery: '' }; /* ========================================== INITIALIZATION ========================================== */ function loadAlertsModule() { console.log('Alerts Module loaded'); loadAlertsData(); } async function loadAlertsData() { await Promise.all([ loadAlerts(), loadTopics(), loadRules(), loadProfile() ]); updateAlertsStats(); } /* ========================================== TAB NAVIGATION ========================================== */ function showAlertsTab(tabId) { // Update tab buttons document.querySelectorAll('.alerts-tab').forEach(tab => { tab.classList.remove('active'); }); event.target.classList.add('active'); // Update tab panels document.querySelectorAll('.alerts-tab-panel').forEach(panel => { panel.classList.remove('active'); }); document.getElementById('alerts-panel-' + tabId).classList.add('active'); } /* ========================================== ALERTS INBOX ========================================== */ async function loadAlerts() { try { const response = await fetch(`${ALERTS_API_BASE}/inbox?limit=100&status=${alertsData.currentFilter === 'all' ? '' : alertsData.currentFilter}`); if (!response.ok) throw new Error('API not available'); const data = await response.json(); alertsData.items = Array.isArray(data.items) ? data.items : (Array.isArray(data) ? data : []); renderAlerts(); } catch (error) { console.log('Alerts API not available, using demo data'); // Demo data alertsData.items = [ { id: 'alert_1', title: 'Neue Studie zur digitalen Bildung an Schulen', url: 'https://example.com/artikel1', snippet: 'Eine aktuelle Studie zeigt, dass digitale Lernmittel den Lernerfolg steigern koennen...', topic_name: 'Digitale Bildung', relevance_score: 0.85, relevance_decision: 'KEEP', status: 'new', fetched_at: new Date().toISOString() }, { id: 'alert_2', title: 'Inklusion: Fortbildungen fuer Lehrkraefte', url: 'https://example.com/artikel2', snippet: 'Das Kultusministerium bietet neue Fortbildungsangebote zum Thema Inklusion an...', topic_name: 'Inklusion', relevance_score: 0.72, relevance_decision: 'KEEP', status: 'new', fetched_at: new Date(Date.now() - 3600000).toISOString() }, { id: 'alert_3', title: 'Stellenangebot: Schulleitung gesucht', url: 'https://example.com/job', snippet: 'Wir suchen eine engagierte Schulleitung fuer unsere Grundschule...', topic_name: 'Schule allgemein', relevance_score: 0.25, relevance_decision: 'DROP', status: 'archived', fetched_at: new Date(Date.now() - 7200000).toISOString() } ]; renderAlerts(); } } function renderAlerts() { const list = document.getElementById('alerts-list'); const emptyState = document.getElementById('alerts-empty-state'); // Filter alerts let filtered = alertsData.items; if (alertsData.currentFilter !== 'all') { if (alertsData.currentFilter === 'new') { filtered = filtered.filter(a => a.status === 'new'); } else if (alertsData.currentFilter === 'keep') { filtered = filtered.filter(a => a.relevance_decision === 'KEEP'); } else if (alertsData.currentFilter === 'review') { filtered = filtered.filter(a => a.relevance_decision === 'REVIEW'); } } // Search filter if (alertsData.searchQuery) { const q = alertsData.searchQuery.toLowerCase(); filtered = filtered.filter(a => a.title.toLowerCase().includes(q) || a.snippet.toLowerCase().includes(q) ); } if (filtered.length === 0) { list.style.display = 'none'; emptyState.style.display = 'flex'; return; } list.style.display = 'flex'; emptyState.style.display = 'none'; list.innerHTML = filtered.map(alert => { const score = alert.relevance_score || 0; const scoreClass = score >= 0.7 ? 'high' : score >= 0.4 ? 'medium' : 'low'; const decisionClass = (alert.relevance_decision || '').toLowerCase(); const timeAgo = formatTimeAgo(alert.fetched_at); return `
${alert.topic_name || 'Unbekannt'}
${escapeHtml(alert.title)}
${escapeHtml(alert.snippet || '')}
${Math.round(score * 100)}% Relevanz ${alert.relevance_decision || '-'}
${timeAgo}
`; }).join(''); } function filterAlerts(filter) { alertsData.currentFilter = filter; // Update filter buttons document.querySelectorAll('.alerts-filter-btn').forEach(btn => { btn.classList.remove('active'); }); event.target.classList.add('active'); renderAlerts(); } function searchAlerts(query) { alertsData.searchQuery = query; renderAlerts(); } function openAlert(alertId) { const alert = alertsData.items.find(a => a.id === alertId); if (alert && alert.url) { window.open(alert.url, '_blank'); // Mark as read markAlertRead(alertId); } } async function markAlertKeep(alertId) { try { await fetch(`${ALERTS_API_BASE}/inbox/${alertId}/action`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'keep' }) }); } catch (error) { console.log('Demo mode: marking as keep'); } const alert = alertsData.items.find(a => a.id === alertId); if (alert) { alert.relevance_decision = 'KEEP'; alert.status = 'kept'; } renderAlerts(); updateAlertsStats(); } async function markAlertDrop(alertId) { try { await fetch(`${ALERTS_API_BASE}/inbox/${alertId}/action`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'archive' }) }); } catch (error) { console.log('Demo mode: marking as drop'); } const alert = alertsData.items.find(a => a.id === alertId); if (alert) { alert.relevance_decision = 'DROP'; alert.status = 'archived'; } renderAlerts(); updateAlertsStats(); } async function markAlertRead(alertId) { const alert = alertsData.items.find(a => a.id === alertId); if (alert) { alert.status = 'read'; } renderAlerts(); updateAlertsStats(); } /* ========================================== TOPICS ========================================== */ async function loadTopics() { try { const response = await fetch(`${ALERTS_API_BASE}/topics`); if (!response.ok) throw new Error('API not available'); const data = await response.json(); alertsData.topics = Array.isArray(data.items) ? data.items : (Array.isArray(data) ? data : []); renderTopics(); } catch (error) { console.log('Topics API not available, using demo data'); // Demo data alertsData.topics = [ { id: 'topic_1', name: 'Digitale Bildung', feed_url: 'https://www.google.com/alerts/feeds/123', feed_type: 'rss', is_active: true, fetch_interval_minutes: 60, alert_count: 47, last_fetched_at: new Date().toISOString() }, { id: 'topic_2', name: 'Inklusion', feed_url: 'https://www.google.com/alerts/feeds/456', feed_type: 'rss', is_active: true, fetch_interval_minutes: 60, alert_count: 32, last_fetched_at: new Date(Date.now() - 1800000).toISOString() } ]; renderTopics(); } } function renderTopics() { const grid = document.getElementById('topics-grid'); grid.innerHTML = alertsData.topics.map(topic => `
📰
${topic.is_active ? 'Aktiv' : 'Pausiert'}
${escapeHtml(topic.name)}
${escapeHtml(topic.feed_url || '')}
${topic.alert_count || 0}
Alerts
${topic.fetch_interval_minutes || 60}m
Intervall
`).join('') + `
Topic hinzufuegen
`; } function openAddTopicModal() { document.getElementById('topic-name').value = ''; document.getElementById('topic-feed-url').value = ''; document.getElementById('topic-feed-type').value = 'rss'; document.getElementById('topic-interval').value = '60'; document.getElementById('add-topic-modal').classList.add('active'); } function closeAddTopicModal() { document.getElementById('add-topic-modal').classList.remove('active'); } async function saveTopic() { const name = document.getElementById('topic-name').value; const feedUrl = document.getElementById('topic-feed-url').value; const feedType = document.getElementById('topic-feed-type').value; const interval = parseInt(document.getElementById('topic-interval').value); if (!name || !feedUrl) { alert('Bitte Name und Feed-URL eingeben'); return; } try { await fetch(`${ALERTS_API_BASE}/topics`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, feed_url: feedUrl, feed_type: feedType, fetch_interval_minutes: interval }) }); } catch (error) { console.log('Demo mode: saving topic'); alertsData.topics.push({ id: 'topic_' + Date.now(), name, feed_url: feedUrl, feed_type: feedType, is_active: true, fetch_interval_minutes: interval, alert_count: 0 }); } closeAddTopicModal(); await loadTopics(); updateAlertsStats(); } async function fetchTopic(topicId) { try { await fetch(`${ALERTS_API_BASE}/topics/${topicId}/fetch`, { method: 'POST' }); alert('Feed wird abgerufen...'); await loadAlerts(); } catch (error) { console.log('Demo mode: fetching topic'); alert('Feed wird abgerufen... (Demo)'); } } function editTopic(topicId) { alert('Topic bearbeiten: ' + topicId + ' (nicht implementiert in Demo)'); } /* ========================================== RULES ========================================== */ async function loadRules() { try { const response = await fetch(`${ALERTS_API_BASE}/rules`); if (!response.ok) throw new Error('API not available'); const data = await response.json(); alertsData.rules = Array.isArray(data.items) ? data.items : (Array.isArray(data) ? data : []); renderRules(); } catch (error) { console.log('Rules API not available, using demo data'); // Demo data alertsData.rules = [ { id: 'rule_1', name: 'Stellenanzeigen ausschliessen', conditions: [{ field: 'title', operator: 'contains', value: 'Stellenangebot' }], action_type: 'drop', is_active: true, priority: 10 }, { id: 'rule_2', name: 'Inklusion priorisieren', conditions: [{ field: 'title', operator: 'contains', value: 'Inklusion' }], action_type: 'keep', is_active: true, priority: 5 } ]; renderRules(); } } function renderRules() { const list = document.getElementById('rules-list'); if (alertsData.rules.length === 0) { list.innerHTML = '
📋

Keine Regeln

Erstellen Sie Regeln zur automatischen Filterung.

'; return; } list.innerHTML = alertsData.rules.map(rule => { const condition = rule.conditions && rule.conditions[0]; const conditionText = condition ? `${condition.field} ${condition.operator} "${condition.value}"` : 'Keine Bedingung'; return `
${escapeHtml(rule.name)}
Wenn: ${conditionText}
${rule.action_type.toUpperCase()}
`; }).join(''); } function openAddRuleModal() { document.getElementById('rule-name').value = ''; document.getElementById('rule-field').value = 'title'; document.getElementById('rule-operator').value = 'contains'; document.getElementById('rule-value').value = ''; document.getElementById('rule-action').value = 'drop'; document.getElementById('add-rule-modal').classList.add('active'); } function closeAddRuleModal() { document.getElementById('add-rule-modal').classList.remove('active'); } async function saveRule() { const name = document.getElementById('rule-name').value; const field = document.getElementById('rule-field').value; const operator = document.getElementById('rule-operator').value; const value = document.getElementById('rule-value').value; const action = document.getElementById('rule-action').value; if (!name || !value) { alert('Bitte Name und Wert eingeben'); return; } try { await fetch(`${ALERTS_API_BASE}/rules`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, conditions: [{ field, operator, value }], action_type: action, is_active: true }) }); } catch (error) { console.log('Demo mode: saving rule'); alertsData.rules.push({ id: 'rule_' + Date.now(), name, conditions: [{ field, operator, value }], action_type: action, is_active: true, priority: alertsData.rules.length + 1 }); } closeAddRuleModal(); await loadRules(); } async function toggleRule(ruleId) { const rule = alertsData.rules.find(r => r.id === ruleId); if (rule) { rule.is_active = !rule.is_active; renderRules(); try { await fetch(`${ALERTS_API_BASE}/rules/${ruleId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(rule) }); } catch (error) { console.log('Demo mode: toggling rule'); } } } /* ========================================== PROFILE ========================================== */ async function loadProfile() { try { const response = await fetch(`${ALERTS_API_BASE}/profile`); alertsData.profile = await response.json(); renderProfile(); } catch (error) { console.error('Error loading profile:', error); alertsData.profile = { priorities: ['Inklusion', 'digitale Bildung'], exclusions: ['Stellenanzeigen', 'Werbung'], policies: { keep_threshold: 0.7, drop_threshold: 0.3 } }; renderProfile(); } } function renderProfile() { if (!alertsData.profile) return; const priorities = alertsData.profile.priorities || []; const exclusions = alertsData.profile.exclusions || []; const policies = alertsData.profile.policies || {}; document.getElementById('profile-priorities').value = priorities.join('\\n'); document.getElementById('profile-exclusions').value = exclusions.join('\\n'); document.getElementById('profile-keep-threshold').value = policies.keep_threshold || 0.7; document.getElementById('profile-drop-threshold').value = policies.drop_threshold || 0.3; } async function saveProfile() { const priorities = document.getElementById('profile-priorities').value .split('\\n') .map(s => s.trim()) .filter(s => s); const exclusions = document.getElementById('profile-exclusions').value .split('\\n') .map(s => s.trim()) .filter(s => s); const keepThreshold = parseFloat(document.getElementById('profile-keep-threshold').value); const dropThreshold = parseFloat(document.getElementById('profile-drop-threshold').value); try { await fetch(`${ALERTS_API_BASE}/profile`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ priorities, exclusions, policies: { keep_threshold: keepThreshold, drop_threshold: dropThreshold } }) }); alert('Profil gespeichert!'); } catch (error) { console.log('Demo mode: saving profile'); alertsData.profile = { priorities, exclusions, policies: { keep_threshold: keepThreshold, drop_threshold: dropThreshold } }; alert('Profil gespeichert! (Demo)'); } } /* ========================================== SYNC & STATS ========================================== */ async function syncAllAlerts() { alert('Synchronisierung gestartet...'); await loadAlertsData(); } function updateAlertsStats() { const newCount = alertsData.items.filter(a => a.status === 'new').length; const keepCount = alertsData.items.filter(a => a.relevance_decision === 'KEEP').length; const reviewCount = alertsData.items.filter(a => a.relevance_decision === 'REVIEW').length; const topicsCount = alertsData.topics.length; document.getElementById('alerts-stat-new').textContent = newCount; document.getElementById('alerts-stat-keep').textContent = keepCount; document.getElementById('alerts-stat-review').textContent = reviewCount; document.getElementById('alerts-stat-topics').textContent = topicsCount; document.getElementById('alerts-inbox-badge').textContent = newCount; } /* ========================================== UTILITIES ========================================== */ function formatTimeAgo(dateStr) { if (!dateStr) return '-'; const date = new Date(dateStr); const now = new Date(); const diffMs = now.getTime() - date.getTime(); const diffMins = Math.floor(diffMs / 60000); if (diffMins < 1) return 'gerade eben'; if (diffMins < 60) return `vor ${diffMins} Min.`; if (diffMins < 1440) return `vor ${Math.floor(diffMins / 60)} Std.`; return `vor ${Math.floor(diffMins / 1440)} Tagen`; } function escapeHtml(text) { if (!text) return ''; return text .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } // Register module for panel loading if (typeof PANEL_IDS !== 'undefined') { PANEL_IDS.push('panel-alerts'); } /* ========================================== GUIDED MODE - STATE & FUNCTIONS ========================================== */ let guidedState = { mode: 'guided', wizardCompleted: false, wizardStep: 1, selectedRole: null, selectedTemplates: [], templates: [], infoCards: [] }; function switchToGuidedMode() { guidedState.mode = 'guided'; document.getElementById('guided-mode-container').style.display = 'flex'; document.getElementById('expert-mode-container').style.display = 'none'; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.mode-btn[data-mode="guided"]').classList.add('active'); checkWizardState(); } function switchToExpertMode() { guidedState.mode = 'expert'; document.getElementById('guided-mode-container').style.display = 'none'; document.getElementById('expert-mode-container').style.display = 'block'; document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active')); document.querySelector('.mode-btn[data-mode="expert"]').classList.add('active'); } async function checkWizardState() { try { const response = await fetch(`${ALERTS_API_BASE}/wizard/state`); if (!response.ok) throw new Error('API not available'); const data = await response.json(); guidedState.wizardCompleted = data.wizard_completed; guidedState.selectedRole = data.user_role; guidedState.selectedTemplates = data.selected_template_ids || []; if (data.wizard_completed) { showGuidedInbox(); } else { showWizard(); } } catch (error) { console.log('Demo mode: wizard state check, showing wizard'); showWizard(); } } function showWizard() { console.log('showWizard() called, current step:', guidedState.wizardStep); const wizard = document.getElementById('guided-wizard'); const wasAlreadyActive = wizard.classList.contains('active'); wizard.classList.add('active'); document.getElementById('guided-inbox').classList.remove('active'); // Only reset to step 1 if wizard wasn't already active if (!wasAlreadyActive) { console.log('Wizard not active, initializing from step 1'); goToWizardStep(1); loadTemplatesForWizard(); } else { console.log('Wizard already active, keeping current step:', guidedState.wizardStep); } initWizardEventListeners(); console.log('showWizard() completed'); } function initWizardEventListeners() { console.log('initWizardEventListeners() called'); // Use event delegation on the wizard container - more reliable than individual listeners const wizard = document.getElementById('guided-wizard'); if (!wizard) { console.log('ERROR: guided-wizard element not found!'); return; } console.log('Found wizard element:', wizard); // Remove any existing handler to prevent duplicates wizard.removeEventListener('click', handleWizardClick); wizard.addEventListener('click', handleWizardClick); console.log('Wizard event delegation initialized successfully'); } function handleWizardClick(e) { const target = e.target; console.log('Wizard click:', target.className, target.id); // Handle role card clicks (check for card or child elements) const roleCard = target.closest('.role-card'); if (roleCard && roleCard.dataset.role) { e.preventDefault(); const role = roleCard.dataset.role; console.log('Role card clicked:', role); guidedState.selectedRole = role; document.querySelectorAll('.role-card').forEach(c => c.classList.remove('selected')); roleCard.classList.add('selected'); const btn = document.getElementById('wizard-next-1'); if (btn) { btn.disabled = false; btn.removeAttribute('disabled'); console.log('Button enabled'); } return; } // Handle Weiter button Step 1 if (target.id === 'wizard-next-1' || target.closest('#wizard-next-1')) { e.preventDefault(); const btn = document.getElementById('wizard-next-1'); console.log('Next 1 clicked, disabled:', btn ? btn.disabled : 'btn not found'); if (btn && !btn.disabled) { console.log('Going to step 2'); goToWizardStep(2); } return; } // Handle Zurueck button Step 2 if (target.id === 'wizard-back-2' || target.closest('#wizard-back-2')) { e.preventDefault(); console.log('Back to step 1'); goToWizardStep(1); return; } // Handle Weiter button Step 2 if (target.id === 'wizard-next-2' || target.closest('#wizard-next-2')) { e.preventDefault(); const btn = document.getElementById('wizard-next-2'); console.log('Next 2 clicked, disabled:', btn ? btn.disabled : 'btn not found'); if (btn && !btn.disabled) { console.log('Going to step 3'); goToWizardStep(3); } return; } // Handle Zurueck button Step 3 if (target.id === 'wizard-back-3' || target.closest('#wizard-back-3')) { e.preventDefault(); console.log('Back to step 2'); goToWizardStep(2); return; } // Handle Fertig button Step 3 if (target.id === 'wizard-finish' || target.closest('#wizard-finish')) { e.preventDefault(); console.log('Finish wizard'); completeWizard(); return; } // Handle Skip button if (target.id === 'wizard-skip-btn' || target.closest('#wizard-skip-btn')) { e.preventDefault(); if (confirm('Ueberspringen? Sie koennen spaeter anpassen.')) { switchToExpertMode(); } return; } // Handle template card clicks const templateCard = target.closest('.template-card'); if (templateCard && templateCard.dataset.templateId) { e.preventDefault(); const templateId = templateCard.dataset.templateId; console.log('Template card clicked:', templateId); toggleTemplate(templateId); return; } } function showGuidedInbox() { document.getElementById('guided-wizard').classList.remove('active'); document.getElementById('guided-inbox').classList.add('active'); loadInfoCards(); } function selectRole(role, element) { guidedState.selectedRole = role; document.querySelectorAll('.role-card').forEach(c => c.classList.remove('selected')); element.classList.add('selected'); const btn = document.getElementById('wizard-next-1'); if (btn) { btn.disabled = false; btn.removeAttribute('disabled'); } } function goToWizardStep(step) { console.log('goToWizardStep called with step:', step); for (let i = 1; i <= 3; i++) { const stepEl = document.getElementById(`wizard-step-${i}`); if (stepEl) { stepEl.classList.remove('active'); console.log(`Step ${i} active removed, display:`, getComputedStyle(stepEl).display); } const dotEl = document.getElementById(`wizard-dot-${i}`); if (dotEl) dotEl.classList.remove('active', 'completed'); if (i < 3) { const lineEl = document.getElementById(`wizard-line-${i}`); if (lineEl) lineEl.classList.remove('completed'); } } const targetStep = document.getElementById(`wizard-step-${step}`); if (targetStep) { targetStep.classList.add('active'); console.log(`Step ${step} activated, display:`, getComputedStyle(targetStep).display); } else { console.log('ERROR: wizard-step-' + step + ' not found!'); } for (let i = 1; i <= step; i++) { if (i < step) { const dotEl = document.getElementById(`wizard-dot-${i}`); if (dotEl) dotEl.classList.add('completed'); if (i < 3) { const lineEl = document.getElementById(`wizard-line-${i}`); if (lineEl) lineEl.classList.add('completed'); } } else { const dotEl = document.getElementById(`wizard-dot-${i}`); if (dotEl) dotEl.classList.add('active'); } } guidedState.wizardStep = step; console.log('Wizard step set to:', guidedState.wizardStep); if (step === 3) updateConfirmation(); } async function loadTemplatesForWizard() { try { const response = await fetch(`${ALERTS_API_BASE}/templates`); const data = await response.json(); guidedState.templates = data.templates || []; renderTemplateGrid(); } catch (error) { guidedState.templates = [ { id: '1', slug: 'foerderprogramme', name: 'Foerderprogramme', icon: '💰', description: 'Foerdergelder, Antragsfristen' }, { id: '2', slug: 'abitur-updates', name: 'Abitur-Updates', icon: '📝', description: 'Pruefungstermine, KMK-Beschluesse' }, { id: '3', slug: 'fortbildungen', name: 'Fortbildungen', icon: '🎓', description: 'Seminare, Workshops' }, { id: '4', slug: 'datenschutz-recht', name: 'Datenschutz & Recht', icon: '⚖', description: 'DSGVO-Updates, Urteile' }, { id: '5', slug: 'it-security', name: 'IT-Security', icon: '🔒', description: 'Sicherheitsluecken, Phishing' }, { id: '6', slug: 'wettbewerbe', name: 'Wettbewerbe', icon: '🏆', description: 'Schueler-Wettbewerbe' } ]; renderTemplateGrid(); } } function renderTemplateGrid() { const grid = document.getElementById('template-grid'); grid.innerHTML = guidedState.templates.map(t => `
${guidedState.selectedTemplates.includes(t.id) ? '✓' : ''}
${t.icon}
${t.name}
${t.description}
`).join(''); } function toggleTemplate(templateId) { const idx = guidedState.selectedTemplates.indexOf(templateId); if (idx > -1) { guidedState.selectedTemplates.splice(idx, 1); } else if (guidedState.selectedTemplates.length < 3) { guidedState.selectedTemplates.push(templateId); } else { alert('Maximal 3 Themen auswaehlbar.'); return; } renderTemplateGrid(); document.getElementById('template-count').textContent = guidedState.selectedTemplates.length; const btn2 = document.getElementById('wizard-next-2'); if (guidedState.selectedTemplates.length > 0) { btn2.disabled = false; btn2.removeAttribute('disabled'); } else { btn2.disabled = true; btn2.setAttribute('disabled', 'disabled'); } } function updateConfirmation() { const roleLabels = { 'lehrkraft': 'Lehrkraft', 'schulleitung': 'Schulleitung', 'it_beauftragte': 'IT-Beauftragte/r' }; document.getElementById('confirm-role').textContent = roleLabels[guidedState.selectedRole] || '-'; document.getElementById('confirm-templates').innerHTML = guidedState.selectedTemplates.map(id => { const t = guidedState.templates.find(x => x.id === id); return `${t ? t.name : id}`; }).join(''); } async function completeWizard() { const email = document.getElementById('digest-email').value; try { await fetch(`${ALERTS_API_BASE}/wizard/step/1`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ role: guidedState.selectedRole }) }); await fetch(`${ALERTS_API_BASE}/wizard/step/2`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ template_ids: guidedState.selectedTemplates }) }); await fetch(`${ALERTS_API_BASE}/wizard/step/3`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ notification_email: email }) }); await fetch(`${ALERTS_API_BASE}/wizard/complete`, { method: 'POST' }); } catch (e) { console.log('Demo mode'); } guidedState.wizardCompleted = true; showGuidedInbox(); } function skipWizard() { if (confirm('Ueberspringen? Sie koennen spaeter anpassen.')) switchToExpertMode(); } async function loadInfoCards() { try { const response = await fetch(`${ALERTS_API_BASE}/inbox/guided?limit=10`); if (!response.ok) throw new Error('API not available'); const data = await response.json(); guidedState.infoCards = Array.isArray(data.items) ? data.items : (Array.isArray(data) ? data : []); renderInfoCards(); } catch (error) { console.log('Guided inbox API not available, using demo data'); guidedState.infoCards = [ { id: '1', title: 'DigitalPakt 2.0: Neue Antragsphase', source_name: 'BMBF', importance_level: 'dringend', why_relevant: 'Frist endet in 45 Tagen.', next_steps: ['Schultraeger informieren'], fetched_at: new Date().toISOString() }, { id: '2', title: 'Kritische Sicherheitsluecke in Moodle', source_name: 'BSI', importance_level: 'kritisch', why_relevant: 'Sofortiges Update erforderlich.', next_steps: ['Systeme pruefen', 'Update einspielen'], fetched_at: new Date().toISOString() } ]; renderInfoCards(); } } function renderInfoCards() { const list = document.getElementById('info-cards-list'); const empty = document.getElementById('guided-empty-state'); document.getElementById('guided-alert-count').textContent = guidedState.infoCards.length; if (guidedState.infoCards.length === 0) { list.style.display = 'none'; empty.style.display = 'flex'; return; } list.style.display = 'flex'; empty.style.display = 'none'; const importanceLabels = { 'kritisch': 'Kritisch', 'dringend': 'Dringend', 'wichtig': 'Wichtig', 'pruefen': 'Pruefen', 'info': 'Info' }; list.innerHTML = guidedState.infoCards.map(card => { const lvl = (card.importance_level || 'info').toLowerCase(); const stepsHtml = (card.next_steps || []).map(s => `
${escapeHtml(s)}
`).join(''); return `
${importanceLabels[lvl] || 'Info'} ${formatTimeAgo(card.fetched_at)}
${escapeHtml(card.title)}
${escapeHtml(card.source_name || '')}
💡 Warum relevant?
${escapeHtml(card.why_relevant || '')}
${stepsHtml ? `
Naechste Schritte:
${stepsHtml}
` : ''}
`; }).join(''); } async function sendQuickFeedback(cardId, feedbackType) { try { await fetch(`${ALERTS_API_BASE}/feedback/quick`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ alert_id: cardId, feedback_type: feedbackType }) }); } catch (e) { console.log('Demo mode'); } guidedState.infoCards = guidedState.infoCards.filter(c => c.id !== cardId); renderInfoCards(); showToast(feedbackType === 'more_like_this' ? 'Mehr aehnliche Meldungen!' : 'Weniger solche Meldungen.'); } function showToast(msg) { const t = document.createElement('div'); t.style.cssText = 'position:fixed;bottom:20px;right:20px;background:#1e293b;color:white;padding:12px 20px;border-radius:8px;font-size:14px;z-index:10000;'; t.textContent = msg; document.body.appendChild(t); setTimeout(() => t.remove(), 3000); } function showDigestModal() { alert('Wochenbericht wird geladen...'); } function openGuidedSettings() { guidedState.wizardCompleted = false; showWizard(); } // Auto-init guided mode when panel becomes visible (function() { // Wait for DOM to be ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initGuidedModeOnLoad); } else { setTimeout(initGuidedModeOnLoad, 100); } function initGuidedModeOnLoad() { // Check if guided mode container exists and alerts panel is active const guidedContainer = document.getElementById('guided-mode-container'); const alertsPanel = document.getElementById('panel-alerts'); // Only initialize if alerts panel is visible if (guidedContainer && alertsPanel && alertsPanel.style.display !== 'none') { showWizard(); console.log('Guided Mode initialized'); } } // Export init function for module loading window.loadAlertsModule = function() { const guidedContainer = document.getElementById('guided-mode-container'); if (guidedContainer) { showWizard(); console.log('Alerts module loaded'); } }; })(); """