reworked site

This commit is contained in:
2025-11-16 13:44:11 +01:00
parent 26d7c4e6b3
commit 274762c825
9 changed files with 775 additions and 816 deletions

View File

@@ -1,27 +1,12 @@
const API_URL = 'https://fb.srv.medeba-media.de/api';
let initialViewParam = null;
// Normalize incoming routing parameters without leaving the index view
(function normalizeViewRouting() {
try {
const params = new URLSearchParams(window.location.search);
const view = params.get('view');
if (!view) {
return;
}
if (view === 'dashboard') {
initialViewParam = view;
params.delete('view');
const remaining = params.toString();
const newUrl = `${window.location.pathname}${remaining ? `?${remaining}` : ''}${window.location.hash}`;
window.history.replaceState({}, document.title, newUrl);
}
initialViewParam = params.get('view');
} catch (error) {
console.warn('Konnte view-Parameter nicht verarbeiten:', error);
console.warn('Konnte view-Parameter nicht auslesen:', error);
}
})();
let focusPostIdParam = null;
let focusPostUrlParam = null;
@@ -145,9 +130,6 @@ function scheduleUpdatesReconnect() {
}
function startUpdatesStream() {
if (isBookmarksPage) {
return;
}
if (typeof EventSource === 'undefined') {
console.warn('EventSource wird von diesem Browser nicht unterstützt. Fallback auf Polling.');
return;
@@ -253,7 +235,6 @@ const bookmarkSearchInput = document.getElementById('bookmarkSearchInput');
const bookmarkSortSelect = document.getElementById('bookmarkSortSelect');
const bookmarkSortDirectionToggle = document.getElementById('bookmarkSortDirectionToggle');
const profileSelectElement = document.getElementById('profileSelect');
const isBookmarksPage = document.body.classList.contains('bookmarks-page');
const REFRESH_SETTINGS_KEY = 'trackerRefreshSettings';
const SORT_SETTINGS_KEY = 'trackerSortSettings';
@@ -2414,10 +2395,6 @@ function applyAutoRefreshSettings() {
: '';
}
if (isBookmarksPage) {
return;
}
if (!autoRefreshSettings.enabled) {
return;
}
@@ -2730,11 +2707,9 @@ function applyProfileNumber(profileNumber, { fromBackend = false } = {}) {
pushProfileState(currentProfile);
}
if (!isBookmarksPage) {
resetVisibleCount();
renderPosts();
}
}
// Load profile from localStorage
function loadProfile() {
@@ -2809,9 +2784,6 @@ if (autoRefreshToggle) {
autoRefreshSettings.enabled = !!autoRefreshToggle.checked;
saveAutoRefreshSettings();
applyAutoRefreshSettings();
if (isBookmarksPage) {
return;
}
if (autoRefreshSettings.enabled && updatesStreamHealthy) {
console.info('Live-Updates sind aktiv; automatisches Refresh bleibt pausiert.');
}
@@ -4119,9 +4091,7 @@ function checkAutoCheck() {
} catch (error) {
console.warn('Konnte check-Parameter nicht entfernen:', error);
}
if (!isBookmarksPage) {
fetchPosts({ showLoader: false });
}
}).catch(console.error);
}
}
@@ -4190,12 +4160,10 @@ loadAutoRefreshSettings();
initializeFocusParams();
initializeTabFromUrl();
loadSortMode();
if (!isBookmarksPage) {
resetManualPostForm();
loadProfile();
startProfilePolling();
fetchPosts();
checkAutoCheck();
startUpdatesStream();
}
applyAutoRefreshSettings();

View File

