Initial commit: AegisSight-Monitor (OSINT-Monitoringsystem)
Dieser Commit ist enthalten in:
541
src/static/dashboard.html
Normale Datei
541
src/static/dashboard.html
Normale Datei
@@ -0,0 +1,541 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script>(function(){var t=localStorage.getItem('osint_theme');if(t)document.documentElement.setAttribute('data-theme',t);try{var a=JSON.parse(localStorage.getItem('osint_a11y')||'{}');Object.keys(a).forEach(function(k){if(a[k])document.documentElement.setAttribute('data-a11y-'+k,'true');});}catch(e){}})()</script>
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon-16x16.png">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/static/apple-touch-icon.png">
|
||||
<link rel="shortcut icon" href="/static/favicon.ico">
|
||||
<title>AegisSight Monitor</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="/static/css/style.css?v=20260304b">
|
||||
</head>
|
||||
<body>
|
||||
<a href="#main-content" class="skip-link">Zum Hauptinhalt springen</a>
|
||||
<div class="dashboard">
|
||||
<!-- Header -->
|
||||
<header class="header">
|
||||
<div class="header-left">
|
||||
<div class="header-logo">Aegis<span>Sight</span> Monitor</div>
|
||||
<h1 class="sr-only">AegisSight Monitor Dashboard</h1>
|
||||
</div>
|
||||
<div class="header-right">
|
||||
<button class="btn btn-secondary btn-small theme-toggle-btn" id="theme-toggle" onclick="ThemeManager.toggle()" title="Theme wechseln" aria-label="Theme wechseln">☼</button>
|
||||
<span class="header-user" id="header-user"></span>
|
||||
<button class="btn btn-secondary btn-small" id="logout-btn">Abmelden</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<!-- Sidebar -->
|
||||
<nav class="sidebar" aria-label="Seitenleiste">
|
||||
<div class="sidebar-section">
|
||||
<button class="btn btn-primary btn-full btn-small" id="new-incident-btn">+ Neue Lage / Recherche</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-filter">
|
||||
<button class="sidebar-filter-btn active" data-filter="all" onclick="App.setSidebarFilter('all')" aria-pressed="true">Alle</button>
|
||||
<button class="sidebar-filter-btn" data-filter="mine" onclick="App.setSidebarFilter('mine')" aria-pressed="false">Eigene</button>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section">
|
||||
<h2 class="sidebar-section-title collapsible" onclick="App.toggleSidebarSection('active-incidents')" role="button" tabindex="0">
|
||||
<span class="sidebar-chevron" id="chevron-active-incidents">▾</span>
|
||||
Aktive Lagen
|
||||
<span class="sidebar-section-count" id="count-active-incidents"></span>
|
||||
</h2>
|
||||
<div id="active-incidents" aria-live="polite"></div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section">
|
||||
<h2 class="sidebar-section-title collapsible" onclick="App.toggleSidebarSection('active-research')" role="button" tabindex="0">
|
||||
<span class="sidebar-chevron" id="chevron-active-research">▾</span>
|
||||
Aktive Recherchen
|
||||
<span class="sidebar-section-count" id="count-active-research"></span>
|
||||
</h2>
|
||||
<div id="active-research" aria-live="polite"></div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-section">
|
||||
<h2 class="sidebar-section-title collapsible" onclick="App.toggleSidebarSection('archived-incidents')" role="button" tabindex="0">
|
||||
<span class="sidebar-chevron" id="chevron-archived-incidents">▾</span>
|
||||
Archiv
|
||||
<span class="sidebar-section-count" id="count-archived-incidents"></span>
|
||||
</h2>
|
||||
<div id="archived-incidents" aria-live="polite"></div>
|
||||
</div>
|
||||
<div class="sidebar-sources-link">
|
||||
<button class="btn btn-secondary btn-full btn-small" onclick="App.openSourceManagement()">Quellen verwalten</button>
|
||||
<button class="btn btn-secondary btn-full btn-small sidebar-feedback-btn" onclick="App.openFeedback()">Feedback senden</button>
|
||||
<div class="sidebar-stats-mini">
|
||||
<span id="stat-sources-count">0 Quellen</span> · <span id="stat-articles-count">0 Artikel</span>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!-- Main Content -->
|
||||
<main class="main-content" id="main-content">
|
||||
<div class="empty-state" id="empty-state">
|
||||
<div class="empty-state-icon">☉</div>
|
||||
<div class="empty-state-title">Kein Vorfall ausgewählt</div>
|
||||
<div class="empty-state-text">Erstelle eine neue Lage oder wähle einen bestehenden Vorfall aus der Seitenleiste.</div>
|
||||
</div>
|
||||
|
||||
<!-- Lagebild (hidden by default) -->
|
||||
<div id="incident-view" style="display:none;">
|
||||
<!-- Header Strip -->
|
||||
<div class="incident-header-strip" id="incident-header-strip">
|
||||
<div class="incident-header-row0">
|
||||
<span class="incident-type-label" id="incident-type-badge"></span>
|
||||
<span class="auto-refresh-indicator" id="meta-refresh-mode"></span>
|
||||
</div>
|
||||
<div class="incident-header-row1">
|
||||
<div class="incident-header-left">
|
||||
<h2 class="incident-header-title" id="incident-title"></h2>
|
||||
</div>
|
||||
<div class="incident-header-actions">
|
||||
<button class="btn btn-primary btn-small" id="refresh-btn">Aktualisieren</button>
|
||||
<button class="btn btn-secondary btn-small" id="edit-incident-btn">Bearbeiten</button>
|
||||
<div class="export-dropdown" id="export-dropdown">
|
||||
<button class="btn btn-secondary btn-small" onclick="App.toggleExportDropdown(event)">Exportieren ▾</button>
|
||||
<div class="export-dropdown-menu" id="export-dropdown-menu">
|
||||
<button class="export-dropdown-item" onclick="App.exportIncident('md','report')">Lagebericht (Markdown)</button>
|
||||
<button class="export-dropdown-item" onclick="App.exportIncident('json','report')">Lagebericht (JSON)</button>
|
||||
<hr class="export-dropdown-divider">
|
||||
<button class="export-dropdown-item" onclick="App.exportIncident('md','full')">Vollexport (Markdown)</button>
|
||||
<button class="export-dropdown-item" onclick="App.exportIncident('json','full')">Vollexport (JSON)</button>
|
||||
<hr class="export-dropdown-divider">
|
||||
<button class="export-dropdown-item" onclick="App.printIncident()">Drucken / PDF</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-small" id="archive-incident-btn">Archivieren</button>
|
||||
<button class="btn btn-danger btn-small" id="delete-incident-btn">Löschen</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="incident-header-row2">
|
||||
<div class="incident-header-row2-left">
|
||||
<span class="incident-creator-badge">von <strong id="incident-creator"></strong></span>
|
||||
<span class="intl-badge" id="intl-badge"></span>
|
||||
<span id="incident-description" class="incident-description-text"></span>
|
||||
</div>
|
||||
<div class="incident-header-row2-right">
|
||||
<div class="summary-meta" id="summary-meta">
|
||||
<span id="meta-updated" class="meta-updated-link" role="button" tabindex="0" onclick="App.toggleRefreshHistory()" onkeydown="if(event.key==='Enter')App.toggleRefreshHistory()"></span>
|
||||
</div>
|
||||
<div class="refresh-history-popover" id="refresh-history-popover" style="display:none;">
|
||||
<div class="refresh-history-header">
|
||||
<span class="refresh-history-title">Refresh-Verlauf</span>
|
||||
<button class="refresh-history-close" onclick="App.closeRefreshHistory()">×</button>
|
||||
</div>
|
||||
<div class="refresh-history-list" id="refresh-history-list">
|
||||
<div style="padding:12px;color:var(--text-disabled);font-size:12px;">Lade...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Fortschrittsanzeige -->
|
||||
<div class="progress-bar" id="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100" aria-label="Verarbeitungsfortschritt" style="display:none;">
|
||||
<div class="progress-steps">
|
||||
<div class="progress-step" id="step-researching">
|
||||
<div class="progress-step-dot"></div>
|
||||
<span>Recherche</span>
|
||||
</div>
|
||||
<div class="progress-step" id="step-analyzing">
|
||||
<div class="progress-step-dot"></div>
|
||||
<span>Analyse</span>
|
||||
</div>
|
||||
<div class="progress-step" id="step-factchecking">
|
||||
<div class="progress-step-dot"></div>
|
||||
<span>Faktencheck</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="progress-track">
|
||||
<div class="progress-fill" id="progress-fill"></div>
|
||||
</div>
|
||||
<div class="progress-label-container">
|
||||
<span id="progress-label" class="progress-label">Warte auf Start...</span>
|
||||
<span id="progress-timer" class="progress-timer"></span>
|
||||
</div>
|
||||
<button id="progress-cancel-btn" class="progress-cancel-btn" onclick="App.cancelRefresh()">Abbrechen</button>
|
||||
</div>
|
||||
|
||||
<!-- Layout-Toolbar -->
|
||||
<div class="layout-toolbar" id="layout-toolbar" style="display:none;">
|
||||
<div class="layout-toggles">
|
||||
<button class="layout-toggle-btn active" data-tile="lagebild" onclick="LayoutManager.toggleTile('lagebild')" aria-pressed="true">Lagebild</button>
|
||||
<button class="layout-toggle-btn active" data-tile="faktencheck" onclick="LayoutManager.toggleTile('faktencheck')" aria-pressed="true">Faktencheck</button>
|
||||
<button class="layout-toggle-btn active" data-tile="quellen" onclick="LayoutManager.toggleTile('quellen')" aria-pressed="true">Quellen</button>
|
||||
<button class="layout-toggle-btn active" data-tile="timeline" onclick="LayoutManager.toggleTile('timeline')" aria-pressed="true">Timeline</button>
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-small" onclick="LayoutManager.reset()">Layout zurücksetzen</button>
|
||||
</div>
|
||||
|
||||
<!-- gridstack Dashboard-Grid -->
|
||||
<div class="grid-stack">
|
||||
<div class="grid-stack-item" gs-id="lagebild" gs-x="0" gs-y="0" gs-w="6" gs-h="4" gs-min-w="4" gs-min-h="4">
|
||||
<div class="grid-stack-item-content">
|
||||
<div class="card incident-analysis-summary">
|
||||
<div class="card-header">
|
||||
<div class="card-title clickable" onclick="openContentModal('Lagebild', 'summary-content')">Lagebild</div>
|
||||
<span class="lagebild-timestamp" id="lagebild-timestamp"></span>
|
||||
</div>
|
||||
<div id="summary-content">
|
||||
<div id="summary-text" class="summary-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-stack-item" gs-id="faktencheck" gs-x="6" gs-y="0" gs-w="6" gs-h="4" gs-min-w="4" gs-min-h="4">
|
||||
<div class="grid-stack-item-content">
|
||||
<div class="card incident-analysis-factcheck" id="factcheck-card">
|
||||
<div class="card-header">
|
||||
<div class="card-title clickable" onclick="openContentModal('Faktencheck', 'factcheck-list')">Faktencheck</div>
|
||||
<div class="fc-filter-bar" id="fc-filters"></div>
|
||||
</div>
|
||||
<div class="factcheck-list" id="factcheck-list">
|
||||
<div class="empty-state" style="padding:20px;">
|
||||
<div class="empty-state-text">Noch keine Fakten geprüft</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-stack-item" gs-id="quellen" gs-x="0" gs-y="4" gs-w="12" gs-h="2" gs-min-w="6" gs-min-h="2">
|
||||
<div class="grid-stack-item-content">
|
||||
<div class="card source-overview-card">
|
||||
<div class="card-header source-overview-header-toggle" onclick="App.toggleSourceOverview()" role="button" tabindex="0" aria-expanded="false">
|
||||
<span class="source-overview-chevron" id="source-overview-chevron" title="Aufklappen" aria-hidden="true">▸</span>
|
||||
<div class="card-title clickable">Quellenübersicht</div>
|
||||
<button class="btn btn-secondary btn-small source-detail-btn" onclick="event.stopPropagation(); openContentModal('Quellenübersicht', 'source-overview-content')">Detailansicht</button>
|
||||
</div>
|
||||
<div id="source-overview-content" style="display:none;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-stack-item" gs-id="timeline" gs-x="0" gs-y="5" gs-w="12" gs-h="4" gs-min-w="6" gs-min-h="4">
|
||||
<div class="grid-stack-item-content">
|
||||
<div class="card timeline-card">
|
||||
<div class="card-header">
|
||||
<div class="card-title clickable" onclick="openContentModal('Ereignis-Timeline', 'timeline')">Ereignis-Timeline</div>
|
||||
<div class="ht-controls">
|
||||
<div class="ht-filter-group">
|
||||
<button class="ht-filter-btn active" data-filter="all" onclick="App.setTimelineFilter('all')" aria-pressed="true">Alle</button>
|
||||
<button class="ht-filter-btn" data-filter="articles" onclick="App.setTimelineFilter('articles')" aria-pressed="false">Meldungen</button>
|
||||
<button class="ht-filter-btn" data-filter="snapshots" onclick="App.setTimelineFilter('snapshots')" aria-pressed="false">Lageberichte</button>
|
||||
</div>
|
||||
<span class="ht-count" id="article-count"></span>
|
||||
<div class="ht-range-group">
|
||||
<button class="ht-range-btn" data-range="24h" onclick="App.setTimelineRange('24h')" aria-pressed="false">24h</button>
|
||||
<button class="ht-range-btn" data-range="7d" onclick="App.setTimelineRange('7d')" aria-pressed="false">7T</button>
|
||||
<button class="ht-range-btn active" data-range="all" onclick="App.setTimelineRange('all')" aria-pressed="true">Alles</button>
|
||||
</div>
|
||||
<label for="timeline-search" class="sr-only">Timeline durchsuchen</label>
|
||||
<input type="text" id="timeline-search" class="timeline-filter-input" placeholder="Suche..." oninput="App.debouncedRerenderTimeline()">
|
||||
</div>
|
||||
</div>
|
||||
<div id="timeline" class="ht-timeline-container">
|
||||
<div class="ht-empty">Noch keine Meldungen</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Parkplatz für ausgeblendete Kacheln -->
|
||||
<div id="tile-parking" style="display:none;"></div>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Neue Lage -->
|
||||
<div class="modal-overlay" id="modal-new" role="dialog" aria-modal="true" aria-labelledby="modal-new-title">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title" id="modal-new-title">Neue Lage anlegen</div>
|
||||
<button class="modal-close" onclick="closeModal('modal-new')" aria-label="Schließen">×</button>
|
||||
</div>
|
||||
<form id="new-incident-form">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="inc-title">Titel des Vorfalls</label>
|
||||
<input type="text" id="inc-title" required placeholder="z.B. Explosion in Madrid">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inc-description">Beschreibung / Kontext</label>
|
||||
<textarea id="inc-description" placeholder="Weitere Details zum Vorfall (optional)"></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inc-type">Art der Lage</label>
|
||||
<select id="inc-type" onchange="toggleTypeDefaults()">
|
||||
<option value="adhoc">Ad-hoc Lage (Breaking News)</option>
|
||||
<option value="research">Recherche (Hintergrund)</option>
|
||||
</select>
|
||||
<div class="form-hint" id="type-hint">
|
||||
RSS-Feeds + WebSearch, automatische Aktualisierung empfohlen
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Quellen</label>
|
||||
<div class="toggle-group">
|
||||
<label class="toggle-label">
|
||||
<input type="checkbox" id="inc-international" checked>
|
||||
<span class="toggle-switch"></span>
|
||||
<span class="toggle-text">Internationale Quellen einbeziehen</span>
|
||||
</label>
|
||||
<div class="form-hint" id="sources-hint">DE + internationale Feeds (Reuters, BBC, Al Jazeera etc.)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Sichtbarkeit</label>
|
||||
<div class="toggle-group">
|
||||
<label class="toggle-label">
|
||||
<input type="checkbox" id="inc-visibility" checked>
|
||||
<span class="toggle-switch"></span>
|
||||
<span class="toggle-text" id="visibility-text">Öffentlich — für alle Nutzer sichtbar</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inc-refresh-mode">Aktualisierung</label>
|
||||
<select id="inc-refresh-mode" onchange="toggleRefreshInterval()">
|
||||
<option value="manual">Manuell</option>
|
||||
<option value="auto">Automatisch</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group conditional-field" id="refresh-interval-field">
|
||||
<label for="inc-refresh-value">Intervall</label>
|
||||
<div class="interval-input-group">
|
||||
<input type="number" id="inc-refresh-value" min="10" value="15">
|
||||
<select id="inc-refresh-unit" onchange="updateIntervalMin()">
|
||||
<option value="1" selected>Minuten</option>
|
||||
<option value="60">Stunden</option>
|
||||
<option value="1440">Tage</option>
|
||||
<option value="10080">Wochen</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="inc-retention">Aufbewahrung (Tage)</label>
|
||||
<input type="number" id="inc-retention" min="0" max="999" value="30" placeholder="0 = Unbegrenzt">
|
||||
<div class="form-hint">0 = Unbegrenzt, max. 999 Tage</div>
|
||||
</div>
|
||||
<div class="form-group" style="margin-top: 8px;">
|
||||
<label>E-Mail-Benachrichtigungen</label>
|
||||
<div class="form-hint" style="margin-bottom: 8px;">Per E-Mail benachrichtigen bei:</div>
|
||||
<div class="toggle-group">
|
||||
<label class="toggle-label">
|
||||
<input type="checkbox" id="inc-notify-summary">
|
||||
<span class="toggle-switch"></span>
|
||||
<span class="toggle-text">Neues Lagebild</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="toggle-group" style="margin-top: 8px;">
|
||||
<label class="toggle-label">
|
||||
<input type="checkbox" id="inc-notify-new-articles">
|
||||
<span class="toggle-switch"></span>
|
||||
<span class="toggle-text">Neue Artikel</span>
|
||||
</label>
|
||||
</div>
|
||||
<div class="toggle-group" style="margin-top: 8px;">
|
||||
<label class="toggle-label">
|
||||
<input type="checkbox" id="inc-notify-status-change">
|
||||
<span class="toggle-switch"></span>
|
||||
<span class="toggle-text">Statusänderung Faktencheck</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-new')">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary" id="modal-new-submit">Lage anlegen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Quellenverwaltung -->
|
||||
<div class="modal-overlay" id="modal-sources" role="dialog" aria-modal="true" aria-labelledby="modal-sources-title">
|
||||
<div class="modal modal-wide">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title" id="modal-sources-title">Quellenverwaltung</div>
|
||||
<button class="modal-close" onclick="closeModal('modal-sources')" aria-label="Schließen">×</button>
|
||||
</div>
|
||||
<div class="modal-body sources-modal-body">
|
||||
<!-- Stats-Leiste -->
|
||||
<div class="sources-stats-bar" id="sources-stats-bar"></div>
|
||||
|
||||
<!-- Toolbar -->
|
||||
<div class="sources-toolbar">
|
||||
<div class="sources-filters">
|
||||
<label for="sources-filter-type" class="sr-only">Quellentyp filtern</label>
|
||||
<select id="sources-filter-type" class="timeline-filter-select" onchange="App.filterSources()">
|
||||
<option value="">Alle Typen</option>
|
||||
<option value="rss_feed">RSS-Feed</option>
|
||||
<option value="web_source">Web-Quelle</option>
|
||||
<option value="excluded">Gesperrt</option>
|
||||
</select>
|
||||
<label for="sources-filter-category" class="sr-only">Kategorie filtern</label>
|
||||
<select id="sources-filter-category" class="timeline-filter-select" onchange="App.filterSources()">
|
||||
<option value="">Alle Kategorien</option>
|
||||
<option value="nachrichtenagentur">Nachrichtenagentur</option>
|
||||
<option value="oeffentlich-rechtlich">Öffentlich-Rechtlich</option>
|
||||
<option value="qualitaetszeitung">Qualitätszeitung</option>
|
||||
<option value="behoerde">Behörde</option>
|
||||
<option value="fachmedien">Fachmedien</option>
|
||||
<option value="think-tank">Think Tank</option>
|
||||
<option value="international">International</option>
|
||||
<option value="regional">Regional</option>
|
||||
<option value="sonstige">Sonstige</option>
|
||||
</select>
|
||||
<label for="sources-search" class="sr-only">Quellen durchsuchen</label>
|
||||
<input type="text" id="sources-search" class="timeline-filter-input sources-search-input" placeholder="Suche..." oninput="App.filterSources()">
|
||||
</div>
|
||||
<div class="sources-toolbar-actions">
|
||||
<button class="btn btn-secondary btn-small source-block-btn" onclick="App.showBlockDomainDialog()">Domain sperren</button>
|
||||
<button class="btn btn-primary btn-small" onclick="App.toggleSourceForm()">+ Quelle</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inline-Formular: Domain sperren (ein-/ausklappbar) -->
|
||||
<div class="sources-add-form" id="sources-block-form" style="display:none;">
|
||||
<div class="sources-form-row">
|
||||
<div class="form-group flex-1">
|
||||
<label for="block-domain-input">Domain</label>
|
||||
<input type="text" id="block-domain-input" placeholder="z.B. bild.de">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="block-domain-notes">Notizen</label>
|
||||
<input type="text" id="block-domain-notes" class="source-notes-input" placeholder="Optional">
|
||||
</div>
|
||||
<button class="btn btn-danger btn-small" onclick="App.blockDomain()">Sperren</button>
|
||||
<button class="btn btn-secondary btn-small" onclick="App.showBlockDomainDialog(false)">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Inline-Formular: Quelle hinzufügen (ein-/ausklappbar) -->
|
||||
<div class="sources-add-form" id="sources-add-form" style="display:none;">
|
||||
<div class="sources-form-row">
|
||||
<div class="form-group flex-1">
|
||||
<label for="src-discover-url">URL oder Domain</label>
|
||||
<input type="text" id="src-discover-url" placeholder="z.B. netzpolitik.org">
|
||||
</div>
|
||||
<button class="btn btn-secondary btn-small" id="src-discover-btn" onclick="App.discoverSource()">Erkennen</button>
|
||||
</div>
|
||||
|
||||
<!-- Ergebnis-Anzeige (nach Discovery) -->
|
||||
<div id="src-discovery-result" class="sources-discovery-result" style="display:none;">
|
||||
<div class="sources-add-form-grid">
|
||||
<div class="form-group">
|
||||
<label for="src-name">Name</label>
|
||||
<input type="text" id="src-name" placeholder="Wird erkannt...">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="src-category">Kategorie</label>
|
||||
<select id="src-category">
|
||||
<option value="nachrichtenagentur">Nachrichtenagentur</option>
|
||||
<option value="oeffentlich-rechtlich">Öffentlich-Rechtlich</option>
|
||||
<option value="qualitaetszeitung">Qualitätszeitung</option>
|
||||
<option value="behoerde">Behörde</option>
|
||||
<option value="fachmedien">Fachmedien</option>
|
||||
<option value="think-tank">Think Tank</option>
|
||||
<option value="international">International</option>
|
||||
<option value="regional">Regional</option>
|
||||
<option value="sonstige" selected>Sonstige</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Typ</label>
|
||||
<input type="text" id="src-type-display" class="input-readonly" readonly>
|
||||
</div>
|
||||
<div class="form-group" id="src-rss-url-group">
|
||||
<label>RSS-Feed URL</label>
|
||||
<input type="text" id="src-rss-url" class="input-readonly" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Domain</label>
|
||||
<input type="text" id="src-domain" class="input-readonly" readonly>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="src-notes">Notizen</label>
|
||||
<input type="text" id="src-notes" placeholder="Optional">
|
||||
</div>
|
||||
</div>
|
||||
<div class="sources-discovery-actions">
|
||||
<button class="btn btn-primary btn-small" onclick="App.saveSource()">Speichern</button>
|
||||
<button class="btn btn-secondary btn-small" onclick="App.toggleSourceForm(false)">Abbrechen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quellen-Liste (gruppiert) -->
|
||||
<div class="sources-list" id="sources-list">
|
||||
<div class="empty-state-text" style="padding:var(--sp-3xl);text-align:center;">Lade Quellen...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Content-Viewer (wiederverwendbar für Lagebild, Faktencheck, Quellenübersicht, Timeline) -->
|
||||
<div class="modal-overlay" id="modal-content-viewer" role="dialog" aria-modal="true" aria-labelledby="content-viewer-title">
|
||||
<div class="modal modal-content-viewer">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title" id="content-viewer-title"></div>
|
||||
<div class="modal-header-extra" id="content-viewer-header-extra"></div>
|
||||
<button class="modal-close" onclick="closeModal('modal-content-viewer')" aria-label="Schließen">×</button>
|
||||
</div>
|
||||
<div class="modal-body" id="content-viewer-body"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal: Feedback -->
|
||||
<div class="modal-overlay" id="modal-feedback" role="dialog" aria-modal="true" aria-labelledby="modal-feedback-title">
|
||||
<div class="modal">
|
||||
<div class="modal-header">
|
||||
<div class="modal-title" id="modal-feedback-title">Feedback senden</div>
|
||||
<button class="modal-close" onclick="closeModal('modal-feedback')" aria-label="Schliessen">×</button>
|
||||
</div>
|
||||
<form id="feedback-form">
|
||||
<div class="modal-body">
|
||||
<div class="form-group">
|
||||
<label for="fb-category">Kategorie</label>
|
||||
<select id="fb-category">
|
||||
<option value="bug">Fehlerbericht</option>
|
||||
<option value="feature">Feature-Wunsch</option>
|
||||
<option value="question">Frage</option>
|
||||
<option value="other">Sonstiges</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="fb-message">Nachricht</label>
|
||||
<textarea id="fb-message" required minlength="10" maxlength="5000" rows="6" placeholder="Beschreibe dein Anliegen (mind. 10 Zeichen)..."></textarea>
|
||||
<div class="form-hint"><span id="fb-char-count">0</span> / 5.000 Zeichen</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeModal('modal-feedback')">Abbrechen</button>
|
||||
<button type="submit" class="btn btn-primary" id="fb-submit-btn">Absenden</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Toast Container -->
|
||||
<div class="toast-container" id="toast-container" aria-live="polite" aria-atomic="true"></div>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/gridstack@12/dist/gridstack-all.js"></script>
|
||||
<script src="/static/js/api.js?v=20260304a"></script>
|
||||
<script src="/static/js/ws.js?v=20260304a"></script>
|
||||
<script src="/static/js/components.js?v=20260304a"></script>
|
||||
<script src="/static/js/layout.js?v=20260304a"></script>
|
||||
<script src="/static/js/app.js?v=20260304a"></script>
|
||||
</body>
|
||||
</html>
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren