A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1583 lines
40 KiB
Python
1583 lines
40 KiB
Python
"""
|
|
BreakPilot Studio - Alerts Guided Mode Modul
|
|
|
|
Dieses Modul implementiert den Guided Mode fuer das Alerts-System:
|
|
- 3-Schritt Wizard (Rolle -> Templates -> Bestaetigung)
|
|
- InfoCards mit Wichtigkeitsstufen
|
|
- 1-Klick Feedback
|
|
- Wochenzusammenfassung (Digest)
|
|
|
|
Zielgruppe: Lehrkraefte, Schulleitungen (nicht IT-affin)
|
|
Design-Prinzip: Einfache Bedienung, max. 10 Karten/Tag, deutsche Erklaerungen
|
|
"""
|
|
|
|
|
|
class AlertsGuidedModule:
|
|
"""Guided Mode fuer das Alerts-System."""
|
|
|
|
@staticmethod
|
|
def get_css() -> str:
|
|
"""CSS fuer den Guided Mode."""
|
|
return """
|
|
/* ==========================================
|
|
ALERTS GUIDED MODE STYLES
|
|
========================================== */
|
|
|
|
/* Mode Switcher */
|
|
.alerts-mode-switcher {
|
|
display: flex;
|
|
gap: 8px;
|
|
padding: 4px;
|
|
background: var(--bp-surface-elevated);
|
|
border-radius: 8px;
|
|
border: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.mode-btn {
|
|
padding: 8px 16px;
|
|
border: none;
|
|
background: transparent;
|
|
border-radius: 6px;
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--bp-text-muted);
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.mode-btn:hover {
|
|
background: var(--bp-surface);
|
|
}
|
|
|
|
.mode-btn.active {
|
|
background: var(--bp-primary);
|
|
color: white;
|
|
}
|
|
|
|
/* Wizard Container */
|
|
.guided-wizard {
|
|
display: none;
|
|
flex-direction: column;
|
|
max-width: 800px;
|
|
margin: 40px auto;
|
|
padding: 0 20px;
|
|
}
|
|
|
|
.guided-wizard.active {
|
|
display: flex;
|
|
}
|
|
|
|
/* Wizard Progress */
|
|
.wizard-progress {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
margin-bottom: 40px;
|
|
}
|
|
|
|
.wizard-step-indicator {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.wizard-step-dot {
|
|
width: 36px;
|
|
height: 36px;
|
|
border-radius: 50%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
background: var(--bp-surface);
|
|
border: 2px solid var(--bp-border);
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.wizard-step-dot.active {
|
|
background: var(--bp-primary);
|
|
border-color: var(--bp-primary);
|
|
color: white;
|
|
}
|
|
|
|
.wizard-step-dot.completed {
|
|
background: var(--bp-success);
|
|
border-color: var(--bp-success);
|
|
color: white;
|
|
}
|
|
|
|
.wizard-step-line {
|
|
width: 60px;
|
|
height: 2px;
|
|
background: var(--bp-border);
|
|
}
|
|
|
|
.wizard-step-line.completed {
|
|
background: var(--bp-success);
|
|
}
|
|
|
|
/* Wizard Step Content */
|
|
.wizard-step {
|
|
display: none;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
}
|
|
|
|
.wizard-step.active {
|
|
display: flex;
|
|
}
|
|
|
|
.wizard-step-title {
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
color: var(--bp-text);
|
|
margin-bottom: 12px;
|
|
text-align: center;
|
|
}
|
|
|
|
.wizard-step-description {
|
|
font-size: 16px;
|
|
color: var(--bp-text-muted);
|
|
text-align: center;
|
|
max-width: 500px;
|
|
margin-bottom: 32px;
|
|
}
|
|
|
|
/* Role Selection */
|
|
.role-cards {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
|
gap: 16px;
|
|
width: 100%;
|
|
max-width: 720px;
|
|
}
|
|
|
|
.role-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
padding: 24px;
|
|
background: var(--bp-surface);
|
|
border: 2px solid var(--bp-border);
|
|
border-radius: 16px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.role-card:hover {
|
|
border-color: var(--bp-primary);
|
|
transform: translateY(-2px);
|
|
}
|
|
|
|
.role-card.selected {
|
|
border-color: var(--bp-primary);
|
|
background: rgba(var(--bp-primary-rgb), 0.05);
|
|
}
|
|
|
|
.role-card-icon {
|
|
font-size: 40px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.role-card-title {
|
|
font-size: 18px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.role-card-description {
|
|
font-size: 13px;
|
|
color: var(--bp-text-muted);
|
|
text-align: center;
|
|
}
|
|
|
|
/* Template Selection */
|
|
.template-grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
|
gap: 16px;
|
|
width: 100%;
|
|
max-width: 900px;
|
|
}
|
|
|
|
.template-card {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 20px;
|
|
background: var(--bp-surface);
|
|
border: 2px solid var(--bp-border);
|
|
border-radius: 12px;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
position: relative;
|
|
}
|
|
|
|
.template-card:hover {
|
|
border-color: var(--bp-primary);
|
|
}
|
|
|
|
.template-card.selected {
|
|
border-color: var(--bp-primary);
|
|
background: rgba(var(--bp-primary-rgb), 0.05);
|
|
}
|
|
|
|
.template-card.disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
.template-card-header {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 12px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.template-card-icon {
|
|
font-size: 28px;
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
.template-card-info {
|
|
flex: 1;
|
|
}
|
|
|
|
.template-card-name {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.template-card-desc {
|
|
font-size: 13px;
|
|
color: var(--bp-text-muted);
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.template-card-check {
|
|
position: absolute;
|
|
top: 12px;
|
|
right: 12px;
|
|
width: 24px;
|
|
height: 24px;
|
|
border-radius: 50%;
|
|
border: 2px solid var(--bp-border);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-size: 14px;
|
|
color: white;
|
|
background: transparent;
|
|
}
|
|
|
|
.template-card.selected .template-card-check {
|
|
background: var(--bp-primary);
|
|
border-color: var(--bp-primary);
|
|
}
|
|
|
|
.template-selection-info {
|
|
text-align: center;
|
|
margin-top: 16px;
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.template-selection-count {
|
|
font-weight: 600;
|
|
color: var(--bp-primary);
|
|
}
|
|
|
|
/* Confirmation Step */
|
|
.confirmation-summary {
|
|
width: 100%;
|
|
max-width: 500px;
|
|
background: var(--bp-surface);
|
|
border-radius: 16px;
|
|
padding: 24px;
|
|
border: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.confirmation-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
padding: 12px 0;
|
|
border-bottom: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.confirmation-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.confirmation-label {
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.confirmation-value {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--bp-text);
|
|
}
|
|
|
|
.confirmation-templates {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
justify-content: flex-end;
|
|
}
|
|
|
|
.confirmation-template-tag {
|
|
background: var(--bp-primary-soft);
|
|
color: var(--bp-primary);
|
|
padding: 4px 10px;
|
|
border-radius: 12px;
|
|
font-size: 12px;
|
|
font-weight: 500;
|
|
}
|
|
|
|
/* Email Input */
|
|
.email-input-group {
|
|
margin-top: 24px;
|
|
width: 100%;
|
|
}
|
|
|
|
.email-input-label {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--bp-text);
|
|
margin-bottom: 8px;
|
|
display: block;
|
|
}
|
|
|
|
.email-input-hint {
|
|
font-size: 12px;
|
|
color: var(--bp-text-muted);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.email-input {
|
|
width: 100%;
|
|
padding: 12px 16px;
|
|
border: 1px solid var(--bp-border);
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
background: var(--bp-surface);
|
|
color: var(--bp-text);
|
|
}
|
|
|
|
.email-input:focus {
|
|
outline: none;
|
|
border-color: var(--bp-primary);
|
|
}
|
|
|
|
/* Wizard Navigation */
|
|
.wizard-nav {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
margin-top: 40px;
|
|
width: 100%;
|
|
max-width: 500px;
|
|
}
|
|
|
|
.wizard-nav-btn {
|
|
padding: 12px 24px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.wizard-nav-btn.secondary {
|
|
background: transparent;
|
|
border: 1px solid var(--bp-border);
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.wizard-nav-btn.secondary:hover {
|
|
background: var(--bp-surface);
|
|
}
|
|
|
|
.wizard-nav-btn.primary {
|
|
background: var(--bp-primary);
|
|
border: none;
|
|
color: white;
|
|
}
|
|
|
|
.wizard-nav-btn.primary:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
.wizard-nav-btn:disabled {
|
|
opacity: 0.5;
|
|
cursor: not-allowed;
|
|
}
|
|
|
|
/* ==========================================
|
|
INFO CARDS (Guided Inbox)
|
|
========================================== */
|
|
|
|
.guided-inbox {
|
|
display: none;
|
|
flex-direction: column;
|
|
height: 100%;
|
|
}
|
|
|
|
.guided-inbox.active {
|
|
display: flex;
|
|
}
|
|
|
|
/* Inbox Header */
|
|
.guided-inbox-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 20px 32px;
|
|
border-bottom: 1px solid var(--bp-border);
|
|
background: var(--bp-surface);
|
|
}
|
|
|
|
.guided-inbox-title h2 {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.guided-inbox-subtitle {
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.guided-inbox-actions {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
/* Info Cards List */
|
|
.info-cards-container {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 24px 32px;
|
|
}
|
|
|
|
.info-cards-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 16px;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
}
|
|
|
|
/* Single Info Card */
|
|
.info-card {
|
|
background: var(--bp-surface);
|
|
border: 1px solid var(--bp-border);
|
|
border-radius: 12px;
|
|
overflow: hidden;
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.info-card:hover {
|
|
box-shadow: 0 4px 12px rgba(0,0,0,0.08);
|
|
}
|
|
|
|
/* Card Header with Importance */
|
|
.info-card-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 12px 16px;
|
|
background: var(--bp-surface-elevated);
|
|
border-bottom: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.info-card-importance {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.importance-badge {
|
|
padding: 4px 10px;
|
|
border-radius: 12px;
|
|
font-size: 11px;
|
|
font-weight: 700;
|
|
text-transform: uppercase;
|
|
letter-spacing: 0.5px;
|
|
}
|
|
|
|
.importance-badge.kritisch {
|
|
background: #fef2f2;
|
|
color: #dc2626;
|
|
}
|
|
|
|
.importance-badge.dringend {
|
|
background: #fff7ed;
|
|
color: #ea580c;
|
|
}
|
|
|
|
.importance-badge.wichtig {
|
|
background: #fffbeb;
|
|
color: #d97706;
|
|
}
|
|
|
|
.importance-badge.pruefen {
|
|
background: #eff6ff;
|
|
color: #2563eb;
|
|
}
|
|
|
|
.importance-badge.info {
|
|
background: #f8fafc;
|
|
color: #64748b;
|
|
}
|
|
|
|
.info-card-time {
|
|
font-size: 12px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
/* Card Body */
|
|
.info-card-body {
|
|
padding: 16px;
|
|
}
|
|
|
|
.info-card-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
margin-bottom: 8px;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
.info-card-source {
|
|
font-size: 13px;
|
|
color: var(--bp-primary);
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
/* Why Relevant Section */
|
|
.info-card-why {
|
|
background: var(--bp-surface-elevated);
|
|
border-radius: 8px;
|
|
padding: 12px;
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.info-card-why-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--bp-text-muted);
|
|
margin-bottom: 6px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
}
|
|
|
|
.info-card-why-content {
|
|
font-size: 14px;
|
|
color: var(--bp-text);
|
|
line-height: 1.5;
|
|
}
|
|
|
|
/* Next Steps Section */
|
|
.info-card-steps {
|
|
margin-top: 12px;
|
|
}
|
|
|
|
.info-card-steps-label {
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
color: var(--bp-text-muted);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.info-card-step {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: 8px;
|
|
font-size: 13px;
|
|
color: var(--bp-text);
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.info-card-step-checkbox {
|
|
width: 16px;
|
|
height: 16px;
|
|
border: 2px solid var(--bp-border);
|
|
border-radius: 4px;
|
|
flex-shrink: 0;
|
|
margin-top: 2px;
|
|
}
|
|
|
|
/* Card Footer with Actions */
|
|
.info-card-footer {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 12px 16px;
|
|
border-top: 1px solid var(--bp-border);
|
|
background: var(--bp-surface-elevated);
|
|
}
|
|
|
|
.info-card-feedback {
|
|
display: flex;
|
|
gap: 8px;
|
|
}
|
|
|
|
.feedback-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 14px;
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
border: 1px solid var(--bp-border);
|
|
background: var(--bp-surface);
|
|
color: var(--bp-text-muted);
|
|
transition: all 0.2s;
|
|
}
|
|
|
|
.feedback-btn:hover {
|
|
background: var(--bp-surface-elevated);
|
|
}
|
|
|
|
.feedback-btn.negative:hover {
|
|
border-color: #ef4444;
|
|
color: #ef4444;
|
|
}
|
|
|
|
.feedback-btn.positive:hover {
|
|
border-color: var(--bp-success);
|
|
color: var(--bp-success);
|
|
}
|
|
|
|
.info-card-open {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 6px;
|
|
padding: 8px 16px;
|
|
background: var(--bp-primary);
|
|
color: white;
|
|
border: none;
|
|
border-radius: 8px;
|
|
font-size: 13px;
|
|
font-weight: 500;
|
|
cursor: pointer;
|
|
transition: opacity 0.2s;
|
|
}
|
|
|
|
.info-card-open:hover {
|
|
opacity: 0.9;
|
|
}
|
|
|
|
/* Empty State */
|
|
.guided-empty-state {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 80px 20px;
|
|
text-align: center;
|
|
}
|
|
|
|
.guided-empty-icon {
|
|
font-size: 64px;
|
|
margin-bottom: 20px;
|
|
opacity: 0.5;
|
|
}
|
|
|
|
.guided-empty-title {
|
|
font-size: 20px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
margin-bottom: 8px;
|
|
}
|
|
|
|
.guided-empty-description {
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
max-width: 400px;
|
|
}
|
|
|
|
/* ==========================================
|
|
DIGEST VIEW
|
|
========================================== */
|
|
|
|
.digest-container {
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 24px;
|
|
}
|
|
|
|
.digest-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.digest-period {
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.digest-export-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 16px;
|
|
background: var(--bp-surface);
|
|
border: 1px solid var(--bp-border);
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
color: var(--bp-text);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.digest-export-btn:hover {
|
|
background: var(--bp-surface-elevated);
|
|
}
|
|
|
|
.digest-section {
|
|
background: var(--bp-surface);
|
|
border: 1px solid var(--bp-border);
|
|
border-radius: 12px;
|
|
margin-bottom: 20px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.digest-section-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 16px 20px;
|
|
background: var(--bp-surface-elevated);
|
|
border-bottom: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.digest-section-title {
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
color: var(--bp-text);
|
|
}
|
|
|
|
.digest-section-count {
|
|
font-size: 14px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.digest-items {
|
|
padding: 12px 20px;
|
|
}
|
|
|
|
.digest-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: flex-start;
|
|
padding: 12px 0;
|
|
border-bottom: 1px solid var(--bp-border);
|
|
}
|
|
|
|
.digest-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
|
|
.digest-item-title {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: var(--bp-text);
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.digest-item-source {
|
|
font-size: 12px;
|
|
color: var(--bp-text-muted);
|
|
}
|
|
|
|
.digest-item-importance {
|
|
font-size: 11px;
|
|
padding: 4px 8px;
|
|
border-radius: 8px;
|
|
font-weight: 600;
|
|
}
|
|
"""
|
|
|
|
@staticmethod
|
|
def get_html() -> str:
|
|
"""HTML fuer den Guided Mode."""
|
|
return """
|
|
<!-- GUIDED MODE CONTAINER -->
|
|
<div class="guided-mode-container" id="guided-mode-container" style="display: none;">
|
|
|
|
<!-- Wizard -->
|
|
<div class="guided-wizard" id="guided-wizard">
|
|
<!-- Progress -->
|
|
<div class="wizard-progress">
|
|
<div class="wizard-step-indicator">
|
|
<div class="wizard-step-dot active" id="wizard-dot-1">1</div>
|
|
<div class="wizard-step-line" id="wizard-line-1"></div>
|
|
</div>
|
|
<div class="wizard-step-indicator">
|
|
<div class="wizard-step-dot" id="wizard-dot-2">2</div>
|
|
<div class="wizard-step-line" id="wizard-line-2"></div>
|
|
</div>
|
|
<div class="wizard-step-indicator">
|
|
<div class="wizard-step-dot" id="wizard-dot-3">3</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 1: Role Selection -->
|
|
<div class="wizard-step active" id="wizard-step-1">
|
|
<h2 class="wizard-step-title">Was beschreibt Sie am besten?</h2>
|
|
<p class="wizard-step-description">
|
|
Wir zeigen Ihnen passende Themen basierend auf Ihrer Rolle.
|
|
</p>
|
|
<div class="role-cards">
|
|
<div class="role-card" onclick="selectRole('lehrkraft')">
|
|
<div class="role-card-icon">📚</div>
|
|
<div class="role-card-title">Ich unterrichte</div>
|
|
<div class="role-card-description">Lehrkraft mit Fokus auf Unterricht und Schueler</div>
|
|
</div>
|
|
<div class="role-card" onclick="selectRole('schulleitung')">
|
|
<div class="role-card-icon">🏫</div>
|
|
<div class="role-card-title">Ich leite die Schule</div>
|
|
<div class="role-card-description">Schulleitung, Verwaltung, Organisation</div>
|
|
</div>
|
|
<div class="role-card" onclick="selectRole('it_beauftragte')">
|
|
<div class="role-card-icon">💻</div>
|
|
<div class="role-card-title">Ich bin IT-verantwortlich</div>
|
|
<div class="role-card-description">IT-Beauftragte, technischer Support</div>
|
|
</div>
|
|
</div>
|
|
<div class="wizard-nav">
|
|
<button class="wizard-nav-btn secondary" onclick="skipWizard()">Ueberspringen</button>
|
|
<button class="wizard-nav-btn primary" id="wizard-next-1" disabled onclick="goToWizardStep(2)">Weiter</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 2: Template Selection -->
|
|
<div class="wizard-step" id="wizard-step-2">
|
|
<h2 class="wizard-step-title">Welche Themen interessieren Sie?</h2>
|
|
<p class="wizard-step-description">
|
|
Waehlen Sie 1-3 Themen. Sie koennen diese spaeter anpassen.
|
|
</p>
|
|
<div class="template-grid" id="template-grid">
|
|
<!-- Wird per JavaScript befuellt -->
|
|
</div>
|
|
<div class="template-selection-info">
|
|
<span class="template-selection-count" id="template-count">0</span> von 3 Themen ausgewaehlt
|
|
</div>
|
|
<div class="wizard-nav">
|
|
<button class="wizard-nav-btn secondary" onclick="goToWizardStep(1)">Zurueck</button>
|
|
<button class="wizard-nav-btn primary" id="wizard-next-2" disabled onclick="goToWizardStep(3)">Weiter</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Step 3: Confirmation -->
|
|
<div class="wizard-step" id="wizard-step-3">
|
|
<h2 class="wizard-step-title">Fast geschafft!</h2>
|
|
<p class="wizard-step-description">
|
|
Pruefen Sie Ihre Auswahl und starten Sie.
|
|
</p>
|
|
<div class="confirmation-summary">
|
|
<div class="confirmation-item">
|
|
<span class="confirmation-label">Ihre Rolle</span>
|
|
<span class="confirmation-value" id="confirm-role">-</span>
|
|
</div>
|
|
<div class="confirmation-item">
|
|
<span class="confirmation-label">Ausgewaehlte Themen</span>
|
|
<div class="confirmation-templates" id="confirm-templates">
|
|
<!-- Wird per JavaScript befuellt -->
|
|
</div>
|
|
</div>
|
|
<div class="confirmation-item">
|
|
<span class="confirmation-label">Erwartete Meldungen</span>
|
|
<span class="confirmation-value">Ca. 5-10 pro Tag</span>
|
|
</div>
|
|
</div>
|
|
<div class="email-input-group">
|
|
<label class="email-input-label">E-Mail fuer Wochenzusammenfassung (optional)</label>
|
|
<p class="email-input-hint">Jeden Montag erhalten Sie eine Zusammenfassung per E-Mail.</p>
|
|
<input type="email" class="email-input" id="digest-email" placeholder="ihre.email@schule.de">
|
|
</div>
|
|
<div class="wizard-nav">
|
|
<button class="wizard-nav-btn secondary" onclick="goToWizardStep(2)">Zurueck</button>
|
|
<button class="wizard-nav-btn primary" onclick="completeWizard()">Jetzt starten</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Guided Inbox -->
|
|
<div class="guided-inbox" id="guided-inbox">
|
|
<div class="guided-inbox-header">
|
|
<div class="guided-inbox-title">
|
|
<h2>Ihre Meldungen</h2>
|
|
<p class="guided-inbox-subtitle">Aktuell <span id="guided-alert-count">0</span> relevante Meldungen</p>
|
|
</div>
|
|
<div class="guided-inbox-actions">
|
|
<button class="btn btn-ghost" onclick="showDigestView()">
|
|
📄 Wochenbericht
|
|
</button>
|
|
<button class="btn btn-ghost" onclick="openGuidedSettings()">
|
|
⚙ Einstellungen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="info-cards-container">
|
|
<div class="info-cards-list" id="info-cards-list">
|
|
<!-- Wird per JavaScript befuellt -->
|
|
</div>
|
|
|
|
<!-- Empty State -->
|
|
<div class="guided-empty-state" id="guided-empty-state" style="display: none;">
|
|
<div class="guided-empty-icon">🎉</div>
|
|
<h3 class="guided-empty-title">Keine neuen Meldungen</h3>
|
|
<p class="guided-empty-description">
|
|
Super! Sie sind auf dem neuesten Stand. Neue Meldungen erscheinen hier automatisch.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Digest Modal -->
|
|
<div class="modal-overlay" id="digest-modal" style="display: none;">
|
|
<div class="modal" style="max-width: 900px;">
|
|
<div class="modal-header">
|
|
<h3>Wochenbericht</h3>
|
|
<button class="modal-close" onclick="closeDigestModal()">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="digest-container" id="digest-content">
|
|
<!-- Wird per JavaScript befuellt -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
"""
|
|
|
|
@staticmethod
|
|
def get_js() -> str:
|
|
"""JavaScript fuer den Guided Mode."""
|
|
return """
|
|
/* ==========================================
|
|
GUIDED MODE - STATE & CONFIG
|
|
========================================== */
|
|
|
|
const GUIDED_API_BASE = '/api/alerts';
|
|
|
|
let guidedState = {
|
|
mode: 'guided', // 'guided' oder 'expert'
|
|
wizardCompleted: false,
|
|
wizardStep: 1,
|
|
selectedRole: null,
|
|
selectedTemplates: [],
|
|
templates: [],
|
|
infoCards: [],
|
|
digestEmail: ''
|
|
};
|
|
|
|
/* ==========================================
|
|
MODE SWITCHING
|
|
========================================== */
|
|
|
|
function switchToGuidedMode() {
|
|
guidedState.mode = 'guided';
|
|
document.getElementById('guided-mode-container').style.display = 'block';
|
|
document.getElementById('panel-alerts').querySelector('.alerts-content').style.display = 'none';
|
|
|
|
// Update mode buttons
|
|
document.querySelectorAll('.mode-btn').forEach(btn => btn.classList.remove('active'));
|
|
document.querySelector('.mode-btn[data-mode="guided"]').classList.add('active');
|
|
|
|
// Check wizard state
|
|
checkWizardState();
|
|
}
|
|
|
|
function switchToExpertMode() {
|
|
guidedState.mode = 'expert';
|
|
document.getElementById('guided-mode-container').style.display = 'none';
|
|
document.getElementById('panel-alerts').querySelector('.alerts-content').style.display = 'block';
|
|
|
|
// Update mode buttons
|
|
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(`${GUIDED_API_BASE}/wizard/state`);
|
|
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');
|
|
showWizard();
|
|
}
|
|
}
|
|
|
|
/* ==========================================
|
|
WIZARD FUNCTIONS
|
|
========================================== */
|
|
|
|
function showWizard() {
|
|
document.getElementById('guided-wizard').classList.add('active');
|
|
document.getElementById('guided-inbox').classList.remove('active');
|
|
loadTemplatesForWizard();
|
|
}
|
|
|
|
function showGuidedInbox() {
|
|
document.getElementById('guided-wizard').classList.remove('active');
|
|
document.getElementById('guided-inbox').classList.add('active');
|
|
loadInfoCards();
|
|
}
|
|
|
|
function goToWizardStep(step) {
|
|
// Update step visibility
|
|
for (let i = 1; i <= 3; i++) {
|
|
document.getElementById(`wizard-step-${i}`).classList.remove('active');
|
|
document.getElementById(`wizard-dot-${i}`).classList.remove('active', 'completed');
|
|
if (i < 3) {
|
|
document.getElementById(`wizard-line-${i}`).classList.remove('completed');
|
|
}
|
|
}
|
|
|
|
document.getElementById(`wizard-step-${step}`).classList.add('active');
|
|
|
|
// Update progress indicators
|
|
for (let i = 1; i <= step; i++) {
|
|
if (i < step) {
|
|
document.getElementById(`wizard-dot-${i}`).classList.add('completed');
|
|
if (i < 3) {
|
|
document.getElementById(`wizard-line-${i}`).classList.add('completed');
|
|
}
|
|
} else {
|
|
document.getElementById(`wizard-dot-${i}`).classList.add('active');
|
|
}
|
|
}
|
|
|
|
guidedState.wizardStep = step;
|
|
|
|
// Update confirmation if on step 3
|
|
if (step === 3) {
|
|
updateConfirmation();
|
|
}
|
|
}
|
|
|
|
function selectRole(role) {
|
|
guidedState.selectedRole = role;
|
|
|
|
// Update UI
|
|
document.querySelectorAll('.role-card').forEach(card => card.classList.remove('selected'));
|
|
event.currentTarget.classList.add('selected');
|
|
|
|
// Enable next button
|
|
document.getElementById('wizard-next-1').disabled = false;
|
|
|
|
// Update template recommendations
|
|
filterTemplatesByRole(role);
|
|
}
|
|
|
|
async function loadTemplatesForWizard() {
|
|
try {
|
|
const response = await fetch(`${GUIDED_API_BASE}/templates`);
|
|
const data = await response.json();
|
|
guidedState.templates = data.templates || [];
|
|
renderTemplateGrid();
|
|
} catch (error) {
|
|
console.log('Demo mode: loading templates');
|
|
guidedState.templates = getDemoTemplates();
|
|
renderTemplateGrid();
|
|
}
|
|
}
|
|
|
|
function getDemoTemplates() {
|
|
return [
|
|
{ id: '1', slug: 'foerderprogramme', name: 'Foerderprogramme & Fristen', icon: '💰', description: 'Foerdergelder, Antragsfristen, EU-Programme', target_roles: ['schulleitung'] },
|
|
{ id: '2', slug: 'abitur-updates', name: 'Abitur-Updates', icon: '📝', description: 'Pruefungstermine, Operatoren, KMK-Beschluesse', target_roles: ['lehrkraft'] },
|
|
{ id: '3', slug: 'fortbildungen', name: 'Fortbildungen', icon: '🎓', description: 'Seminare, Workshops, Online-Kurse', target_roles: ['lehrkraft'] },
|
|
{ id: '4', slug: 'datenschutz-recht', name: 'Datenschutz & Recht', icon: '⚖', description: 'DSGVO-Updates, Urteile, Handreichungen', target_roles: ['schulleitung', 'it_beauftragte'] },
|
|
{ id: '5', slug: 'it-security', name: 'IT-Security', icon: '🔒', description: 'Sicherheitsluecken, Phishing-Warnungen', target_roles: ['it_beauftragte'] },
|
|
{ id: '6', slug: 'wettbewerbe-projekte', name: 'Wettbewerbe & Projekte', icon: '🏆', description: 'Schueler-Wettbewerbe, Projekttage', target_roles: ['lehrkraft'] }
|
|
];
|
|
}
|
|
|
|
function renderTemplateGrid() {
|
|
const grid = document.getElementById('template-grid');
|
|
grid.innerHTML = guidedState.templates.map(template => `
|
|
<div class="template-card ${guidedState.selectedTemplates.includes(template.id) ? 'selected' : ''}"
|
|
data-id="${template.id}"
|
|
onclick="toggleTemplate('${template.id}')">
|
|
<div class="template-card-check">${guidedState.selectedTemplates.includes(template.id) ? '✓' : ''}</div>
|
|
<div class="template-card-header">
|
|
<div class="template-card-icon">${template.icon}</div>
|
|
<div class="template-card-info">
|
|
<div class="template-card-name">${template.name}</div>
|
|
<div class="template-card-desc">${template.description}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
function filterTemplatesByRole(role) {
|
|
// Highlight recommended templates for role
|
|
document.querySelectorAll('.template-card').forEach(card => {
|
|
const id = card.dataset.id;
|
|
const template = guidedState.templates.find(t => t.id === id);
|
|
if (template && template.target_roles && template.target_roles.includes(role)) {
|
|
card.style.order = '-1';
|
|
} else {
|
|
card.style.order = '0';
|
|
}
|
|
});
|
|
}
|
|
|
|
function toggleTemplate(templateId) {
|
|
const index = guidedState.selectedTemplates.indexOf(templateId);
|
|
|
|
if (index > -1) {
|
|
guidedState.selectedTemplates.splice(index, 1);
|
|
} else if (guidedState.selectedTemplates.length < 3) {
|
|
guidedState.selectedTemplates.push(templateId);
|
|
} else {
|
|
alert('Maximal 3 Themen auswaehlbar. Entfernen Sie erst ein Thema.');
|
|
return;
|
|
}
|
|
|
|
// Update UI
|
|
renderTemplateGrid();
|
|
updateTemplateCount();
|
|
|
|
// Enable/disable next button
|
|
document.getElementById('wizard-next-2').disabled = guidedState.selectedTemplates.length === 0;
|
|
}
|
|
|
|
function updateTemplateCount() {
|
|
document.getElementById('template-count').textContent = guidedState.selectedTemplates.length;
|
|
}
|
|
|
|
function updateConfirmation() {
|
|
// Role
|
|
const roleLabels = {
|
|
'lehrkraft': 'Lehrkraft',
|
|
'schulleitung': 'Schulleitung',
|
|
'it_beauftragte': 'IT-Beauftragte/r'
|
|
};
|
|
document.getElementById('confirm-role').textContent = roleLabels[guidedState.selectedRole] || '-';
|
|
|
|
// Templates
|
|
const templatesContainer = document.getElementById('confirm-templates');
|
|
templatesContainer.innerHTML = guidedState.selectedTemplates.map(id => {
|
|
const template = guidedState.templates.find(t => t.id === id);
|
|
return `<span class="confirmation-template-tag">${template ? template.name : id}</span>`;
|
|
}).join('');
|
|
}
|
|
|
|
async function completeWizard() {
|
|
const email = document.getElementById('digest-email').value;
|
|
guidedState.digestEmail = email;
|
|
|
|
try {
|
|
// Step 1 speichern
|
|
await fetch(`${GUIDED_API_BASE}/wizard/step/1`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ role: guidedState.selectedRole })
|
|
});
|
|
|
|
// Step 2 speichern
|
|
await fetch(`${GUIDED_API_BASE}/wizard/step/2`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ template_ids: guidedState.selectedTemplates })
|
|
});
|
|
|
|
// Step 3 speichern
|
|
await fetch(`${GUIDED_API_BASE}/wizard/step/3`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ notification_email: email })
|
|
});
|
|
|
|
// Wizard abschliessen
|
|
await fetch(`${GUIDED_API_BASE}/wizard/complete`, {
|
|
method: 'POST'
|
|
});
|
|
|
|
} catch (error) {
|
|
console.log('Demo mode: completing wizard');
|
|
}
|
|
|
|
guidedState.wizardCompleted = true;
|
|
showGuidedInbox();
|
|
}
|
|
|
|
function skipWizard() {
|
|
if (confirm('Moechten Sie wirklich ueberspringen? Sie koennen die Einstellungen spaeter anpassen.')) {
|
|
switchToExpertMode();
|
|
}
|
|
}
|
|
|
|
/* ==========================================
|
|
INFO CARDS (Guided Inbox)
|
|
========================================== */
|
|
|
|
async function loadInfoCards() {
|
|
try {
|
|
const response = await fetch(`${GUIDED_API_BASE}/inbox/guided?limit=10`);
|
|
const data = await response.json();
|
|
guidedState.infoCards = data.items || [];
|
|
renderInfoCards();
|
|
} catch (error) {
|
|
console.log('Demo mode: loading info cards');
|
|
guidedState.infoCards = getDemoInfoCards();
|
|
renderInfoCards();
|
|
}
|
|
}
|
|
|
|
function getDemoInfoCards() {
|
|
return [
|
|
{
|
|
id: '1',
|
|
title: 'DigitalPakt 2.0: Neue Antragsphase startet am 1. April',
|
|
source_name: 'Bundesministerium fuer Bildung',
|
|
source_url: 'https://example.com/digitalpakt',
|
|
importance_level: 'dringend',
|
|
why_relevant: 'Frist endet in 45 Tagen. Betrifft alle Schulen mit Foerderbedarf.',
|
|
next_steps: ['Schultraeger kontaktieren', 'Bedarfsanalyse erstellen'],
|
|
fetched_at: new Date(Date.now() - 2 * 60 * 60 * 1000).toISOString()
|
|
},
|
|
{
|
|
id: '2',
|
|
title: 'CVE-2026-1234: Kritische Sicherheitsluecke in Moodle',
|
|
source_name: 'BSI CERT-Bund',
|
|
source_url: 'https://example.com/cve',
|
|
importance_level: 'kritisch',
|
|
why_relevant: 'Sofortiges Update erforderlich. Exploit bereits aktiv.',
|
|
next_steps: ['Betroffene Systeme pruefen', 'Update einspielen'],
|
|
fetched_at: new Date(Date.now() - 30 * 60 * 1000).toISOString()
|
|
},
|
|
{
|
|
id: '3',
|
|
title: 'Kostenlose Fortbildung: KI im Unterricht',
|
|
source_name: 'Landesinstitut',
|
|
source_url: 'https://example.com/fortbildung',
|
|
importance_level: 'pruefen',
|
|
why_relevant: 'Passt zu Ihrem Interessenprofil. Online-Format, 4 Stunden.',
|
|
next_steps: ['Termin und Ort pruefen', 'Bei Interesse anmelden'],
|
|
fetched_at: new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
|
|
}
|
|
];
|
|
}
|
|
|
|
function renderInfoCards() {
|
|
const list = document.getElementById('info-cards-list');
|
|
const emptyState = document.getElementById('guided-empty-state');
|
|
|
|
if (guidedState.infoCards.length === 0) {
|
|
list.style.display = 'none';
|
|
emptyState.style.display = 'flex';
|
|
document.getElementById('guided-alert-count').textContent = '0';
|
|
return;
|
|
}
|
|
|
|
list.style.display = 'flex';
|
|
emptyState.style.display = 'none';
|
|
document.getElementById('guided-alert-count').textContent = guidedState.infoCards.length;
|
|
|
|
list.innerHTML = guidedState.infoCards.map(card => {
|
|
const timeAgo = formatTimeAgo(card.fetched_at);
|
|
const importanceClass = (card.importance_level || 'info').toLowerCase();
|
|
const importanceLabel = {
|
|
'kritisch': 'Kritisch',
|
|
'dringend': 'Dringend',
|
|
'wichtig': 'Wichtig',
|
|
'pruefen': 'Zu pruefen',
|
|
'info': 'Info'
|
|
}[importanceClass] || 'Info';
|
|
|
|
const stepsHtml = (card.next_steps || []).map(step => `
|
|
<div class="info-card-step">
|
|
<div class="info-card-step-checkbox"></div>
|
|
<span>${escapeHtml(step)}</span>
|
|
</div>
|
|
`).join('');
|
|
|
|
return `
|
|
<div class="info-card" data-id="${card.id}">
|
|
<div class="info-card-header">
|
|
<div class="info-card-importance">
|
|
<span class="importance-badge ${importanceClass}">${importanceLabel}</span>
|
|
</div>
|
|
<span class="info-card-time">${timeAgo}</span>
|
|
</div>
|
|
<div class="info-card-body">
|
|
<div class="info-card-title">${escapeHtml(card.title)}</div>
|
|
<div class="info-card-source">${escapeHtml(card.source_name || 'Unbekannte Quelle')}</div>
|
|
|
|
<div class="info-card-why">
|
|
<div class="info-card-why-label">
|
|
💡 Warum relevant?
|
|
</div>
|
|
<div class="info-card-why-content">${escapeHtml(card.why_relevant || '')}</div>
|
|
</div>
|
|
|
|
${stepsHtml ? `
|
|
<div class="info-card-steps">
|
|
<div class="info-card-steps-label">Naechste Schritte:</div>
|
|
${stepsHtml}
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
<div class="info-card-footer">
|
|
<div class="info-card-feedback">
|
|
<button class="feedback-btn negative" onclick="sendQuickFeedback('${card.id}', 'not_relevant')">
|
|
👎 Nicht relevant
|
|
</button>
|
|
<button class="feedback-btn positive" onclick="sendQuickFeedback('${card.id}', 'more_like_this')">
|
|
👍 Mehr davon
|
|
</button>
|
|
</div>
|
|
<button class="info-card-open" onclick="openInfoCard('${card.id}', '${escapeHtml(card.source_url || '')}')">
|
|
Oeffnen →
|
|
</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}).join('');
|
|
}
|
|
|
|
function openInfoCard(cardId, url) {
|
|
if (url) {
|
|
window.open(url, '_blank');
|
|
}
|
|
}
|
|
|
|
async function sendQuickFeedback(cardId, feedbackType) {
|
|
try {
|
|
await fetch(`${GUIDED_API_BASE}/feedback/quick`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
alert_id: cardId,
|
|
feedback_type: feedbackType
|
|
})
|
|
});
|
|
} catch (error) {
|
|
console.log('Demo mode: sending feedback');
|
|
}
|
|
|
|
// Remove card from list
|
|
guidedState.infoCards = guidedState.infoCards.filter(c => c.id !== cardId);
|
|
renderInfoCards();
|
|
|
|
// Show confirmation
|
|
const message = feedbackType === 'more_like_this'
|
|
? 'Danke! Sie erhalten mehr aehnliche Meldungen.'
|
|
: 'Verstanden! Weniger solche Meldungen.';
|
|
showToast(message);
|
|
}
|
|
|
|
function showToast(message) {
|
|
// Simple toast notification
|
|
const toast = document.createElement('div');
|
|
toast.style.cssText = `
|
|
position: fixed;
|
|
bottom: 20px;
|
|
right: 20px;
|
|
background: #1e293b;
|
|
color: white;
|
|
padding: 12px 20px;
|
|
border-radius: 8px;
|
|
font-size: 14px;
|
|
z-index: 10000;
|
|
animation: fadeIn 0.3s, fadeOut 0.3s 2.7s;
|
|
`;
|
|
toast.textContent = message;
|
|
document.body.appendChild(toast);
|
|
setTimeout(() => toast.remove(), 3000);
|
|
}
|
|
|
|
/* ==========================================
|
|
DIGEST VIEW
|
|
========================================== */
|
|
|
|
async function showDigestView() {
|
|
document.getElementById('digest-modal').style.display = 'flex';
|
|
await loadDigest();
|
|
}
|
|
|
|
function closeDigestModal() {
|
|
document.getElementById('digest-modal').style.display = 'none';
|
|
}
|
|
|
|
async function loadDigest() {
|
|
const container = document.getElementById('digest-content');
|
|
|
|
try {
|
|
const response = await fetch(`${GUIDED_API_BASE}/digests?limit=1`);
|
|
const data = await response.json();
|
|
|
|
if (data.digests && data.digests.length > 0) {
|
|
renderDigest(data.digests[0]);
|
|
} else {
|
|
container.innerHTML = '<p style="text-align: center; color: var(--bp-text-muted);">Noch kein Wochenbericht verfuegbar.</p>';
|
|
}
|
|
} catch (error) {
|
|
console.log('Demo mode: loading digest');
|
|
renderDemoDigest();
|
|
}
|
|
}
|
|
|
|
function renderDemoDigest() {
|
|
const container = document.getElementById('digest-content');
|
|
container.innerHTML = `
|
|
<div class="digest-header">
|
|
<div>
|
|
<h2 style="margin: 0 0 4px 0;">Wochenbericht</h2>
|
|
<div class="digest-period">13.01.2026 - 19.01.2026</div>
|
|
</div>
|
|
<button class="digest-export-btn" onclick="exportDigestPDF()">
|
|
📄 Als PDF exportieren
|
|
</button>
|
|
</div>
|
|
|
|
<div class="digest-section">
|
|
<div class="digest-section-header">
|
|
<div class="digest-section-title">🔴 Kritisch & Dringend</div>
|
|
<div class="digest-section-count">2 Meldungen</div>
|
|
</div>
|
|
<div class="digest-items">
|
|
<div class="digest-item">
|
|
<div>
|
|
<div class="digest-item-title">CVE-2026-1234: Moodle Sicherheitsluecke</div>
|
|
<div class="digest-item-source">BSI CERT-Bund</div>
|
|
</div>
|
|
<span class="digest-item-importance" style="background: #fef2f2; color: #dc2626;">Kritisch</span>
|
|
</div>
|
|
<div class="digest-item">
|
|
<div>
|
|
<div class="digest-item-title">DigitalPakt 2.0 Antragsphase</div>
|
|
<div class="digest-item-source">BMBF</div>
|
|
</div>
|
|
<span class="digest-item-importance" style="background: #fff7ed; color: #ea580c;">Dringend</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="digest-section">
|
|
<div class="digest-section-header">
|
|
<div class="digest-section-title">🟢 Wichtig</div>
|
|
<div class="digest-section-count">3 Meldungen</div>
|
|
</div>
|
|
<div class="digest-items">
|
|
<div class="digest-item">
|
|
<div>
|
|
<div class="digest-item-title">Neue Operatoren fuer Abitur Deutsch</div>
|
|
<div class="digest-item-source">KMK</div>
|
|
</div>
|
|
<span class="digest-item-importance" style="background: #fffbeb; color: #d97706;">Wichtig</span>
|
|
</div>
|
|
<div class="digest-item">
|
|
<div>
|
|
<div class="digest-item-title">Fortbildung: KI im Unterricht</div>
|
|
<div class="digest-item-source">Landesinstitut</div>
|
|
</div>
|
|
<span class="digest-item-importance" style="background: #eff6ff; color: #2563eb;">Pruefen</span>
|
|
</div>
|
|
<div class="digest-item">
|
|
<div>
|
|
<div class="digest-item-title">Jugend forscht Anmeldeschluss</div>
|
|
<div class="digest-item-source">Jugend forscht e.V.</div>
|
|
</div>
|
|
<span class="digest-item-importance" style="background: #fffbeb; color: #d97706;">Wichtig</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
function renderDigest(digest) {
|
|
const container = document.getElementById('digest-content');
|
|
container.innerHTML = digest.summary_html || '<p>Kein Inhalt verfuegbar.</p>';
|
|
}
|
|
|
|
async function exportDigestPDF() {
|
|
try {
|
|
const response = await fetch(`${GUIDED_API_BASE}/digests/latest/pdf`);
|
|
const blob = await response.blob();
|
|
const url = window.URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = 'wochenbericht.pdf';
|
|
a.click();
|
|
} catch (error) {
|
|
console.log('Demo mode: PDF export not available');
|
|
alert('PDF-Export wird in Kuerze verfuegbar sein.');
|
|
}
|
|
}
|
|
|
|
/* ==========================================
|
|
SETTINGS
|
|
========================================== */
|
|
|
|
function openGuidedSettings() {
|
|
// Zurueck zum Wizard um Einstellungen anzupassen
|
|
guidedState.wizardCompleted = false;
|
|
showWizard();
|
|
}
|
|
|
|
/* ==========================================
|
|
INITIALIZATION
|
|
========================================== */
|
|
|
|
// Add mode switcher to alerts header on load
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
const headerActions = document.querySelector('.alerts-header-actions');
|
|
if (headerActions) {
|
|
const modeSwitcher = document.createElement('div');
|
|
modeSwitcher.className = 'alerts-mode-switcher';
|
|
modeSwitcher.innerHTML = `
|
|
<button class="mode-btn active" data-mode="guided" onclick="switchToGuidedMode()">Einfach</button>
|
|
<button class="mode-btn" data-mode="expert" onclick="switchToExpertMode()">Experte</button>
|
|
`;
|
|
headerActions.insertBefore(modeSwitcher, headerActions.firstChild);
|
|
}
|
|
|
|
// Default to guided mode for new users
|
|
setTimeout(() => {
|
|
if (document.getElementById('panel-alerts').classList.contains('active')) {
|
|
switchToGuidedMode();
|
|
}
|
|
}, 100);
|
|
});
|
|
|
|
// Override showAlertsPanel to check mode
|
|
const originalShowAlertsTab = window.showAlertsTab;
|
|
window.showAlertsTab = function(tab) {
|
|
if (guidedState.mode === 'guided') {
|
|
// Stay in guided mode
|
|
return;
|
|
}
|
|
if (originalShowAlertsTab) {
|
|
originalShowAlertsTab(tab);
|
|
}
|
|
};
|
|
"""
|