Sources lag außerhalb von <main class="app-content"> und hatte dadurch kein max-width/padding - ging auf volle Bildschirmbreite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
509 Zeilen
20 KiB
HTML
509 Zeilen
20 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>IntelSight Verwaltung</title>
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
</head>
|
|
<body>
|
|
<!-- Header -->
|
|
<header class="app-header">
|
|
<div class="logo">IntelSight <span>Verwaltung</span></div>
|
|
<div class="header-right">
|
|
<span class="header-user" id="headerUser"></span>
|
|
<button class="btn btn-secondary btn-small" id="logoutBtn">Abmelden</button>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="app-content">
|
|
<!-- Navigation -->
|
|
<nav class="nav-tabs">
|
|
<button class="nav-tab active" data-section="dashboard">Dashboard</button>
|
|
<button class="nav-tab" data-section="orgs">Organisationen</button>
|
|
<button class="nav-tab" data-section="licenses">Lizenzen</button>
|
|
<button class="nav-tab" data-section="sources">Quellen</button>
|
|
</nav>
|
|
|
|
<!-- Dashboard Section -->
|
|
<div class="section active" id="sec-dashboard">
|
|
<div class="stats-grid" id="statsGrid"></div>
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 16px;">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Bald ablaufende Lizenzen</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<ul class="expiring-list" id="expiringList">
|
|
<li class="text-muted" style="padding: 8px 0;">Laden...</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Letzte Aktivität</h2>
|
|
</div>
|
|
<div class="card-body" id="recentActivity">
|
|
<div class="text-muted">Laden...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Organizations Section -->
|
|
<div class="section" id="sec-orgs">
|
|
<!-- Org List -->
|
|
<div id="orgListView">
|
|
<div class="action-bar">
|
|
<input type="text" class="search-input" id="orgSearch" placeholder="Organisation suchen...">
|
|
<button class="btn btn-primary" id="newOrgBtn">+ Neue Organisation</button>
|
|
</div>
|
|
<div class="card">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Slug</th>
|
|
<th>Nutzer</th>
|
|
<th>Lizenz</th>
|
|
<th>Status</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="orgTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Org Detail -->
|
|
<div class="detail-panel" id="orgDetail">
|
|
<button class="detail-back" id="orgBackBtn">← Zurück</button>
|
|
<div id="orgDetailHeader"></div>
|
|
|
|
<div class="nav-tabs mt-16" id="orgDetailTabs">
|
|
<button class="nav-tab active" data-subtab="users">Nutzer</button>
|
|
<button class="nav-tab" data-subtab="org-licenses">Lizenzen</button>
|
|
<button class="nav-tab" data-subtab="settings">Einstellungen</button>
|
|
</div>
|
|
|
|
<!-- Users Sub-Tab -->
|
|
<div class="section active" id="sub-users">
|
|
<div class="action-bar">
|
|
<span class="text-secondary" id="userLimitInfo"></span>
|
|
<button class="btn btn-primary" id="newUserBtn">+ Nutzer anlegen</button>
|
|
</div>
|
|
<div class="card">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>E-Mail</th>
|
|
<th>Rolle</th>
|
|
<th>Status</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="userTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Licenses Sub-Tab -->
|
|
<div class="section" id="sub-org-licenses">
|
|
<div class="action-bar">
|
|
<span></span>
|
|
<button class="btn btn-primary" id="newLicenseBtn">+ Neue Lizenz</button>
|
|
</div>
|
|
<div class="card">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Typ</th>
|
|
<th>Max Nutzer</th>
|
|
<th>Gültig ab</th>
|
|
<th>Gültig bis</th>
|
|
<th>Status</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="licenseTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Settings Sub-Tab -->
|
|
<div class="section" id="sub-settings">
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<form id="orgEditForm">
|
|
<div class="form-group">
|
|
<label for="editOrgName">Name</label>
|
|
<input type="text" id="editOrgName">
|
|
</div>
|
|
<div class="form-group">
|
|
<label>Status</label>
|
|
<select id="editOrgActive">
|
|
<option value="true">Aktiv</option>
|
|
<option value="false">Deaktiviert</option>
|
|
</select>
|
|
</div>
|
|
<div style="display: flex; gap: 8px; margin-top: 16px;">
|
|
<button type="submit" class="btn btn-primary">Speichern</button>
|
|
<button type="button" class="btn btn-danger" id="deleteOrgBtn">Organisation löschen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Licenses Section (global overview) -->
|
|
<div class="section" id="sec-licenses">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Bald ablaufende Lizenzen</h2>
|
|
<select id="expiringDays" style="background: var(--bg-primary); border: 1px solid var(--border); border-radius: var(--radius); color: var(--text-primary); padding: 4px 8px; font-size: 13px;">
|
|
<option value="14">14 Tage</option>
|
|
<option value="30" selected>30 Tage</option>
|
|
<option value="90">90 Tage</option>
|
|
</select>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Organisation</th>
|
|
<th>Typ</th>
|
|
<th>Max Nutzer</th>
|
|
<th>Läuft ab</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="expiringTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Sources Section -->
|
|
<div class="section" id="sec-sources">
|
|
<div class="nav-tabs" id="sourceSubTabs">
|
|
<button class="nav-tab active" data-subtab="global-sources">Grundquellen</button>
|
|
<button class="nav-tab" data-subtab="tenant-sources">Kundenquellen</button>
|
|
<button class="nav-tab" data-subtab="source-health">Quellen-Health</button>
|
|
</div>
|
|
|
|
<!-- Grundquellen -->
|
|
<div class="section active" id="sub-global-sources">
|
|
<div class="action-bar">
|
|
<div style="display:flex;align-items:center;gap:12px;flex-wrap:wrap;">
|
|
<input type="text" class="search-input" id="globalSourceSearch" placeholder="Grundquelle suchen...">
|
|
<select class="filter-select" id="globalFilterType" onchange="filterGlobalSources()">
|
|
<option value="">Alle Typen</option>
|
|
<option value="rss_feed">RSS-Feed</option>
|
|
<option value="web_source">Webquelle</option>
|
|
</select>
|
|
<select class="filter-select" id="globalFilterCategory" onchange="filterGlobalSources()">
|
|
<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="boulevard">Boulevard</option>
|
|
<option value="sonstige">Sonstige</option>
|
|
</select>
|
|
<select class="filter-select" id="globalFilterStatus" onchange="filterGlobalSources()">
|
|
<option value="">Alle Status</option>
|
|
<option value="active">Aktiv</option>
|
|
<option value="inactive">Inaktiv</option>
|
|
</select>
|
|
<span class="text-secondary" id="globalSourceCount"></span>
|
|
</div>
|
|
<button class="btn btn-secondary" id="discoverSourceBtn">Erkennen</button>
|
|
<button class="btn btn-primary" id="newGlobalSourceBtn">+ Neue Grundquelle</button>
|
|
</div>
|
|
<div class="card">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th class="sortable" data-sort="name" onclick="sortGlobalSources('name')">Name <span class="sort-icon"></span></th>
|
|
<th>URL</th>
|
|
<th class="sortable" data-sort="domain" onclick="sortGlobalSources('domain')">Domain <span class="sort-icon"></span></th>
|
|
<th class="sortable" data-sort="source_type" onclick="sortGlobalSources('source_type')">Typ <span class="sort-icon"></span></th>
|
|
<th class="sortable" data-sort="category" onclick="sortGlobalSources('category')">Kategorie <span class="sort-icon"></span></th>
|
|
<th class="sortable" data-sort="article_count" onclick="sortGlobalSources('article_count')">Artikel <span class="sort-icon"></span></th>
|
|
<th class="sortable" data-sort="status" onclick="sortGlobalSources('status')">Status <span class="sort-icon"></span></th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="globalSourceTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Kundenquellen -->
|
|
<div class="section" id="sub-tenant-sources">
|
|
<div class="action-bar">
|
|
<div style="display:flex;align-items:center;gap:12px;">
|
|
<input type="text" class="search-input" id="tenantSourceSearch" placeholder="Kundenquelle suchen...">
|
|
<span class="text-secondary" id="tenantSourceCount"></span>
|
|
</div>
|
|
</div>
|
|
<div class="card">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Name</th>
|
|
<th>Domain</th>
|
|
<th>Typ</th>
|
|
<th>Kategorie</th>
|
|
<th>Organisation</th>
|
|
<th>Hinzugefügt von</th>
|
|
<th>Aktionen</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tenantSourceTable"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quellen-Health -->
|
|
<div class="section" id="sub-source-health">
|
|
<div class="action-bar">
|
|
<h2 style="font-size:16px;font-weight:600;">Quellen-Health & Vorschläge</h2>
|
|
<button class="btn btn-primary" id="runHealthCheckBtn" onclick="runHealthCheck()">Jetzt prüfen</button>
|
|
</div>
|
|
<div id="healthContent">
|
|
<div class="text-muted" style="padding:20px;">Tab auswählen um Health-Daten zu laden...</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<!-- Modal: New Organization -->
|
|
<div class="modal-overlay" id="modalNewOrg">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3>Neue Organisation</h3>
|
|
<button class="modal-close" onclick="closeModal('modalNewOrg')">×</button>
|
|
</div>
|
|
<form id="newOrgForm">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="newOrgName">Name</label>
|
|
<input type="text" id="newOrgName" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="newOrgSlug">Slug (URL-freundlich)</label>
|
|
<input type="text" id="newOrgSlug" required pattern="[a-z0-9-]+" placeholder="z.B. bundespolizei">
|
|
</div>
|
|
<div id="newOrgError" class="error-msg" style="display:none"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('modalNewOrg')">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Anlegen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: New User -->
|
|
<div class="modal-overlay" id="modalNewUser">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3>Neuen Nutzer anlegen</h3>
|
|
<button class="modal-close" onclick="closeModal('modalNewUser')">×</button>
|
|
</div>
|
|
<form id="newUserForm">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="newUserEmail">E-Mail</label>
|
|
<input type="email" id="newUserEmail" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="newUserRole">Rolle</label>
|
|
<select id="newUserRole">
|
|
<option value="member">Mitglied</option>
|
|
<option value="org_admin">Org-Admin</option>
|
|
</select>
|
|
</div>
|
|
<div id="newUserError" class="error-msg" style="display:none"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('modalNewUser')">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Anlegen & Einladung senden</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: New License -->
|
|
<div class="modal-overlay" id="modalNewLicense">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3>Neue Lizenz</h3>
|
|
<button class="modal-close" onclick="closeModal('modalNewLicense')">×</button>
|
|
</div>
|
|
<form id="newLicenseForm">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="newLicType">Lizenztyp</label>
|
|
<select id="newLicType">
|
|
<option value="trial">Trial</option>
|
|
<option value="annual">Jahreslizenz</option>
|
|
<option value="permanent">Permanent</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="newLicMaxUsers">Maximale Nutzer</label>
|
|
<input type="number" id="newLicMaxUsers" value="5" min="1" max="1000">
|
|
</div>
|
|
<div class="form-group" id="durationGroup">
|
|
<label for="newLicDuration">Laufzeit (Tage)</label>
|
|
<input type="number" id="newLicDuration" value="14" min="1" max="3650">
|
|
<div class="text-muted mt-8" style="font-size: 12px;">Trial: Standard 14 Tage, Jahreslizenz: Standard 365 Tage</div>
|
|
</div>
|
|
<div id="newLicError" class="error-msg" style="display:none"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('modalNewLicense')">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Lizenz erstellen</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<!-- Modal: Source (Create/Edit) -->
|
|
<div class="modal-overlay" id="modalSource">
|
|
<div class="modal">
|
|
<div class="modal-header">
|
|
<h3 id="sourceModalTitle">Neue Grundquelle</h3>
|
|
<button class="modal-close" onclick="closeModal('modalSource')">×</button>
|
|
</div>
|
|
<form id="sourceForm">
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="sourceName">Name</label>
|
|
<input type="text" id="sourceName" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sourceUrl">Feed-URL</label>
|
|
<input type="url" id="sourceUrl" placeholder="https://...">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sourceDomain">Domain</label>
|
|
<input type="text" id="sourceDomain" placeholder="z.B. tagesschau.de">
|
|
</div>
|
|
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;">
|
|
<div class="form-group">
|
|
<label for="sourceType">Typ</label>
|
|
<select id="sourceType">
|
|
<option value="rss_feed">RSS-Feed</option>
|
|
<option value="web_source">Webquelle</option>
|
|
<option value="excluded">Ausgeschlossen</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sourceCategory">Kategorie</label>
|
|
<select id="sourceCategory">
|
|
<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="boulevard">Boulevard</option>
|
|
<option value="sonstige" selected>Sonstige</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sourceStatus">Status</label>
|
|
<select id="sourceStatus">
|
|
<option value="active">Aktiv</option>
|
|
<option value="inactive">Inaktiv</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="sourceNotes">Notizen</label>
|
|
<input type="text" id="sourceNotes" placeholder="Optional">
|
|
</div>
|
|
<div id="sourceError" class="error-msg" style="display:none"></div>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="closeModal('modalSource')">Abbrechen</button>
|
|
<button type="submit" class="btn btn-primary">Speichern</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Discover Sources -->
|
|
<div class="modal-overlay" id="modalDiscover">
|
|
<div class="modal" style="max-width:600px;">
|
|
<div class="modal-header">
|
|
<h3>Quellen erkennen</h3>
|
|
<button class="modal-close" onclick="closeModal('modalDiscover')">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<div class="form-group">
|
|
<label for="discoverUrl">Website-URL</label>
|
|
<div style="display:flex;gap:8px;">
|
|
<input type="url" id="discoverUrl" placeholder="https://www.example.de" style="flex:1;" required>
|
|
<button class="btn btn-primary" id="discoverBtn" onclick="runDiscover()">Erkennen</button>
|
|
</div>
|
|
</div>
|
|
<div id="discoverStatus" style="display:none;padding:12px 0;color:var(--text-secondary);font-size:13px;"></div>
|
|
<div id="discoverResults" style="display:none;">
|
|
<div id="discoverExisting" style="display:none;margin-bottom:12px;"></div>
|
|
<div id="discoverFeeds"></div>
|
|
<div style="margin-top:12px;display:flex;justify-content:flex-end;">
|
|
<button class="btn btn-primary" id="addDiscoveredBtn" style="display:none;" onclick="addDiscoveredFeeds()">Ausgewählte hinzufügen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Modal: Confirm -->
|
|
<div class="modal-overlay" id="modalConfirm">
|
|
<div class="modal" style="max-width: 400px;">
|
|
<div class="modal-header">
|
|
<h3 id="confirmTitle">Bestätigung</h3>
|
|
<button class="modal-close" onclick="closeModal('modalConfirm')">×</button>
|
|
</div>
|
|
<div class="modal-body">
|
|
<p class="confirm-text" id="confirmText"></p>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn btn-secondary" onclick="closeModal('modalConfirm')">Abbrechen</button>
|
|
<button class="btn btn-danger" id="confirmOkBtn">Bestätigen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="/static/js/app.js"></script>
|
|
<script src="/static/js/sources.js"></script>
|
|
<script src="/static/js/source-health.js"></script>
|
|
</body>
|
|
</html>
|