@@ -2,61 +2,20 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Bookmarks Post Tracker</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post Tracker Bookmarks</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">
<link rel="stylesheet" href="style.css">
<script>
(function redirectToShell() {
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
params.set('view', 'bookmarks');
const target = `index.html${params.toString() ? `?${params}` : ''}${url.hash}`;
window.location.replace(target);
})();
</script>
</head>
<body class="bookmarks-page">
<div class="container">
<main class="bookmark-page">
<section class="bookmark-page__panel">
<div class="bookmark-panel__header">
<h2 class="bookmark-panel__title">🔖 Bookmarks</h2>
<a href="index.html" class="btn btn-secondary">Zurück zum Dashboard</a>
</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>
<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>
<script src="app.js"></script>
<body>
<p>Weiterleitung zur Bookmarks-Ansicht…</p>
</body>
</html>

View File

@@ -2,272 +2,20 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Dashboard Post Tracker</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Dashboard - Facebook 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">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="dashboard.css">
<script>
(function redirectToShell() {
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
params.set('view', 'dashboard');
const target = `index.html${params.toString() ? `?${params}` : ''}${url.hash}`;
window.location.replace(target);
})();
</script>
</head>
<body>
<div class="container">
<div class="page-toolbar page-toolbar--dashboard">
<div class="page-toolbar__title">
<h1>📊 Dashboard</h1>
</div>
<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="refreshBtn">
🔄 Aktualisieren
</button>
</div>
</div>
<div id="loading" class="loading">Lade Statistiken...</div>
<div id="error" 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>
<script src="dashboard.js"></script>
<p>Weiterleitung zum Dashboard…</p>
</body>
</html>

View File

