abdulsalam2121
Add live browser screenshot panel for dashboard
99c2f3b
Raw
History Blame Contribute Delete
6.91 kB
const elements = {
username: document.getElementById('username'),
password: document.getElementById('password'),
studio: document.getElementById('studio'),
minPrice: document.getElementById('min_price'),
startBtn: document.getElementById('startBtn'),
stopBtn: document.getElementById('stopBtn'),
saveBtn: document.getElementById('saveBtn'),
downloadBtn: document.getElementById('downloadBtn'),
clearLogsBtn: document.getElementById('clearLogsBtn'),
errorBanner: document.getElementById('errorBanner'),
successBanner: document.getElementById('successBanner'),
connectionStatus: document.getElementById('connectionStatus'),
currentState: document.getElementById('currentState'),
totalItems: document.getElementById('totalItems'),
currentPage: document.getElementById('currentPage'),
lastRunTime: document.getElementById('lastRunTime'),
progressBar: document.getElementById('progressBar'),
progressLabel: document.getElementById('progressLabel'),
csvLabel: document.getElementById('csvLabel'),
logOutput: document.getElementById('logOutput'),
};
let logCursor = 0;
let pollTimer = null;
let running = false;
let screenshotTimer = null;
function setBanner(element, message) {
if (!message) {
element.classList.add('hidden');
element.textContent = '';
return;
}
element.textContent = message;
element.classList.remove('hidden');
}
function showError(message) {
setBanner(elements.errorBanner, message);
setBanner(elements.successBanner, '');
}
function showSuccess(message) {
setBanner(elements.successBanner, message);
setBanner(elements.errorBanner, '');
}
function clearBanners() {
setBanner(elements.errorBanner, '');
setBanner(elements.successBanner, '');
}
function readFormData() {
return {
username: elements.username.value.trim(),
password: elements.password.value,
studio: elements.studio.value.trim(),
min_price: elements.minPrice.value.trim(),
};
}
function setRunningState(isRunning) {
running = isRunning;
elements.startBtn.disabled = isRunning;
elements.stopBtn.disabled = !isRunning;
elements.connectionStatus.textContent = isRunning ? 'Running' : 'Ready';
if (isRunning) {
elements.connectionStatus.parentElement.querySelector('.dot').style.background = '#f59e0b';
} else {
elements.connectionStatus.parentElement.querySelector('.dot').style.background = '#22c55e';
}
}
function updateStatusPanel(data) {
elements.currentState.textContent = data.current_state || 'Idle';
elements.totalItems.textContent = String(data.total_items_found ?? 0);
elements.currentPage.textContent = String(data.current_page ?? 0);
elements.lastRunTime.textContent = data.last_run_time || '-';
const progress = Number(data.progress || 0);
elements.progressBar.style.width = `${Math.max(0, Math.min(100, progress))}%`;
elements.progressLabel.textContent = `${Math.max(0, Math.min(100, progress))}%`;
elements.csvLabel.textContent = data.last_csv_path ? `Latest CSV: ${data.last_csv_path}` : 'No CSV generated yet';
if (data.last_error) {
showError(data.last_error);
}
}
function appendLogs(logs) {
if (!Array.isArray(logs) || logs.length <= logCursor) {
return;
}
const newLogs = logs.slice(logCursor);
if (!newLogs.length) {
return;
}
const current = elements.logOutput.textContent === 'Waiting for the next run...' ? '' : elements.logOutput.textContent;
const merged = [current.trimEnd(), ...newLogs].filter(Boolean).join('\n');
elements.logOutput.textContent = merged;
elements.logOutput.scrollTop = elements.logOutput.scrollHeight;
logCursor = logs.length;
}
async function fetchStatus() {
try {
const response = await fetch('/status', { cache: 'no-store' });
const data = await response.json();
setRunningState(Boolean(data.running));
updateStatusPanel(data);
appendLogs(data.logs || []);
if (!data.running && pollTimer) {
window.clearInterval(pollTimer);
pollTimer = null;
}
} catch (error) {
console.error(error);
}
}
async function startBot() {
clearBanners();
const payload = readFormData();
if (!payload.username || !payload.password || !payload.studio) {
showError('Please enter username, password, and a studio name or URL.');
return;
}
if (!payload.min_price || Number.isNaN(Number(payload.min_price))) {
showError('Minimum price must be a number.');
return;
}
try {
const response = await fetch('/start', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok || !data.ok) {
showError(data.error || 'Failed to start automation.');
return;
}
showSuccess('Automation started.');
logCursor = 0;
elements.logOutput.textContent = 'Waiting for the next run...';
setRunningState(true);
if (!pollTimer) {
pollTimer = window.setInterval(fetchStatus, 2000);
}
await fetchStatus();
} catch (error) {
showError(error.message || 'Unable to start the bot.');
}
}
async function stopBot() {
try {
const response = await fetch('/stop', { method: 'POST' });
const data = await response.json();
showSuccess(data.message || 'Stop requested.');
await fetchStatus();
} catch (error) {
showError(error.message || 'Unable to stop the bot.');
}
}
async function saveSettings() {
clearBanners();
const payload = readFormData();
try {
const response = await fetch('/save-settings', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const data = await response.json();
if (!response.ok || !data.ok) {
showError(data.error || 'Unable to save settings.');
return;
}
showSuccess('Settings saved.');
} catch (error) {
showError(error.message || 'Unable to save settings.');
}
}
function clearLogs() {
logCursor = 0;
elements.logOutput.textContent = 'Waiting for the next run...';
}
function boot() {
elements.startBtn.addEventListener('click', startBot);
elements.stopBtn.addEventListener('click', stopBot);
elements.saveBtn.addEventListener('click', saveSettings);
elements.clearLogsBtn.addEventListener('click', clearLogs);
elements.downloadBtn.addEventListener('click', () => {
elements.downloadBtn.setAttribute('href', '/download');
});
setRunningState(false);
fetchStatus();
pollTimer = window.setInterval(fetchStatus, 2000);
// Start screenshot refresher
const img = document.getElementById('screenshotImg');
if (img) {
const refresh = () => {
img.src = '/screenshot?ts=' + Date.now();
};
// refresh every 3 seconds
screenshotTimer = window.setInterval(refresh, 3000);
// try an initial load
refresh();
}
}
document.addEventListener('DOMContentLoaded', boot);