telegram-analytics / templates /settings.html
rottg's picture
Update code
03f1ed6 verified
<!DOCTYPE html>
<html lang="he" dir="rtl">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Settings - Telegram Analytics</title>
<link rel="stylesheet" href="/static/css/style.css">
<style>
.upload-zone {
border: 2px dashed var(--border-color);
border-radius: var(--radius-lg);
padding: var(--spacing-xl);
text-align: center;
transition: all 0.3s ease;
cursor: pointer;
margin-bottom: var(--spacing-xl);
}
.upload-zone:hover,
.upload-zone.dragover {
border-color: var(--primary);
background: rgba(0, 136, 204, 0.1);
}
.upload-zone-icon {
font-size: 3rem;
margin-bottom: var(--spacing-md);
}
.upload-zone-text {
color: var(--text-secondary);
margin-bottom: var(--spacing-sm);
}
.upload-zone-hint {
font-size: 0.75rem;
color: var(--text-muted);
}
.upload-progress {
display: none;
margin-top: var(--spacing-lg);
}
.upload-progress.active {
display: block;
}
.progress-bar-container {
background: var(--bg-sidebar);
border-radius: var(--radius-md);
height: 20px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: var(--primary);
transition: width 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 0.75rem;
}
.upload-result {
display: none;
margin-top: var(--spacing-lg);
padding: var(--spacing-lg);
border-radius: var(--radius-lg);
}
.upload-result.success {
display: block;
background: rgba(40, 167, 69, 0.2);
border: 1px solid var(--success);
}
.upload-result.error {
display: block;
background: rgba(220, 53, 69, 0.2);
border: 1px solid var(--danger);
}
.result-title {
font-weight: 600;
margin-bottom: var(--spacing-md);
display: flex;
align-items: center;
gap: var(--spacing-sm);
}
.result-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: var(--spacing-md);
}
.result-stat {
text-align: center;
padding: var(--spacing-md);
background: var(--bg-sidebar);
border-radius: var(--radius-md);
}
.result-stat-value {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
}
.result-stat-label {
font-size: 0.75rem;
color: var(--text-muted);
}
.db-stats {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: var(--spacing-md);
margin-bottom: var(--spacing-xl);
}
.db-stat {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
border: 1px solid var(--border-color);
}
.db-stat-value {
font-size: 1.75rem;
font-weight: 700;
color: var(--primary);
}
.db-stat-label {
font-size: 0.875rem;
color: var(--text-muted);
margin-top: var(--spacing-xs);
}
.instructions {
background: var(--bg-card);
border-radius: var(--radius-lg);
padding: var(--spacing-lg);
border: 1px solid var(--border-color);
}
.instructions h3 {
margin-bottom: var(--spacing-md);
}
.instructions ol {
padding-right: var(--spacing-lg);
color: var(--text-secondary);
line-height: 1.8;
}
.instructions code {
background: var(--bg-sidebar);
padding: 2px 6px;
border-radius: var(--radius-sm);
font-family: monospace;
}
</style>
</head>
<body>
<button class="mobile-menu-btn" onclick="toggleMobileMenu()">&#9776;</button>
<div class="sidebar-overlay" onclick="toggleMobileMenu()"></div>
<!-- Sidebar -->
<nav class="sidebar">
<div class="logo">
<span class="logo-icon">📊</span>
<span class="logo-text">TG Analytics</span>
</div>
<ul class="nav-menu">
<li class="nav-item">
<a href="/" class="nav-link">
<span class="icon">📈</span>
<span>Overview</span>
</a>
</li>
<li class="nav-item">
<a href="/users" class="nav-link">
<span class="icon">👥</span>
<span>Users</span>
</a>
</li>
<li class="nav-item">
<a href="/chat" class="nav-link">
<span class="icon">💬</span>
<span>Chat</span>
</a>
</li>
<li class="nav-item">
<a href="/search" class="nav-link">
<span class="icon">🔍</span>
<span>Search</span>
</a>
</li>
<li class="nav-item">
<a href="/ai-search" class="nav-link">
<span class="icon">🤖</span>
<span>AI Search</span>
</a>
</li>
<li class="nav-item">
<a href="/moderation" class="nav-link">
<span class="icon">🛡️</span>
<span>Moderation</span>
</a>
</li>
<li class="nav-item active">
<a href="/settings" class="nav-link">
<span class="icon">⚙️</span>
<span>Settings</span>
</a>
</li>
<li class="nav-item">
<a href="/maintenance" class="nav-link">
<span class="icon">🔒</span>
<span>Maintenance</span>
</a>
</li>
</ul>
</nav>
<!-- Main Content -->
<main class="main-content">
<!-- Header -->
<header class="header">
<h1>⚙️ Settings & Update Data</h1>
</header>
<!-- Database Stats -->
<section>
<h2 style="margin-bottom: var(--spacing-md);">📊 Database Status</h2>
<div class="db-stats" id="db-stats">
<div class="db-stat">
<div class="db-stat-value" id="stat-messages">-</div>
<div class="db-stat-label">Total Messages</div>
</div>
<div class="db-stat">
<div class="db-stat-value" id="stat-users">-</div>
<div class="db-stat-label">Total Users</div>
</div>
<div class="db-stat">
<div class="db-stat-value" id="stat-first">-</div>
<div class="db-stat-label">First Message</div>
</div>
<div class="db-stat">
<div class="db-stat-value" id="stat-last">-</div>
<div class="db-stat-label">Last Message</div>
</div>
<div class="db-stat">
<div class="db-stat-value" id="stat-size">-</div>
<div class="db-stat-label">Database Size</div>
</div>
</div>
</section>
<!-- Upload Section (disabled - updates done locally) -->
<section class="chart-card" style="margin-bottom: var(--spacing-xl); opacity: 0.6;">
<div class="chart-header">
<h3>📤 Update Database</h3>
</div>
<div style="padding: var(--spacing-lg); text-align: center; color: var(--text-muted);">
<p>עדכוני מסד הנתונים מתבצעים מקומית באמצעות daily_sync.py</p>
</div>
</section>
<!-- Instructions -->
<section class="instructions">
<h3>📖 איך לייצא נתונים מטלגרם</h3>
<ol>
<li>פתח את <strong>Telegram Desktop</strong> (לא ניתן מהאפליקציה הניידת)</li>
<li>לך ל-<strong>Settings → Advanced → Export Telegram data</strong></li>
<li>בחר את הקבוצה/צ'אט שברצונך לייצא</li>
<li>סמן <strong>JSON</strong> כפורמט הייצוא</li>
<li>לחץ <strong>Export</strong> והמתן לסיום</li>
<li>העלה את קובץ <code>result.json</code> כאן</li>
</ol>
<div style="margin-top: var(--spacing-lg); padding: var(--spacing-md); background: var(--bg-sidebar); border-radius: var(--radius-md);">
<strong>💡 טיפ:</strong> המערכת תזהה אוטומטית הודעות כפולות ותוסיף רק הודעות חדשות.
אין צורך לדאוג מהעלאת אותו קובץ פעמיים.
</div>
</section>
<!-- CLI Instructions -->
<section class="instructions" style="margin-top: var(--spacing-xl);">
<h3>💻 עדכון דרך שורת הפקודה</h3>
<p style="color: var(--text-secondary); margin-bottom: var(--spacing-md);">
לקבצים גדולים, מומלץ להשתמש בשורת הפקודה:
</p>
<pre style="background: var(--bg-sidebar); padding: var(--spacing-md); border-radius: var(--radius-md); overflow-x: auto; direction: ltr; text-align: left;">
# עדכון database קיים עם JSON חדש
python indexer.py new_export.json --db telegram.db --update
# יצירת database חדש
python indexer.py result.json --db telegram.db
</pre>
</section>
</main>
<script>
// Load database stats on page load
document.addEventListener('DOMContentLoaded', loadDbStats);
async function loadDbStats() {
try {
const response = await fetch('/api/db/stats');
const stats = await response.json();
document.getElementById('stat-messages').textContent =
stats.total_messages?.toLocaleString() || '-';
document.getElementById('stat-users').textContent =
stats.total_users?.toLocaleString() || '-';
document.getElementById('stat-first').textContent =
stats.first_message ? new Date(stats.first_message).toLocaleDateString('he-IL') : '-';
document.getElementById('stat-last').textContent =
stats.last_message ? new Date(stats.last_message).toLocaleDateString('he-IL') : '-';
document.getElementById('stat-size').textContent =
stats.db_size_mb ? `${stats.db_size_mb} MB` : '-';
} catch (error) {
console.error('Error loading db stats:', error);
}
}
// Drag and drop handlers
const uploadZone = document.getElementById('upload-zone');
uploadZone.addEventListener('dragover', (e) => {
e.preventDefault();
uploadZone.classList.add('dragover');
});
uploadZone.addEventListener('dragleave', () => {
uploadZone.classList.remove('dragover');
});
uploadZone.addEventListener('drop', (e) => {
e.preventDefault();
uploadZone.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
uploadFile(files[0]);
}
});
function handleFileSelect(event) {
const file = event.target.files[0];
if (file) {
uploadFile(file);
}
}
async function uploadFile(file) {
if (!file.name.endsWith('.json')) {
showError('נא לבחור קובץ JSON בלבד');
return;
}
const progressDiv = document.getElementById('upload-progress');
const progressFill = document.getElementById('progress-fill');
const progressText = document.getElementById('progress-text');
const resultDiv = document.getElementById('upload-result');
// Reset and show progress
progressDiv.classList.add('active');
resultDiv.className = 'upload-result';
progressFill.style.width = '0%';
progressFill.textContent = '0%';
progressText.textContent = `מעלה ${file.name}...`;
try {
// Read file
progressFill.style.width = '20%';
progressFill.textContent = '20%';
progressText.textContent = 'קורא קובץ...';
const formData = new FormData();
formData.append('file', file);
// Upload
progressFill.style.width = '50%';
progressFill.textContent = '50%';
progressText.textContent = 'מעבד נתונים...';
const response = await fetch('/api/update', {
method: 'POST',
body: formData
});
const result = await response.json();
progressFill.style.width = '100%';
progressFill.textContent = '100%';
if (result.success) {
showSuccess(result.stats);
loadDbStats(); // Refresh stats
} else {
showError(result.error || 'שגיאה לא ידועה');
}
} catch (error) {
showError(error.message);
}
// Hide progress after a delay
setTimeout(() => {
progressDiv.classList.remove('active');
}, 1000);
}
function showSuccess(stats) {
const resultDiv = document.getElementById('upload-result');
const resultTitle = document.getElementById('result-title');
const resultStats = document.getElementById('result-stats');
resultDiv.className = 'upload-result success';
resultTitle.innerHTML = '✅ העדכון הושלם בהצלחה!';
resultStats.innerHTML = `
<div class="result-stat">
<div class="result-stat-value">${stats.total_in_file?.toLocaleString() || 0}</div>
<div class="result-stat-label">הודעות בקובץ</div>
</div>
<div class="result-stat">
<div class="result-stat-value">${stats.new_messages?.toLocaleString() || 0}</div>
<div class="result-stat-label">הודעות חדשות נוספו</div>
</div>
<div class="result-stat">
<div class="result-stat-value">${stats.duplicates?.toLocaleString() || 0}</div>
<div class="result-stat-label">כפילויות (דולגו)</div>
</div>
<div class="result-stat">
<div class="result-stat-value">${stats.elapsed_seconds?.toFixed(1) || 0}s</div>
<div class="result-stat-label">זמן עיבוד</div>
</div>
`;
}
function showError(message) {
const resultDiv = document.getElementById('upload-result');
const resultTitle = document.getElementById('result-title');
const resultStats = document.getElementById('result-stats');
resultDiv.className = 'upload-result error';
resultTitle.innerHTML = `❌ שגיאה: ${message}`;
resultStats.innerHTML = '';
}
</script>
<script>
function toggleMobileMenu(){var s=document.querySelector('.sidebar'),o=document.querySelector('.sidebar-overlay');s.classList.toggle('open');if(o)o.classList.toggle('active');}
</script>
</body>
</html>