Files
PostTracker/web/index.html
2025-12-21 14:21:55 +01:00

1508 lines
74 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post Tracker</title>
<link rel="icon" type="image/png" sizes="32x32" href="assets/app-icon-64.png">
<link rel="icon" type="image/png" sizes="192x192" href="assets/app-icon-192.png">
<link rel="apple-touch-icon" href="assets/app-icon-192.png">
<style>
body.auth-pending {
visibility: hidden;
overflow: hidden;
}
</style>
<script>
(function gateAssets() {
const API_URL = 'https://fb.srv.medeba-media.de/api';
const LOGIN_PAGE = 'login.html';
const cssFiles = [
{ href: 'style.css' },
{ href: 'dashboard.css' },
{ href: 'settings.css' },
{ href: 'automation.css' },
{ href: 'daily-bookmarks.css', id: 'dailyBookmarksCss', disabled: true }
];
const jsFiles = [
'app.js',
'dashboard.js',
'settings.js',
'vendor/list.min.js',
'automation.js',
'daily-bookmarks.js'
];
function redirectToLogin() {
try {
const redirect = encodeURIComponent(window.location.href);
window.location.replace(`${LOGIN_PAGE}?redirect=${redirect}`);
} catch (_error) {
window.location.replace(LOGIN_PAGE);
}
}
function loadStyles() {
cssFiles.forEach(({ href, id, disabled }) => {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = href;
if (id) link.id = id;
if (disabled) link.disabled = true;
document.head.appendChild(link);
});
}
function loadScriptsSequentially(list, index = 0) {
return new Promise((resolve, reject) => {
if (index >= list.length) {
resolve();
return;
}
const script = document.createElement('script');
script.src = list[index];
script.onload = () => loadScriptsSequentially(list, index + 1).then(resolve).catch(reject);
script.onerror = reject;
document.body.appendChild(script);
});
}
async function bootstrap() {
try {
const res = await fetch(`${API_URL}/session`, { credentials: 'include' });
if (res.status === 401) {
redirectToLogin();
return;
}
loadStyles();
await loadScriptsSequentially(jsFiles);
} catch (_error) {
redirectToLogin();
}
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
})();
</script>
</head>
<body class="auth-pending">
<div class="shell">
<header class="site-header site-header--global">
<div class="container">
<div class="header-main">
<div>
<h1>📋 Post Tracker</h1>
</div>
<div class="site-nav">
<a class="site-nav__btn" data-view-target="posts" href="posts.html">📝 Beiträge</a>
<a class="site-nav__btn" data-view-target="dashboard" href="dashboard.html">📊 Dashboard</a>
<a class="site-nav__btn" data-view-target="settings" href="settings.html">⚙️ Einstellungen</a>
<a class="site-nav__btn" data-view-target="bookmarks" href="bookmarks.html">🔖 Bookmarks</a>
<a class="site-nav__btn" data-view-target="automation" href="index.html?view=automation">⚡️ Automationen</a>
<a class="site-nav__btn" data-view-target="daily-bookmarks" href="index.html?view=daily-bookmarks">✅ Daily Bookmarks</a>
<button class="site-nav__btn site-nav__btn--icon" id="logoutBtn" type="button" aria-label="Abmelden">🚪</button>
</div>
</div>
</div>
</header>
<main class="shell-main">
<section id="view-posts" class="app-view app-view--active" data-view="posts">
<div class="container">
<div class="page-toolbar">
<div class="page-toolbar__title">
<h2>Beiträge</h2>
<button type="button" class="btn btn-primary" id="openManualPostModalBtn">Beitrag hinzufügen</button>
</div>
<div class="header-controls">
<div class="control-group">
<label for="profileSelect">Dein Profil:</label>
<select id="profileSelect" class="control-select">
<option value="1">Profil 1</option>
<option value="2">Profil 2</option>
<option value="3">Profil 3</option>
<option value="4">Profil 4</option>
<option value="5">Profil 5</option>
</select>
</div>
<div class="control-group">
<label class="switch">
<input type="checkbox" id="autoRefreshToggle" checked>
<span>Auto-Refresh</span>
</label>
<select id="autoRefreshInterval" class="control-select">
<option value="15000">15 s</option>
<option value="30000">30 s</option>
<option value="60000">1 min</option>
<option value="120000">2 min</option>
<option value="300000">5 min</option>
</select>
<button type="button" id="manualRefreshBtn" class="refresh-btn" aria-label="Aktualisieren" title="Aktualisieren">🔄</button>
</div>
<div class="control-group">
<label for="sortMode">Sortierung:</label>
<div class="sort-controls">
<select id="sortMode" class="control-select">
<option value="created">Erstelldatum</option>
<option value="deadline">Deadline</option>
<option value="lastCheck">Letzte Teilnahme</option>
<option value="lastChange">Letzte Änderung</option>
<option value="smart">Smart (Dringlichkeit)</option>
</select>
<button type="button" id="sortDirectionToggle" class="sort-direction-toggle" aria-label="Absteigend" aria-pressed="false" title="Absteigend">
<span class="sort-direction-toggle__icon" aria-hidden="true"></span>
</button>
</div>
</div>
</div>
</div>
<div class="tabs-section">
<div class="tabs">
<button class="tab-btn" data-tab="all">Alle Beiträge</button>
<button class="tab-btn active" data-tab="pending">Offene Beiträge</button>
</div>
<div class="merge-controls" id="mergeControls" hidden>
<div class="merge-actions">
<button type="button" class="btn btn-secondary" id="mergeModeToggle">Merge-Modus</button>
<button type="button" class="btn btn-primary" id="mergeSubmitBtn" disabled>Beiträge mergen</button>
</div>
</div>
<div class="search-container">
<label class="search-filter-toggle" for="includeExpiredToggle">
<input type="checkbox" id="includeExpiredToggle">
<span>Abgelaufene/abgeschlossene anzeigen</span>
</label>
<input type="text" id="searchInput" class="search-input" placeholder="Beiträge durchsuchen...">
</div>
</div>
<div id="loading" class="loading">Lade Beiträge...</div>
<div id="error" class="error" style="display: none;"></div>
<div id="postsContainer" class="posts-container"></div>
</div>
<div id="screenshotModal" class="screenshot-modal" hidden>
<div id="screenshotModalBackdrop" class="screenshot-modal__backdrop" aria-hidden="true"></div>
<div id="screenshotModalContent" class="screenshot-modal__content" role="dialog" aria-modal="true">
<button type="button" id="screenshotModalClose" class="screenshot-modal__close" aria-label="Schließen">×</button>
<img id="screenshotModalImage" alt="Screenshot zum Beitrag" />
</div>
</div>
<div id="manualPostModal" class="modal" hidden>
<div id="manualPostModalBackdrop" class="modal__backdrop" aria-hidden="true"></div>
<div id="manualPostModalContent" class="modal__content" role="dialog" aria-modal="true" aria-labelledby="manualPostModalTitle" tabindex="-1">
<button type="button" id="manualPostModalClose" class="modal__close" aria-label="Schließen">×</button>
<h2 id="manualPostModalTitle">Beitrag hinzufügen</h2>
<form id="manualPostForm" novalidate>
<div class="form-grid">
<label class="form-field">
<span>Direktlink *</span>
<input type="url" id="manualPostUrl" placeholder="https://www.facebook.com/..." required>
</label>
<label class="form-field">
<span>Titel</span>
<input type="text" id="manualPostTitle" placeholder="Kurzbeschreibung" maxlength="200">
</label>
<label class="form-field">
<span>Benötigte Profile *</span>
<select id="manualPostTarget" required>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</label>
<label class="form-field">
<span>Erstellt von (Facebook-Name)</span>
<input type="text" id="manualPostCreatorName" placeholder="z.B. Max Mustermann">
</label>
<label class="form-field">
<span>Deadline</span>
<input type="datetime-local" id="manualPostDeadline">
</label>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary" id="manualPostSubmitBtn">Speichern</button>
<button type="button" class="btn btn-secondary" id="manualPostReset">Zurücksetzen</button>
</div>
<div id="manualPostMessage" class="form-message" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</section>
<section id="view-automation" class="app-view automation-view" data-view="automation">
<div class="container">
<div class="auto-shell">
<header class="auto-hero">
<div class="hero-head">
<div class="hero-text">
<h1>Request Automationen</h1>
</div>
<div class="hero-actions">
<button class="ghost-btn" id="openImportBtn" type="button">📥 Vorlage importieren</button>
<button class="primary-btn" id="newAutomationBtn" type="button">+ Neue Automation</button>
</div>
</div>
<div class="hero-stats" id="heroStats"></div>
</header>
<section class="panel list-panel">
<div class="panel-header">
<div>
<p class="panel-eyebrow">Geplante Requests</p>
<h2>Automationen</h2>
</div>
<div class="panel-actions">
<input id="tableFilterInput" class="filter-input" type="search" placeholder="Filtern nach Name/Typ…" />
<button class="ghost-btn" id="refreshBtn" type="button">Aktualisieren</button>
</div>
</div>
<div id="listStatus" class="list-status" aria-live="polite"></div>
<div class="table-wrap" id="automationTable">
<table class="auto-table">
<thead>
<tr>
<th data-sort-column="name">Name<span class="sort-indicator"></span></th>
<th data-sort-column="next">Nächster Lauf<span class="sort-indicator"></span></th>
<th data-sort-column="runUntil">Läuft bis<span class="sort-indicator"></span></th>
<th data-sort-column="last">Letzter Lauf<span class="sort-indicator"></span></th>
<th data-sort-column="status">Status<span class="sort-indicator"></span></th>
<th data-sort-column="runs">#Läufe<span class="sort-indicator"></span></th>
<th>Aktionen</th>
</tr>
<tr class="table-filter-row">
<th><input id="filterName" type="search" placeholder="Name/Typ/E-Mail/URL"></th>
<th><input id="filterNext" type="search" placeholder="z.B. heute"></th>
<th><input id="filterRunUntil" type="search" placeholder="z.B. morgen"></th>
<th><input id="filterLast" type="search" placeholder="z.B. HTTP 200"></th>
<th>
<select id="filterStatus">
<option value="">Alle</option>
<option value="success">OK</option>
<option value="error">Fehler</option>
</select>
</th>
<th><input id="filterRuns" type="number" min="0" placeholder="≥"></th>
<th></th>
</tr>
</thead>
<tbody id="requestTableBody" class="list"></tbody>
</table>
</div>
</section>
<section class="panel runs-panel">
<div class="panel-header">
<div>
<p class="panel-eyebrow">Verlauf</p>
<h2>Run-Historie</h2>
</div>
<p class="runs-hint">Letzte Läufe der ausgewählten Automation.</p>
</div>
<div id="runsStatus" class="runs-status" aria-live="polite"></div>
<ul id="runsList" class="runs-list"></ul>
</section>
</div>
</div>
<div id="formModal" class="modal" hidden>
<div class="modal__backdrop" id="formModalBackdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<div class="modal__header">
<div>
<p class="panel-eyebrow" id="formModeLabel">Neue Automation</p>
<h2 id="modalTitle">Request & Zeitplan</h2>
</div>
<button class="ghost-btn" id="modalCloseBtn" type="button">×</button>
</div>
<form id="automationForm" class="form-grid" novalidate>
<div class="field">
<label for="typeSelect">Typ</label>
<select id="typeSelect">
<option value="request">HTTP Request</option>
<option value="email">E-Mail</option>
<option value="flow">Flow (bis 3 Schritte)</option>
</select>
</div>
<div class="field">
<label for="nameInput">Name *</label>
<input id="nameInput" type="text" placeholder="API Ping (stündlich)" required maxlength="160">
</div>
<div class="field">
<label for="descriptionInput">Notizen</label>
<textarea id="descriptionInput" rows="2" placeholder="Kurzbeschreibung oder Zweck"></textarea>
</div>
<div class="field" data-section="http">
<label for="urlInput">URL-Template *</label>
<input id="urlInput" type="url" placeholder="https://api.example.com/{{date}}/trigger" required>
</div>
<div class="field inline" data-section="http">
<label for="methodSelect">Methode</label>
<select id="methodSelect">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="field inline">
<label for="activeToggle">Aktiv</label>
<label class="switch">
<input type="checkbox" id="activeToggle" checked>
<span class="switch-slider"></span>
<span class="switch-label">Plan aktiv</span>
</label>
</div>
<div class="field" data-section="http">
<label for="headersInput">Headers (Key: Value pro Zeile oder JSON)</label>
<textarea id="headersInput" rows="3" placeholder="Authorization: Bearer {{token}}\nContent-Type: application/json"></textarea>
</div>
<div class="field" data-section="http">
<label for="bodyInput">Body (optional, Templates möglich)</label>
<textarea id="bodyInput" rows="5" placeholder='{"date":"{{date}}","id":"{{uuid}}"}'></textarea>
</div>
<div class="field full" data-section="email">
<label for="emailToInput">E-Mail Empfänger *</label>
<input id="emailToInput" type="text" placeholder="max@example.com, lisa@example.com">
</div>
<div class="field full" data-section="email">
<label for="emailSubjectInput">Betreff *</label>
<input id="emailSubjectInput" type="text" placeholder="Status Update {{date}}">
</div>
<div class="field full" data-section="email">
<label for="emailBodyInput">Body *</label>
<textarea id="emailBodyInput" rows="6" placeholder="Hallo,\nheutiger Status: {{uuid}}"></textarea>
</div>
<div class="field full" data-section="flow">
<div class="template-hint">
<p class="template-title">Flow Schritte</p>
<p class="template-copy">Max. 3 Schritte, Kontext steht als {{step1_json}}, {{step1_text}}, {{step1_status_code}} usw. im nächsten Schritt zur Verfügung.</p>
</div>
</div>
<div class="field" data-section="flow">
<label for="flowStep1Url">Step 1 URL *</label>
<input id="flowStep1Url" type="url" placeholder="https://api.example.com/first">
</div>
<div class="field inline" data-section="flow">
<label for="flowStep1Method">Step 1 Methode</label>
<select id="flowStep1Method">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="field" data-section="flow">
<label for="flowStep1Headers">Step 1 Headers</label>
<textarea id="flowStep1Headers" rows="2" placeholder="Authorization: Bearer {{token}}"></textarea>
</div>
<div class="field" data-section="flow">
<label for="flowStep1Body">Step 1 Body</label>
<textarea id="flowStep1Body" rows="3" placeholder='{"id":"{{uuid}}"}'></textarea>
</div>
<div class="field" data-section="flow">
<label for="flowStep2Url">Step 2 URL (optional)</label>
<input id="flowStep2Url" type="url" placeholder="https://api.example.com/second">
</div>
<div class="field inline" data-section="flow">
<label for="flowStep2Method">Step 2 Methode</label>
<select id="flowStep2Method">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="PATCH">PATCH</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="field" data-section="flow">
<label for="flowStep2Headers">Step 2 Headers</label>
<textarea id="flowStep2Headers" rows="2" placeholder="Authorization: Bearer {{token}}"></textarea>
</div>
<div class="field" data-section="flow">
<label for="flowStep2Body">Step 2 Body</label>
<textarea id="flowStep2Body" rows="3" placeholder='{"fromStep1":"{{step1_json.id}}"}'></textarea>
</div>
<div class="field inline">
<label for="intervalPreset">Intervall</label>
<select id="intervalPreset">
<option value="hourly">Jede Stunde</option>
<option value="daily">Jeden Tag</option>
<option value="custom">Eigene Minuten</option>
</select>
</div>
<div class="field inline">
<label for="intervalMinutesInput">Intervall (Minuten)</label>
<input id="intervalMinutesInput" type="number" min="5" max="20160" step="5" value="60">
</div>
<div class="field inline">
<label for="jitterInput">Varianz (Minuten)</label>
<input id="jitterInput" type="number" min="0" max="120" step="5" value="10">
<small>Auslösung erfolgt zufällig +0…Varianz Min nach dem Intervall.</small>
</div>
<div class="field inline">
<label for="startAtInput">Start ab</label>
<input id="startAtInput" type="datetime-local">
</div>
<div class="field inline">
<label for="runUntilInput">Läuft bis</label>
<input id="runUntilInput" type="datetime-local">
</div>
<div class="field full">
<label>Ausschlusszeiten (täglich, Serverzeit)</label>
<div class="exclusion-controls">
<input id="excludeStartInput" type="time" aria-label="Ausschluss von">
<span class="exclusion-sep">bis</span>
<input id="excludeEndInput" type="time" aria-label="Ausschluss bis">
<button class="secondary-btn" id="addExclusionBtn" type="button">Hinzufügen</button>
</div>
<div id="exclusionList" class="exclusion-list"></div>
<p class="placeholder-hint">Beispiel: 00:0007:00, um nächtliche Ausführungen zu vermeiden.</p>
</div>
<div class="field full">
<div class="template-hint">
<p class="template-title">Platzhalter</p>
<table class="placeholder-table">
<tbody id="placeholderTableBody"></tbody>
</table>
<p class="placeholder-hint">Beispiele beziehen sich auf den aktuellen Zeitpunkt.</p>
</div>
</div>
<div class="field full">
<div class="preview-panel">
<div class="preview-header">
<div>
<p class="panel-eyebrow">Vorschau</p>
<h3>Aufgelöste Werte</h3>
</div>
<button class="secondary-btn" type="button" id="refreshPreviewBtn">Aktualisieren</button>
</div>
<div class="preview-grid">
<div class="preview-block">
<p class="preview-label">URL</p>
<pre id="previewUrl" class="preview-value"></pre>
</div>
<div class="preview-block">
<p class="preview-label">Headers</p>
<pre id="previewHeaders" class="preview-value"></pre>
</div>
<div class="preview-block">
<p class="preview-label">Body</p>
<pre id="previewBody" class="preview-value"></pre>
</div>
</div>
<p class="preview-hint">Die Vorschau nutzt die aktuellen Formularwerte und füllt Platzhalter ({{date}}, {{uuid}}, …) mit Beispielwerten.</p>
</div>
</div>
<div class="field full modal-actions">
<div id="formStatus" class="form-status" aria-live="polite"></div>
<div class="panel-actions">
<button class="ghost-btn" id="resetFormBtn" type="button">Zurücksetzen</button>
<button class="primary-btn" id="saveBtn" type="submit">Speichern</button>
</div>
</div>
</form>
</div>
</div>
<div id="importModal" class="modal" hidden>
<div class="modal__backdrop" id="importModalBackdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true" aria-labelledby="importTitle">
<div class="modal__header">
<div>
<p class="panel-eyebrow">Import</p>
<h2 id="importTitle">Vorlage einfügen</h2>
</div>
<button class="ghost-btn" id="importCloseBtn" type="button">×</button>
</div>
<p class="import-hint">Füge hier "Copy as cURL", "Copy as fetch" oder Powershell ein. Header, Methode, Body und URL werden übernommen.</p>
<textarea id="importInput" rows="7" placeholder="curl https://api.example.com -X POST -H 'Authorization: Bearer token' --data '{\"hello\":\"world\"}'"></textarea>
<div class="modal-actions">
<div id="importStatus" class="import-status" aria-live="polite"></div>
<button class="secondary-btn" id="applyImportBtn" type="button">Vorlage übernehmen</button>
</div>
</div>
</div>
</section>
<section id="view-daily-bookmarks" class="app-view daily-bookmarks-view" data-view="daily-bookmarks">
<div class="container">
<div class="daily-shell">
<header class="hero">
<h1 class="hero__title">Daily Bookmarks</h1>
<div class="hero__controls">
<div class="day-switch">
<button class="ghost-btn" id="dailyPrevDayBtn" aria-label="Vorheriger Tag"></button>
<div class="day-switch__label">
<div id="dailyDayLabel" class="day-switch__day">Heute</div>
<div id="dailyDaySubLabel" class="day-switch__sub"></div>
</div>
<button class="ghost-btn" id="dailyNextDayBtn" aria-label="Nächster Tag"></button>
<button class="ghost-btn ghost-btn--today" id="dailyTodayBtn">Heute</button>
</div>
<div class="hero__actions">
<div class="bulk-actions">
<label for="dailyBulkCountSelect">Anzahl</label>
<select id="dailyBulkCountSelect">
<option value="1">1</option>
<option value="5">5</option>
<option value="10">10</option>
<option value="15">15</option>
<option value="20">20</option>
</select>
<label class="auto-open-toggle">
<input type="checkbox" id="dailyAutoOpenToggle">
<span>Auto öffnen</span>
</label>
<button class="secondary-btn" id="dailyBulkOpenBtn" type="button">Öffnen & abhaken</button>
</div>
<button class="primary-btn" id="dailyOpenCreateBtn" type="button">+ Bookmark</button>
<button class="secondary-btn" id="dailyOpenImportBtn" type="button">Liste importieren</button>
<button class="ghost-btn" id="dailyRefreshBtn" type="button">Aktualisieren</button>
<span id="dailyHeroStats" class="hero__stats"></span>
</div>
</div>
</header>
<div id="dailyAutoOpenOverlay" class="auto-open-overlay" hidden>
<div class="auto-open-overlay__panel" id="dailyAutoOpenOverlayPanel">
<div class="auto-open-overlay__badge">Auto-Öffnen startet gleich</div>
<div class="auto-open-overlay__timer">
<span id="dailyAutoOpenCountdown" class="auto-open-overlay__count">0.0</span>
<span class="auto-open-overlay__unit">Sek.</span>
</div>
<p class="auto-open-overlay__text">
Die nächste Runde deiner gefilterten Bookmarks öffnet automatisch. Abbrechen, falls du noch warten willst.
</p>
<p class="auto-open-overlay__hint">Klicke irgendwo in dieses Panel, um abzubrechen.</p>
</div>
</div>
<main class="panel list-panel">
<header class="panel__header panel__header--row">
<div>
<p class="panel__eyebrow">Tägliche Liste</p>
<h2 class="panel__title">Alle Bookmarks</h2>
</div>
<div class="list-summary" id="dailyListSummary"></div>
</header>
<div id="dailyListStatus" class="list-status" role="status" aria-live="polite"></div>
<div class="table-wrapper">
<table class="bookmark-table">
<thead>
<tr>
<th class="col-url">
<button type="button" class="sort-btn" data-sort-key="url_template">URL (aufgelöst)</button>
</th>
<th class="col-marker">
<button type="button" class="sort-btn" data-sort-key="marker">Marker</button>
</th>
<th class="col-created">
<button type="button" class="sort-btn" data-sort-key="created_at">Erstelldatum</button>
</th>
<th class="col-last">
<button type="button" class="sort-btn" data-sort-key="last_completed_at">Letzte Erledigung</button>
</th>
<th>Aktionen</th>
</tr>
<tr class="table-filter-row">
<th>
<label class="visually-hidden" for="dailyUrlFilter">Nach URL filtern</label>
<input id="dailyUrlFilter" type="search" placeholder="URL filtern">
</th>
<th>
<label class="visually-hidden" for="dailyMarkerFilter">Nach Marker filtern</label>
<select id="dailyMarkerFilter">
<option value="">Alle Marker</option>
<option value="__none">Ohne Marker</option>
</select>
</th>
<th></th>
<th></th>
<th class="filter-hint">
<button type="button" class="ghost-btn ghost-btn--tiny" id="dailyResetViewBtn" aria-label="Filter und Sortierung zurücksetzen"></button>
</th>
</tr>
</thead>
<tbody id="dailyTableBody"></tbody>
</table>
</div>
</main>
</div>
</div>
<div id="dailyBookmarkModal" class="modal" hidden>
<div class="modal__backdrop" id="dailyModalBackdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true" aria-labelledby="dailyModalTitle">
<header class="modal__header">
<div>
<p class="panel__eyebrow" id="dailyFormModeLabel">Neues Bookmark</p>
<h2 id="dailyModalTitle" class="panel__title">Bookmark pflegen</h2>
<p class="panel__subtitle">Titel optional, URL-Template erforderlich. Platzhalter werden für den gewählten Tag aufgelöst.</p>
</div>
<button class="ghost-btn modal__close" type="button" id="dailyModalCloseBtn" aria-label="Schließen">×</button>
</header>
<form id="dailyBookmarkForm" class="bookmark-form" autocomplete="off">
<label class="field">
<span>Titel (optional)</span>
<input id="dailyTitleInput" type="text" name="title" maxlength="160" placeholder="z.B. Daily Gewinnspielrunde">
</label>
<label class="field">
<span>URL-Template *</span>
<input id="dailyUrlInput" type="url" name="url_template" maxlength="800" placeholder="https://www.test.de/tag-{{day}}/" required>
</label>
<div id="dailyUrlSuggestionBox" class="suggestion-box" hidden></div>
<label class="field">
<span>Notiz (optional)</span>
<textarea id="dailyNotesInput" name="notes" maxlength="800" rows="3" placeholder="Kurze Hinweise oder To-do für diesen Link"></textarea>
</label>
<label class="field">
<span>Marker (optional)</span>
<input id="dailyMarkerInput" type="text" name="marker" maxlength="120" placeholder="z.B. März-Import oder Kampagne A">
</label>
<div class="field field--switch">
<span>Status</span>
<label class="switch-control">
<input id="dailyActiveInput" type="checkbox" name="is_active" checked>
<span class="switch-control__label">Bookmark ist aktiv</span>
</label>
<p class="field__hint">Deaktivierte Bookmarks bleiben erhalten, werden aber beim Auto-Öffnen und Abhaken übersprungen.</p>
</div>
<div class="form-preview">
<div>
<p class="form-preview__label">Aufgelöste URL für den gewählten Tag:</p>
<a id="dailyPreviewLink" class="form-preview__link" href="#" target="_blank" rel="noopener"></a>
</div>
<div class="form-preview__actions">
<button class="secondary-btn" type="button" id="dailyResetBtn">Zurücksetzen</button>
<button class="primary-btn" type="submit" id="dailySubmitBtn">Speichern</button>
</div>
</div>
<div class="placeholder-help">
<p class="placeholder-help__title">Dynamische Platzhalter</p>
<ul class="placeholder-help__list">
<li><code>{{day}}</code> Tag des Monats (131), <code>{{dd}}</code> zweistellig</li>
<li><code>{{date}}</code> liefert <code>YYYY-MM-DD</code></li>
<li><code>{{mm}}</code> Monat zweistellig, <code>{{yyyy}}</code> Jahr</li>
<li><code>{{day+1}}</code> oder <code>{{date-2}}</code> verschieben um Tage</li>
<li><code>{{counter:477}}</code> Basiswert + aktueller Tag, z.B. <code>https://www.test.de/sweepstakes/{{counter:477}}/</code></li>
</ul>
</div>
<div id="dailyFormStatus" class="form-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
<div id="dailyImportModal" class="modal" hidden>
<div class="modal__backdrop" id="dailyImportBackdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true" aria-labelledby="dailyImportModalTitle">
<header class="modal__header">
<div>
<p class="panel__eyebrow">Masseneingabe</p>
<h2 id="dailyImportModalTitle" class="panel__title">Viele Bookmarks importieren</h2>
<p class="panel__subtitle">Füge hunderte Links gleichzeitig hinzu und vergebe einen gemeinsamen Marker.</p>
</div>
<button class="ghost-btn modal__close" type="button" id="dailyImportCloseBtn" aria-label="Schließen">×</button>
</header>
<form id="dailyImportForm" class="bookmark-form" autocomplete="off">
<label class="field">
<span>Liste der URL-Templates *</span>
<textarea id="dailyImportInput" name="import_urls" maxlength="120000" rows="8" placeholder="Jeder Link in eine neue Zeile, z.B. https://example.com/{{date}}/"></textarea>
</label>
<label class="field">
<span>Marker für alle Einträge (optional)</span>
<input id="dailyImportMarkerInput" type="text" name="import_marker" maxlength="120" placeholder="z.B. Batch März 2024">
</label>
<p class="import-hint">Doppelte oder ungültige Zeilen werden automatisch übersprungen.</p>
<div class="import-actions">
<button class="ghost-btn" type="button" id="dailyImportResetBtn">Zurücksetzen</button>
<button class="primary-btn" type="submit" id="dailyImportSubmitBtn">Importieren</button>
</div>
<div id="dailyImportStatus" class="import-status" role="status" aria-live="polite"></div>
</form>
</div>
</div>
</section>
<section id="view-dashboard" class="app-view" data-view="dashboard">
<div class="container">
<div class="page-toolbar page-toolbar--dashboard">
<div class="header-controls">
<div class="control-group">
<label for="timeFilter">Zeitraum:</label>
<select id="timeFilter" class="control-select">
<option value="all">Alle</option>
<option value="today">Heute</option>
<option value="week" selected>Diese Woche</option>
<option value="month">Dieser Monat</option>
<option value="year">Dieses Jahr</option>
</select>
</div>
<div class="control-group">
<label for="profileFilter">Profil-Filter:</label>
<select id="profileFilter" class="control-select">
<option value="all">Alle Profile</option>
<option value="1">Profil 1</option>
<option value="2">Profil 2</option>
<option value="3">Profil 3</option>
<option value="4">Profil 4</option>
<option value="5">Profil 5</option>
</select>
</div>
<button type="button" class="btn btn-primary" id="dailyRefreshBtn">
🔄 Aktualisieren
</button>
</div>
</div>
<div id="dashboardLoading" class="loading">Lade Statistiken...</div>
<div id="dashboardError" class="error" style="display: none;"></div>
<div id="dashboardContainer" class="dashboard-container" style="display: none;">
<!-- SECTION 1: OVERVIEW -->
<section class="dashboard-section">
<h2 class="section-title">Übersicht</h2>
<!-- Primary Stats -->
<div class="stats-grid">
<div class="stat-card stat-card--primary">
<div class="stat-card__icon">📋</div>
<div class="stat-card__content">
<div class="stat-card__label">Gesamt Beiträge</div>
<div class="stat-card__value" id="totalPosts">0</div>
</div>
</div>
<div class="stat-card stat-card--success">
<div class="stat-card__icon"></div>
<div class="stat-card__content">
<div class="stat-card__label">Abgeschlossen</div>
<div class="stat-card__value" id="completedPosts">0</div>
</div>
</div>
<div class="stat-card stat-card--warning">
<div class="stat-card__icon"></div>
<div class="stat-card__content">
<div class="stat-card__label">In Bearbeitung</div>
<div class="stat-card__value" id="activePosts">0</div>
</div>
</div>
<div class="stat-card stat-card--danger">
<div class="stat-card__icon">⚠️</div>
<div class="stat-card__content">
<div class="stat-card__label">Abgelaufen</div>
<div class="stat-card__value" id="expiredPosts">0</div>
</div>
</div>
<div class="stat-card stat-card--info">
<div class="stat-card__icon">🏆</div>
<div class="stat-card__content">
<div class="stat-card__label">Erfolgreich</div>
<div class="stat-card__value" id="successfulPosts">0</div>
</div>
</div>
</div>
<!-- Key Metrics -->
<div class="metrics-grid">
<div class="metric-card">
<div class="metric-card__label">Erfolgsquote</div>
<div class="metric-card__value" id="successRateMetric">0%</div>
<div class="metric-card__change" id="successRateChange"></div>
</div>
<div class="metric-card">
<div class="metric-card__label">Ø Bearbeitungszeit</div>
<div class="metric-card__value" id="avgCompletionTime">-</div>
<div class="metric-card__subtext">bis Abschluss</div>
</div>
<div class="metric-card">
<div class="metric-card__label">Teilnahmen heute</div>
<div class="metric-card__value" id="checksToday">0</div>
<div class="metric-card__change" id="checksTodayChange"></div>
</div>
<div class="metric-card">
<div class="metric-card__label">Deadline-Risiko</div>
<div class="metric-card__value" id="deadlineRiskValue">0</div>
<div class="metric-card__subtext" id="deadlineRiskText">keine Risiken</div>
</div>
</div>
</section>
<!-- SECTION 2: ANALYTICS -->
<section class="dashboard-section">
<h2 class="section-title">Analyse</h2>
<div class="charts-row">
<!-- Activity Timeline -->
<div class="chart-card chart-card--full">
<div class="chart-card__header">
<h3 class="chart-card__title">Aktivitätsverlauf</h3>
<span class="chart-card__subtitle" id="timelineSubtitle"></span>
</div>
<div class="chart-card__body">
<canvas id="timelineChart" width="1200" height="280"></canvas>
</div>
</div>
</div>
<div class="charts-row">
<!-- Profile Performance -->
<div class="chart-card">
<div class="chart-card__header">
<h3 class="chart-card__title">Teilnahmen pro Profil</h3>
<span class="chart-card__subtitle" id="profileChartSubtitle"></span>
</div>
<div class="chart-card__body">
<div id="profileChart" class="bar-chart"></div>
</div>
</div>
<!-- Progress Overview -->
<div class="chart-card">
<div class="chart-card__header">
<h3 class="chart-card__title">Status-Verteilung</h3>
<span class="chart-card__subtitle" id="progressChartSubtitle"></span>
</div>
<div class="chart-card__body">
<canvas id="progressChart" width="400" height="300"></canvas>
</div>
</div>
</div>
<div class="charts-row">
<!-- Period Trend -->
<div class="chart-card">
<div class="chart-card__header">
<h3 class="chart-card__title">Teilnahmen-Trend</h3>
<span class="chart-card__subtitle" id="trendChartSubtitle"></span>
</div>
<div class="chart-card__body">
<canvas id="periodTrendChart" width="500" height="300"></canvas>
</div>
</div>
<!-- Profile Comparison -->
<div class="chart-card">
<div class="chart-card__header">
<h3 class="chart-card__title">Profilvergleich</h3>
<span class="chart-card__subtitle" id="profileComparisonSubtitle"></span>
</div>
<div class="chart-card__body">
<canvas id="profileComparisonChart" width="500" height="300"></canvas>
</div>
</div>
</div>
</section>
<!-- SECTION 3: PERFORMANCE COMPARISONS -->
<section class="dashboard-section">
<h2 class="section-title">Performance-Vergleiche</h2>
<div class="comparison-grid">
<div class="comparison-card">
<h3 class="comparison-card__title">Wochenvergleich</h3>
<div class="comparison-card__content" id="weeklyComparison"></div>
</div>
<div class="comparison-card">
<h3 class="comparison-card__title">Monatsvergleich</h3>
<div class="comparison-card__content" id="monthlyComparison"></div>
</div>
<div class="comparison-card">
<h3 class="comparison-card__title">Jahresvergleich</h3>
<div class="comparison-card__content" id="yearlyComparison"></div>
</div>
</div>
<div class="success-comparison-grid">
<div class="success-comparison-card">
<h3 class="success-comparison-card__title">Erfolgsanalyse: Woche</h3>
<div class="success-comparison-card__content" id="weeklySuccessComparison"></div>
</div>
<div class="success-comparison-card">
<h3 class="success-comparison-card__title">Erfolgsanalyse: Monat</h3>
<div class="success-comparison-card__content" id="monthlySuccessComparison"></div>
</div>
<div class="success-comparison-card">
<h3 class="success-comparison-card__title">Erfolgsanalyse: Jahr</h3>
<div class="success-comparison-card__content" id="yearlySuccessComparison"></div>
</div>
</div>
</section>
<!-- SECTION 4: DETAILS -->
<section class="dashboard-section">
<h2 class="section-title">Details</h2>
<div class="details-grid">
<!-- Top Performers -->
<div class="detail-card">
<div class="detail-card__header">
<h3 class="detail-card__title">Top Performers</h3>
<span class="detail-card__badge" id="performersCount">0</span>
</div>
<div class="detail-card__body">
<div id="topPerformers" class="performers-list"></div>
</div>
</div>
<!-- Upcoming Deadlines -->
<div class="detail-card">
<div class="detail-card__header">
<h3 class="detail-card__title">Anstehende Deadlines</h3>
<span class="detail-card__badge" id="deadlinesCount">0</span>
</div>
<div class="detail-card__body">
<div id="upcomingDeadlines" class="deadline-list"></div>
</div>
</div>
<!-- Recent Activity -->
<div class="detail-card detail-card--full">
<div class="detail-card__header">
<h3 class="detail-card__title">Letzte Aktivitäten</h3>
<span class="detail-card__badge" id="activityCount">0</span>
</div>
<div class="detail-card__body">
<div id="recentActivity" class="activity-list"></div>
</div>
</div>
</div>
</section>
</div>
</div>
</section>
<section id="view-settings" class="app-view" data-view="settings">
<div class="container">
<div id="settingsLoading" class="loading" style="display: none;">Lade Einstellungen...</div>
<div id="settingsError" class="error" style="display: none;"></div>
<div id="settingsSuccess" class="success" style="display: none;"></div>
<div class="settings-container">
<!-- AI Credentials Section -->
<section class="settings-section">
<h2 class="section-title">AI-Anmeldedaten</h2>
<p class="section-description">
Verwalte deine API-Schlüssel für verschiedene AI-Provider. Du kannst mehrere Credentials speichern und schnell zwischen ihnen wechseln.
</p>
<div id="credentialsList" class="credentials-list"></div>
<button type="button" class="btn btn-primary" id="addCredentialBtn">
Neue Anmeldedaten hinzufügen
</button>
</section>
<!-- Profile Friends Section -->
<section class="settings-section">
<h2 class="section-title">👥 Freundesnamen pro Profil</h2>
<p class="section-description">
Gib für jedes Profil eine Liste von Freundesnamen an, die im Prompt verwendet werden können.
</p>
<div id="profileFriendsList"></div>
</section>
<!-- AI Settings Section -->
<section class="settings-section">
<h2 class="section-title">AI-Kommentar-Generator</h2>
<p class="section-description">
Konfiguriere die automatische Generierung von Kommentaren durch KI.
</p>
<form id="aiSettingsForm">
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="aiEnabled" class="form-checkbox">
<span>AI-Kommentar-Generator aktivieren</span>
</label>
</div>
<div class="form-group">
<label for="activeCredential" class="form-label">Aktive Anmeldedaten</label>
<select id="activeCredential" class="form-select">
<option value="">-- Bitte wählen --</option>
</select>
<p class="form-help">
Wähle welche API-Anmeldedaten verwendet werden sollen
</p>
</div>
<div class="form-group">
<label for="aiPromptPrefix" class="form-label">Prompt-Präfix</label>
<textarea id="aiPromptPrefix" class="form-textarea" rows="4"
placeholder="Anweisungen für die KI vor dem Post-Text..."></textarea>
<p class="form-help">
Dieser Text wird vor dem eigentlichen Post-Text an die KI gesendet. Verwende <code>{FREUNDE}</code> als Platzhalter für Freundesnamen.
</p>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" id="testBtn">
🧪 Kommentar testen
</button>
</div>
</form>
</section>
<!-- Sports scoring Section -->
<section class="settings-section">
<h2 class="section-title">🏷️ Sport-Post-Scoring</h2>
<p class="section-description">
Analysiert Beitragstexte nach Sport-Begriffen (z.B. Fußball, Volleyball) und weist einen Score zu.
Beiträge oberhalb des Schwellwerts würden später automatisch ausgeblendet aktuell wird nur markiert.
</p>
<form id="moderationSettingsForm">
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="sportsScoringEnabled" class="form-checkbox">
<span>Scoring aktivieren</span>
</label>
<p class="form-help">
Nutze das heuristische Punktesystem, um offensichtliche Sport-/Spiel-Posts zu erkennen.
</p>
</div>
<div class="form-group">
<label for="sportsScoreThreshold" class="form-label">Schwellwert für Sport-Posts</label>
<input type="number" id="sportsScoreThreshold" class="form-input" min="0" max="50" step="0.5" value="5">
<p class="form-help">
Ab diesem Score kann der Post automatisch versteckt werden.
</p>
</div>
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="sportsAutoHideEnabled" class="form-checkbox">
<span>Automatisch verstecken bei Überschreitung</span>
</label>
<p class="form-help">
Wenn aktiviert, blendet die Extension Posts mit Score ≥ Schwellwert automatisch aus (Feed & Suche).
</p>
</div>
<div class="form-group">
<label class="form-label">Gewichte (010)</label>
<div class="grid-weights">
<label class="form-field-inline">
<span>Ergebnis (1:0)</span>
<input type="number" id="sportWeightScoreline" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Score-Emojis (+3⃣)</span>
<input type="number" id="sportWeightScoreEmoji" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Sport-Emojis (⚽️)</span>
<input type="number" id="sportWeightSportEmoji" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Verben (gewinnen)</span>
<input type="number" id="sportWeightSportVerb" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Nomen (Liga, Tor)</span>
<input type="number" id="sportWeightSportNoun" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Hashtags (#auswärtssieg)</span>
<input type="number" id="sportWeightHashtag" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Team-Kürzel (FC…)</span>
<input type="number" id="sportWeightTeamToken" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Wettbewerbe (Cup)</span>
<input type="number" id="sportWeightCompetition" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Ergebnisbezug (Sieg)</span>
<input type="number" id="sportWeightCelebration" class="form-input" min="0" max="10" step="0.5">
</label>
<label class="form-field-inline">
<span>Ort (Auswärts)</span>
<input type="number" id="sportWeightLocation" class="form-input" min="0" max="10" step="0.5">
</label>
</div>
<p class="form-help">
Je höher das Gewicht, desto stärker zahlt der jeweilige Treffer auf den Sport-Score ein.
</p>
</div>
<div class="form-group">
<label class="form-label">Stichwort-Listen (kommagetrennt)</label>
<div class="grid-weights">
<label class="form-field-inline">
<span>Nomen</span>
<textarea id="sportTermsNouns" class="form-textarea" rows="2" placeholder="auswärtssieg, liga, tor ..."></textarea>
</label>
<label class="form-field-inline">
<span>Verben</span>
<textarea id="sportTermsVerbs" class="form-textarea" rows="2" placeholder="gewinnen, punkten ..."></textarea>
</label>
<label class="form-field-inline">
<span>Wettbewerbe</span>
<textarea id="sportTermsCompetitions" class="form-textarea" rows="2" placeholder="bundesliga, cup ..."></textarea>
</label>
<label class="form-field-inline">
<span>Ergebnisbezug</span>
<textarea id="sportTermsCelebrations" class="form-textarea" rows="2" placeholder="sieg, tabellenführung ..."></textarea>
</label>
<label class="form-field-inline">
<span>Orte</span>
<textarea id="sportTermsLocations" class="form-textarea" rows="2" placeholder="auswärts, stadion ..."></textarea>
</label>
<label class="form-field-inline">
<span>Negativliste</span>
<textarea id="sportTermsNegatives" class="form-textarea" rows="2" placeholder="rezept, politik ..."></textarea>
</label>
</div>
<p class="form-help">
Leere Felder nutzen die Standardliste. Negativliste senkt den Score bei Treffern.
</p>
</div>
</form>
</section>
<!-- Hidden posts / purge settings -->
<section class="settings-section">
<h2 class="section-title">Versteckte Beiträge bereinigen</h2>
<p class="section-description">
Steuere die automatische Bereinigung versteckter/ausgeblendeter Beiträge aus der Suche und starte bei Bedarf eine manuelle Bereinigung.
</p>
<form id="hiddenSettingsForm">
<div class="form-group">
<label class="form-label">
<input type="checkbox" id="autoPurgeHiddenToggle" class="form-checkbox">
<span>Automatisches Purging aktivieren</span>
</label>
<p class="form-help">
Löscht versteckte Beiträge nach Ablauf der Aufbewahrungsdauer automatisch.
</p>
</div>
<div class="form-group">
<label for="hiddenRetentionDays" class="form-label">Aufbewahrungsdauer (Tage)</label>
<input type="number" id="hiddenRetentionDays" class="form-input" min="1" max="365" step="1" value="90">
<p class="form-help">
Ältere versteckte Beiträge werden beim Auto-Purge entfernt. Min 1 Tag, max 365 Tage.
</p>
</div>
<div class="form-actions">
<button type="button" class="btn btn-secondary" id="purgeHiddenNowBtn">
🧹 Jetzt bereinigen
</button>
</div>
</form>
</section>
</div>
<button type="button" class="floating-save-btn" id="saveAllFloatingBtn" aria-label="Einstellungen speichern">
<span class="spinner" aria-hidden="true" style="display: none;"></span>
<span class="label">Einstellungen speichern</span>
</button>
</div>
<!-- Add/Edit Credential Modal -->
<div id="credentialModal" class="modal" hidden>
<div class="modal__backdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true">
<button type="button" id="credentialModalClose" class="modal__close">×</button>
<h2 class="modal__title" id="credentialModalTitle">Anmeldedaten hinzufügen</h2>
<form id="credentialForm">
<input type="hidden" id="credentialId">
<div class="form-group">
<label for="credentialName" class="form-label">Name *</label>
<input type="text" id="credentialName" class="form-input" placeholder="z.B. Mein Gemini Key" required>
<p class="form-help">Gib einen eindeutigen Namen für diese Anmeldedaten an</p>
</div>
<div class="form-group">
<label for="credentialProvider" class="form-label">Provider *</label>
<select id="credentialProvider" class="form-select" required>
<option value="gemini">Google Gemini</option>
<option value="claude">Anthropic Claude</option>
<option value="openai">OpenAI / ChatGPT</option>
</select>
</div>
<div class="form-group">
<label for="credentialApiKey" class="form-label">API-Schlüssel</label>
<input type="password" id="credentialApiKey" class="form-input" placeholder="sk-...">
<p class="form-help" id="credentialApiKeyHelp"></p>
</div>
<div class="form-group" id="credentialBaseUrlGroup" style="display: none;">
<label for="credentialBaseUrl" class="form-label">Basis-URL</label>
<input type="text" id="credentialBaseUrl" class="form-input" placeholder="https://api.openai.com/v1">
<p class="form-help" id="credentialBaseUrlHelp"></p>
</div>
<div class="form-group">
<label for="credentialModel" class="form-label">Modell</label>
<input type="text" id="credentialModel" class="form-input" list="credentialModelOptions"
placeholder="z.B. gpt-4o-mini">
<datalist id="credentialModelOptions"></datalist>
<p class="form-help" id="credentialModelHelp"></p>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">Speichern</button>
<button type="button" class="btn btn-secondary" id="credentialCancelBtn">Abbrechen</button>
</div>
</form>
</div>
</div>
<!-- Test Modal -->
<div id="testModal" class="modal" hidden>
<div class="modal__backdrop"></div>
<div class="modal__content" role="dialog" aria-modal="true">
<button type="button" id="testModalClose" class="modal__close">×</button>
<h2 class="modal__title">Kommentar-Generator testen</h2>
<div class="modal__body">
<div class="form-group">
<label for="testProfileNumber" class="form-label">Test mit Profil</label>
<select id="testProfileNumber" class="form-select">
<option value="1">Profil 1</option>
<option value="2">Profil 2</option>
<option value="3">Profil 3</option>
<option value="4">Profil 4</option>
<option value="5">Profil 5</option>
</select>
<p class="form-help">Wähle ein Profil, um die Freundesnamen zu testen</p>
</div>
<div class="form-group">
<label for="testPostText" class="form-label">Test Post-Text</label>
<textarea id="testPostText" class="form-textarea" rows="4"
placeholder="Füge hier einen Beispiel-Post-Text ein..."></textarea>
</div>
<button type="button" class="btn btn-primary" id="generateTestComment">
✨ Kommentar generieren
</button>
<div id="testLoading" class="test-loading" style="display: none;">
Generiere Kommentar...
</div>
<div id="testResult" class="test-result" style="display: none;">
<h3>Generierter Kommentar:</h3>
<div id="testComment" class="test-comment"></div>
</div>
<div id="testError" class="test-error" style="display: none;"></div>
</div>
</div>
</div>
</section>
<section id="view-bookmarks" class="app-view" data-view="bookmarks">
<div class="container">
<main class="bookmark-page">
<section class="bookmark-page__panel">
<div class="bookmark-panel__header">
<h2 class="bookmark-panel__title">🔖 Bookmarks</h2>
</div>
<p class="bookmark-page__lead">Über die Bookmarks kannst du auf einen Schlag mehrere relevante Suchanfragen öffnen.</p>
<div class="bookmark-panel__toolbar">
<label class="bookmark-panel__search">
<span>Suche</span>
<input type="search" id="bookmarkSearchInput" placeholder="Keyword oder Titel durchsuchen">
</label>
<div class="bookmark-panel__sort">
<label>
<span>Sortierung</span>
<select id="bookmarkSortSelect">
<option value="recent">Zuletzt verwendet</option>
<option value="label">Alphabetisch</option>
</select>
</label>
<button type="button" class="bookmark-sort__direction" id="bookmarkSortDirectionToggle" aria-pressed="false" title="Sortierreihenfolge umkehren">
<span class="bookmark-sort__direction-icon" aria-hidden="true"></span>
</button>
</div>
</div>
<form id="bookmarkQuickForm" class="bookmark-quicksearch" autocomplete="off">
<div class="bookmark-quicksearch__fields">
<label class="bookmark-quicksearch__field">
<span>Einmalige Suche</span>
<input type="text" id="bookmarkQuickQuery" placeholder="Suchbegriff nur öffnen, nicht speichern">
</label>
<button type="submit" class="btn btn-secondary">Sofort suchen</button>
</div>
<p class="bookmark-quicksearch__hint">Öffnet die drei Varianten ohne ein Bookmark anzulegen.</p>
<div id="bookmarkQuickStatus" class="bookmark-status" aria-live="polite" hidden></div>
</form>
<div id="bookmarksList" class="bookmark-list" role="list" aria-live="polite"></div>
<form id="bookmarkForm" class="bookmark-form" autocomplete="off">
<div class="bookmark-form__fields">
<label class="bookmark-form__field">
<span>Titel</span>
<input type="text" id="bookmarkName" maxlength="40" placeholder="Optionaler Titel">
</label>
<label class="bookmark-form__field">
<span>Keyword *</span>
<input type="text" id="bookmarkQuery" required placeholder="z.B. gewinnspiel">
</label>
</div>
<div class="bookmark-form__actions">
<button type="submit" class="btn btn-primary">Speichern</button>
<button type="button" class="btn btn-secondary" id="bookmarkCancelBtn">Zurücksetzen</button>
</div>
<p class="bookmark-form__hint">Öffnet drei Varianten (… Gewinnspiel / … gewinnen / … verlosen) mit Filter auf die letzten vier Wochen.</p>
</form>
</section>
</main>
</div>
</section>
</main>
</div>
<script>
(function() {
const buttons = Array.from(document.querySelectorAll('[data-view-target]'));
const views = Array.from(document.querySelectorAll('.app-view'));
const viewMap = Object.fromEntries(views.map((section) => [section.dataset.view, section]));
const params = new URLSearchParams(window.location.search);
const initialParam = params.get('view');
const defaultView = viewMap[initialParam] ? initialParam : 'posts';
const VIEW_CHANGE_EVENT = 'app:view-change';
const BASE_TITLE = 'Post Tracker';
const VIEW_TITLES = {
posts: 'Beiträge',
dashboard: 'Dashboard',
settings: 'Einstellungen',
bookmarks: 'Bookmarks',
automation: 'Automationen',
'daily-bookmarks': 'Daily Bookmarks'
};
function dispatchViewChange(view) {
window.dispatchEvent(new CustomEvent(VIEW_CHANGE_EVENT, { detail: { view } }));
}
function updateDocumentTitle(view) {
const title = VIEW_TITLES[view];
document.title = title ? `${title} ${BASE_TITLE}` : BASE_TITLE;
}
function setView(view, options = { updateHistory: true }) {
if (!viewMap[view]) {
view = 'posts';
}
views.forEach((section) => {
section.classList.toggle('app-view--active', section.dataset.view === view);
});
buttons.forEach((button) => {
const isActive = button.dataset.viewTarget === view;
button.classList.toggle('nav-active', isActive);
if (isActive) {
button.setAttribute('aria-current', 'page');
} else {
button.removeAttribute('aria-current');
}
});
if (options.updateHistory) {
const nextParams = new URLSearchParams(window.location.search);
if (view === 'posts') {
nextParams.delete('view');
} else {
nextParams.set('view', view);
nextParams.delete('tab');
}
const query = nextParams.toString();
const newUrl = query ? `${window.location.pathname}?${query}` : window.location.pathname;
window.history.pushState({ view }, '', newUrl);
}
updateDocumentTitle(view);
dispatchViewChange(view);
}
buttons.forEach((button) => {
button.addEventListener('click', (event) => {
if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
return;
}
event.preventDefault();
setView(button.dataset.viewTarget);
});
});
window.addEventListener('popstate', (event) => {
const view = (event.state && event.state.view) || new URLSearchParams(window.location.search).get('view') || 'posts';
setView(view, { updateHistory: false });
});
setView(defaultView, { updateHistory: false });
const initParams = new URLSearchParams(window.location.search);
if (defaultView === 'posts') {
initParams.delete('view');
} else {
initParams.set('view', defaultView);
initParams.delete('tab');
}
const initQuery = initParams.toString();
window.history.replaceState({ view: defaultView }, '', initQuery ? `${window.location.pathname}?${initQuery}` : window.location.pathname);
})();
</script>
<script>
(function() {
const AUTOMATION_VIEW = 'automation';
const DAILY_VIEW = 'daily-bookmarks';
function handleViewChange(event) {
const view = event?.detail?.view;
if (view === AUTOMATION_VIEW) {
window.AutomationPage?.activate?.();
} else {
window.AutomationPage?.deactivate?.();
}
if (view === DAILY_VIEW) {
window.DailyBookmarksPage?.activate?.();
} else {
window.DailyBookmarksPage?.deactivate?.();
}
}
window.addEventListener('app:view-change', handleViewChange);
const automationSection = document.querySelector('[data-view="automation"]');
if (automationSection && automationSection.classList.contains('app-view--active')) {
window.AutomationPage?.activate?.();
}
const dailySection = document.querySelector('[data-view="daily-bookmarks"]');
if (dailySection && dailySection.classList.contains('app-view--active')) {
window.DailyBookmarksPage?.activate?.();
}
})();
</script>
</body>
</html>