@@ -1,17 +1,6 @@
(() => {
const API_URL = 'https://fb.srv.medeba-media.de/api';
// Check if we should redirect to posts view
(function checkViewRouting() {
const params = new URLSearchParams(window.location.search);
const view = params.get('view');
if (view === 'posts') {
// Remove view parameter and keep other params
params.delete('view');
const remainingParams = params.toString();
window.location.href = 'index.html' + (remainingParams ? '?' + remainingParams : '');
}
})();
let posts = [];
let filteredPosts = [];
let currentTimeFilter = 'week';
@@ -323,21 +312,21 @@ function apiFetch(url, options = {}) {
}
function showLoading() {
const loading = document.getElementById('loading');
const loading = document.getElementById('dashboardLoading');
if (loading) {
loading.style.display = 'block';
}
}
function hideLoading() {
const loading = document.getElementById('loading');
const loading = document.getElementById('dashboardLoading');
if (loading) {
loading.style.display = 'none';
}
}
function showError(message) {
const error = document.getElementById('error');
const error = document.getElementById('dashboardError');
if (error) {
error.textContent = message;
error.style.display = 'block';
@@ -345,7 +334,7 @@ function showError(message) {
}
function hideError() {
const error = document.getElementById('error');
const error = document.getElementById('dashboardError');
if (error) {
error.style.display = 'none';
}
@@ -1579,3 +1568,4 @@ document.getElementById('refreshBtn')?.addEventListener('click', () => {
// Initialize
fetchPosts();
})();

View File

@@ -8,102 +8,19 @@
<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">
<link rel="stylesheet" href="style.css">
<style>
body {
margin: 0;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
background: #f4f5f7;
color: #111827;
}
.shell {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f4f5f7;
}
.site-header {
background: #ffffff;
border-bottom: 1px solid #e5e7eb;
padding: 16px 24px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
position: sticky;
top: 0;
z-index: 100;
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.1);
}
.site-header__brand h1 {
margin: 0;
font-size: 20px;
}
.site-header__nav {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.site-header__nav-btn {
border: 1px solid transparent;
background: transparent;
padding: 10px 18px;
border-radius: 999px;
font-size: 14px;
font-weight: 600;
color: #111827;
cursor: pointer;
transition: all 0.2s;
}
.site-header__nav-btn:hover {
background: #eef2ff;
border-color: #c7d2fe;
}
.site-header__nav-btn.active {
background: #111827;
color: #ffffff;
border-color: #111827;
}
.shell-main {
flex: 1;
min-height: 0;
}
#appFrame {
width: 100%;
height: calc(100vh - 72px);
border: none;
}
@media (max-width: 640px) {
.site-header {
flex-direction: column;
align-items: flex-start;
}
#appFrame {
height: calc(100vh - 140px);
}
}
</style>
<link rel="stylesheet" href="dashboard.css">
<link rel="stylesheet" href="settings.css">
</head>
<body>
<div class="shell">
<header class="site-header">
<header class="site-header site-header--global">
<div class="container">
<div class="header-main">
<div>
<h1>📋 Post Tracker</h1>
<p style="margin: 0; font-size: 13px; color: #6b7280;">Alle Bereiche über diese Navigation laden</p>
<p class="site-header__subtitle">Alle Bereiche ohne Seitenwechsel erreichen</p>
</div>
<div class="header-links">
<div class="header-links site-nav">
<button type="button" class="btn btn-secondary" data-view-target="posts">Beiträge</button>
<button type="button" class="btn btn-secondary" data-view-target="dashboard">Dashboard</button>
<button type="button" class="btn btn-secondary" data-view-target="settings">⚙️ Einstellungen</button>
@@ -113,43 +30,653 @@
</div>
</header>
<main class="shell-main">
<iframe id="appFrame" src="" title="Post Tracker Inhalt" loading="lazy"></iframe>
<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">
<h1>📋 Post Tracker</h1>
<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 active" data-tab="pending">Offene Beiträge</button>
<button class="tab-btn" data-tab="expired">Abgelaufen/Abgeschlossen</button>
<button class="tab-btn" data-tab="all">Alle Beiträge</button>
</div>
<div class="search-container">
<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-dashboard" class="app-view" data-view="dashboard">
<div class="container">
<div class="page-toolbar page-toolbar--dashboard">
<div class="page-toolbar__title">
<h1>📊 Dashboard</h1>
</div>
<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="refreshBtn">
🔄 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>
<!-- 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>
</form>
</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>
<!-- Save Button at the end -->
<section class="settings-section">
<div class="form-actions">
<button type="button" class="btn btn-primary" id="saveAllBtn">
💾 Einstellungen speichern
</button>
<button type="button" class="btn btn-secondary" id="testBtn">
🧪 Kommentar testen
</button>
</div>
</section>
</div>
</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>
<a href="index.html" class="btn btn-secondary">Zurück zum Dashboard</a>
</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>
<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 src="app.js"></script>
<script src="dashboard.js"></script>
<script src="settings.js"></script>
<script>
(function() {
const frame = document.getElementById('appFrame');
const buttons = Array.from(document.querySelectorAll('[data-view-target]'));
const viewUrls = {
posts: 'posts.html',
dashboard: 'dashboard.html',
settings: 'settings.html',
bookmarks: 'bookmarks.html'
};
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 queryParams = new URLSearchParams(window.location.search);
const initial = queryParams.get('view');
const defaultView = initial && viewUrls[initial] ? initial : 'posts';
function setView(view, options = { updateHistory: true }) {
if (!viewMap[view]) {
view = 'posts';
}
function setView(view) {
buttons.forEach((button) => {
button.classList.toggle('active', button.dataset.viewTarget === view);
views.forEach((section) => {
section.classList.toggle('app-view--active', section.dataset.view === view);
});
const targetUrl = viewUrls[view] || viewUrls.posts;
if (frame.src !== targetUrl) {
frame.src = targetUrl;
}
buttons.forEach((button) => {
const isActive = button.dataset.viewTarget === view;
button.classList.toggle('nav-active', isActive);
button.setAttribute('aria-pressed', isActive ? 'true' : 'false');
});
const params = new URLSearchParams(window.location.search);
if (options.updateHistory) {
const nextParams = new URLSearchParams(window.location.search);
if (view === 'posts') {
params.delete('view');
nextParams.delete('view');
} else {
params.set('view', view);
nextParams.set('view', view);
}
const query = nextParams.toString();
const newUrl = query ? `${window.location.pathname}?${query}` : window.location.pathname;
window.history.pushState({ view }, '', newUrl);
}
const url = params.toString() ? `?${params.toString()}` : window.location.pathname;
window.history.replaceState({}, document.title, url);
}
buttons.forEach((button) => {
@@ -158,7 +685,20 @@
});
});
setView(defaultView);
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);
}
const initQuery = initParams.toString();
window.history.replaceState({ view: defaultView }, '', initQuery ? `${window.location.pathname}?${initQuery}` : window.location.pathname);
})();
</script>
</body>

View File

@@ -2,131 +2,21 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Beiträge Post Tracker</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Post Tracker - Web Interface</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">
<link rel="stylesheet" href="style.css">
<script>
(function redirectToShell() {
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
params.set('view', 'posts');
const query = params.toString();
const target = `index.html${query ? `?${query}` : ''}${url.hash}`;
window.location.replace(target);
})();
</script>
</head>
<body>
<div class="container">
<div class="page-toolbar">
<div class="page-toolbar__title">
<h1>📋 Post Tracker</h1>
<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 active" data-tab="pending">Offene Beiträge</button>
<button class="tab-btn" data-tab="expired">Abgelaufen/Abgeschlossen</button>
<button class="tab-btn" data-tab="all">Alle Beiträge</button>
</div>
<div class="search-container">
<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>
<script src="app.js"></script>
<p>Weiterleitung zur Beiträge-Ansicht…</p>
</body>
</html>

View File

@@ -2,195 +2,20 @@
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Einstellungen Post Tracker</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Einstellungen - Facebook 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">
<link rel="stylesheet" href="style.css">
<link rel="stylesheet" href="settings.css">
<script>
(function redirectToShell() {
const url = new URL(window.location.href);
const params = new URLSearchParams(url.search);
params.set('view', 'settings');
const target = `index.html${params.toString() ? `?${params}` : ''}${url.hash}`;
window.location.replace(target);
})();
</script>
</head>
<body>
<div class="container">
<div id="loading" class="loading" style="display: none;">Lade Einstellungen...</div>
<div id="error" class="error" style="display: none;"></div>
<div id="success" 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>
<!-- 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>
</form>
</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>
<!-- Save Button at the end -->
<section class="settings-section">
<div class="form-actions">
<button type="button" class="btn btn-primary" id="saveAllBtn">
💾 Einstellungen speichern
</button>
<button type="button" class="btn btn-secondary" id="testBtn">
🧪 Kommentar testen
</button>
</div>
</section>
</div>
</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>
<script src="settings.js"></script>
<p>Weiterleitung zu den Einstellungen…</p>
</body>
</html>

View File

@@ -1,3 +1,4 @@
(() => {
const API_URL = 'https://fb.srv.medeba-media.de/api';
const PROVIDER_MODELS = {
@@ -792,3 +793,4 @@ document.getElementById('generateTestComment').addEventListener('click', generat
// Initialize
Promise.all([loadCredentials(), loadSettings(), loadProfileFriends()]).catch(err => showError(err.message));
})();

View File

@@ -11,6 +11,18 @@ body {
line-height: 1.5;
}
.shell {
min-height: 100vh;
display: flex;
flex-direction: column;
background: #f0f2f5;
}
.shell-main {
flex: 1;
padding-bottom: 40px;
}
.container {
max-width: 1200px;
margin: 0 auto;
@@ -29,6 +41,12 @@ header {
gap: 12px;
}
.site-header__subtitle {
margin: 0;
font-size: 13px;
color: #6b7280;
}
.page-toolbar {
background: white;
padding: 16px 18px;
@@ -61,6 +79,17 @@ header {
flex-wrap: wrap;
}
.site-nav .btn {
border-radius: 999px;
padding-inline: 20px;
}
.site-nav .btn.nav-active {
background: #111827;
color: #ffffff;
border-color: #111827;
}
.header-controls {
display: flex;
flex-wrap: wrap;
@@ -155,6 +184,14 @@ header {
transform: scale(0.95);
}
.app-view {
display: none;
}
.app-view--active {
display: block;
}
.control-group label {
font-weight: 600;
}