feat: add per-account web UI with account tabs

- Account tab bar with tab switching (loadAccounts, switchAccount)
- Per-account dashboard panel showing auth status, channels, jobs, continuous
- Settings dialog with account management (add/delete accounts, credentials)
- Auth forms per account (QR, phone, password, set credentials)
- All action buttons with error handling (alert on failure)
- AMOLED dark theme with Telegram-style message bubbles
- Account tab and account list CSS styles
This commit is contained in:
2026-06-27 15:20:49 +02:00
parent bb4e28a710
commit 62a6a492e0
3 changed files with 1017 additions and 444 deletions
+180 -110
View File
@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Telegram Scraper Control Panel</title>
<link rel="stylesheet" href="/static/style.css?v=2" />
<link rel="stylesheet" href="/static/style.css?v=4" />
</head>
<body>
<div class="app-shell">
@@ -22,10 +22,10 @@
<a class="nav-link" href="/health">Health</a>
</nav>
<section class="status-card">
<div class="section-title">Telegram Status</div>
<div id="auth-status" class="status-badge">Checking...</div>
<p id="auth-details" class="muted small"></p>
<section class="status-card" id="active-account-card">
<div class="section-title">Active Account</div>
<div id="active-account-name" class="status-badge">Loading...</div>
<p id="active-account-status" class="muted small"></p>
</section>
<section class="status-card">
@@ -38,127 +38,54 @@
</aside>
<main class="content">
<div class="content-tabs">
<button class="content-tab active" data-tab-target="overview-tab" type="button">Overview</button>
<button class="content-tab" data-tab-target="continuous-tab" type="button">Continuous</button>
<!-- Account Tabs -->
<div id="account-tabs" class="account-tabs"></div>
<!-- Account Dashboard Panels -->
<div id="account-panels">
<!-- Each account's dashboard is rendered here dynamically -->
</div>
<section id="overview-tab" class="tab-panel active">
<section class="panel-grid">
<div class="panel stat-panel">
<div class="section-title">Channels</div>
<div id="channel-count" class="stat-value">0</div>
<!-- Fallback for no accounts -->
<section id="no-accounts-msg" class="panel hidden">
<div class="panel-header">
<div>
<h2>No Accounts Configured</h2>
<p class="muted">Click "Add Account" in Settings to get started, or use the legacy single-account mode.</p>
</div>
<div class="panel stat-panel">
<div class="section-title">Media scraping</div>
<div id="scrape-media-label" class="stat-value stat-compact">OFF</div>
</div>
<div class="panel stat-panel">
<div class="section-title">Forwarding rules</div>
<div id="forwarding-count" class="stat-value">0</div>
</div>
</section>
<section class="panel">
<div class="panel-header">
<div>
<h2>Tracked Channels</h2>
<p class="muted">Stored in <code>data/state.json</code>. Message stats come from SQLite.</p>
</div>
<form id="add-channel-form" class="inline-form">
<input id="add-channel-id" name="channel_id" placeholder="ID or @username" required />
<input id="add-channel-name" name="name" placeholder="Display name" />
<button class="button primary" type="submit">Add</button>
</form>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Channel</th>
<th>Messages</th>
<th>Media</th>
<th>Last message</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="channels-table"></tbody>
</table>
</div>
</section>
<section class="panel jobs-panel">
<div class="panel-header">
<div>
<h2>Jobs</h2>
<p class="muted">Queued work runs one job at a time to keep the Telegram session stable.</p>
</div>
<button class="button" id="refresh-jobs-btn">Refresh</button>
</div>
<div id="jobs-list" class="jobs-list"></div>
</section>
</section>
<section id="continuous-tab" class="tab-panel">
<section class="panel">
<div class="panel-header">
<div>
<h2>Continuous Scraping</h2>
<p class="muted">Runs on a timer and keeps a compact, highlighted run log.</p>
</div>
</div>
<form id="continuous-form" class="continuous-form">
<label class="toggle-row">
<span>Enabled</span>
<span class="switch">
<input id="continuous-enabled" type="checkbox" checked />
<span class="switch-slider"></span>
</span>
</label>
<label class="stack-form">
<span class="muted small">Interval, minutes</span>
<input id="continuous-interval" type="number" min="1" value="1" />
</label>
<label class="toggle-row">
<span>All tracked channels</span>
<span class="switch">
<input id="continuous-all" type="checkbox" checked />
<span class="switch-slider"></span>
</span>
</label>
<div class="channel-picker">
<div class="muted small">Continuous channel set</div>
<div id="continuous-channel-list" class="checkbox-grid"></div>
</div>
<button class="button primary" type="submit">Save continuous settings</button>
</form>
<div class="continuous-meta">
<div>Status: <span id="continuous-status">-</span></div>
<div>Last run: <span id="continuous-last-iteration">-</span></div>
<div>Last error: <span id="continuous-last-error">-</span></div>
</div>
<div id="continuous-logs" class="log-viewer"></div>
</section>
</div>
</section>
</main>
</div>
<!-- ════════ Settings Dialog ════════ -->
<dialog id="settings-dialog" class="settings-dialog">
<div class="dialog-shell">
<div class="dialog-header">
<div>
<div class="eyebrow">Settings</div>
<h2>Telegram & Scraping</h2>
<h2>Global & Accounts</h2>
</div>
<button class="button button-small" id="close-settings-btn" type="button">Close</button>
</div>
<!-- ── Accounts Management ── -->
<section class="settings-section">
<div class="section-title">Telegram Login</div>
<div class="section-title">Accounts</div>
<div id="accounts-list" class="accounts-list"></div>
<form id="add-account-form" class="stack-form add-account-form">
<input id="add-account-id" name="account_id" placeholder="Account ID (e.g. work)" required />
<input id="add-account-label" name="label" placeholder="Display name (e.g. Work Account)" />
<input id="add-account-api-id" name="api_id" placeholder="API ID" />
<input id="add-account-api-hash" name="api_hash" placeholder="API Hash" />
<button class="button primary" type="submit">Add Account</button>
</form>
</section>
<!-- ── Account Auth ── (shown per account selected in UI) -->
<section class="settings-section" id="account-auth-section">
<div class="section-title">Account Auth: <span id="auth-account-label">-</span></div>
<form id="credentials-form" class="stack-form">
<input id="api-id-input" name="api_id" placeholder="API ID" />
<input id="api-hash-input" name="api_hash" placeholder="API Hash" />
@@ -190,6 +117,7 @@
</form>
</section>
<!-- ── Scraping ── -->
<section class="settings-section">
<div class="section-title">Scraping</div>
<label class="toggle-row">
@@ -203,6 +131,132 @@
</div>
</dialog>
<!-- ════════ Templates ════════ -->
<!-- Account Tab -->
<template id="account-tab-template">
<button class="account-tab" type="button">
<span class="account-tab-name"></span>
<span class="account-tab-status"></span>
</button>
</template>
<!-- Account Dashboard Panel -->
<template id="account-panel-template">
<section class="account-panel">
<!-- Dashboard tabs: Overview, Continuous -->
<div class="content-tabs">
<button class="content-tab active" data-tab-target="overview" type="button">Overview</button>
<button class="content-tab" data-tab-target="continuous" type="button">Continuous</button>
</div>
<!-- Overview Tab -->
<div class="tab-panel active" data-panel="overview">
<section class="panel-grid">
<div class="panel stat-panel">
<div class="section-title">Channels</div>
<div class="stat-value channel-count">0</div>
</div>
<div class="panel stat-panel">
<div class="section-title">Media scraping</div>
<div class="stat-value stat-compact scrape-media-label">OFF</div>
</div>
<div class="panel stat-panel">
<div class="section-title">Forwarding rules</div>
<div class="stat-value forwarding-count">0</div>
</div>
</section>
<section class="panel">
<div class="panel-header">
<div>
<h2>Tracked Channels</h2>
<p class="muted">Per-account channel list.</p>
</div>
<form class="inline-form add-channel-form">
<input class="add-channel-id" name="channel_id" placeholder="ID or @username" required />
<input class="add-channel-name" name="name" placeholder="Display name" />
<button class="button primary" type="submit">Add</button>
</form>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>Channel</th>
<th>Messages</th>
<th>Media</th>
<th>Last message</th>
<th>Actions</th>
</tr>
</thead>
<tbody class="channels-table"></tbody>
</table>
</div>
</section>
<section class="panel jobs-panel">
<div class="panel-header">
<div>
<h2>Jobs</h2>
<p class="muted">Queued work runs one job at a time.</p>
</div>
<button class="button refresh-jobs-btn">Refresh</button>
</div>
<div class="jobs-list"></div>
</section>
</div>
<!-- Continuous Tab -->
<div class="tab-panel" data-panel="continuous">
<section class="panel">
<div class="panel-header">
<div>
<h2>Continuous Scraping</h2>
<p class="muted">Per-account continuous scrape settings.</p>
</div>
</div>
<form class="continuous-form">
<label class="toggle-row">
<span>Enabled</span>
<span class="switch">
<input type="checkbox" class="continuous-enabled" checked />
<span class="switch-slider"></span>
</span>
</label>
<label class="stack-form">
<span class="muted small">Interval, minutes</span>
<input type="number" class="continuous-interval" min="1" value="1" />
</label>
<label class="toggle-row">
<span>All tracked channels</span>
<span class="switch">
<input type="checkbox" class="continuous-all" checked />
<span class="switch-slider"></span>
</span>
</label>
<div class="channel-picker">
<div class="muted small">Continuous channel set</div>
<div class="checkbox-grid continuous-channel-list"></div>
</div>
<button class="button primary" type="submit">Save continuous settings</button>
</form>
<div class="continuous-meta">
<div>Status: <span class="continuous-status">-</span></div>
<div>Last run: <span class="continuous-last-iteration">-</span></div>
<div>Last error: <span class="continuous-last-error">-</span></div>
</div>
<div class="log-viewer continuous-logs"></div>
</section>
</div>
</section>
</template>
<!-- Channel Row -->
<template id="channel-row-template">
<tr>
<td>
@@ -224,6 +278,7 @@
</tr>
</template>
<!-- Job Template -->
<template id="job-template">
<article class="job-card">
<div class="job-head">
@@ -235,6 +290,21 @@
</article>
</template>
<script src="/static/app.js"></script>
<!-- Account list item in settings -->
<template id="account-list-item-template">
<div class="account-list-item">
<div class="account-list-info">
<span class="account-list-label"></span>
<span class="muted small account-list-id"></span>
<span class="account-list-auth-status"></span>
</div>
<div class="account-list-actions">
<button class="button button-small account-select-btn">Select</button>
<button class="button button-small danger account-remove-btn">Remove</button>
</div>
</div>
</template>
<script src="/static/app.js?v=3"></script>
</body>
</html>