1st restructure

This commit is contained in:
Michael Beck
2025-02-06 23:27:30 +01:00
parent f552601c4b
commit ceebbafed5
24 changed files with 56 additions and 198 deletions

26
app/static/color_mode.js Normal file
View File

@@ -0,0 +1,26 @@
document.addEventListener('DOMContentLoaded', () => {
const themeToggle = document.getElementById('bd-theme');
// Check if a theme preference is saved in localStorage
const savedTheme = localStorage.getItem('theme');
if (savedTheme === 'dark') {
themeToggle.checked = true;
document.documentElement.setAttribute('data-bs-theme', 'dark');
} else {
themeToggle.checked = false;
document.documentElement.setAttribute('data-bs-theme', 'light');
}
// Add event listener to toggle theme on checkbox change
themeToggle.addEventListener('change', () => {
if (themeToggle.checked) {
document.documentElement.setAttribute('data-bs-theme', 'dark');
localStorage.setItem('theme', 'dark');
} else {
document.documentElement.setAttribute('data-bs-theme', 'light');
localStorage.setItem('theme', 'light');
}
});
});

View File

@@ -0,0 +1,104 @@
async function deleteFiles(filePaths) {
try {
const response = await fetch('/delete_files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ file_paths: filePaths })
});
const data = await response.json();
if (data.success) {
alert('Files deleted successfully');
location.reload();
} else {
alert(`Error deleting files: ${JSON.stringify(data.errors)}`);
}
} catch (error) {
console.error('Error:', error);
alert('An error occurred while deleting files.');
}
}
function getSelectedFiles() {
return Array.from(document.querySelectorAll('input[name="fileCheckbox"]:checked'))
.map(checkbox => checkbox.value);
}
function deleteSelectedFiles() {
const selectedFiles = getSelectedFiles();
if (selectedFiles.length > 0) {
deleteFiles(selectedFiles);
} else {
alert('No files selected');
}
}
async function downloadSelectedFiles() {
const selectedFiles = getSelectedFiles();
if (selectedFiles.length === 0) {
alert('No files selected');
return;
}
try {
const response = await fetch('/download_files', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ file_paths: selectedFiles })
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(errorData.error || 'Failed to download files.');
}
const blob = await response.blob();
if (blob.type !== 'application/zip') {
throw new Error('Received invalid ZIP file.');
}
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'files.zip';
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('Download error:', error);
alert(`Error: ${error.message}`);
}
}
function sortTable(columnIndex, tableId) {
const table = document.getElementById(tableId);
const tbody = table.querySelector('tbody');
const rows = Array.from(tbody.rows);
const isAscending = table.dataset.sortAsc === 'true';
rows.sort((rowA, rowB) => {
const cellA = rowA.cells[columnIndex].innerText.trim().toLowerCase();
const cellB = rowB.cells[columnIndex].innerText.trim().toLowerCase();
return cellA.localeCompare(cellB) * (isAscending ? 1 : -1);
});
// Toggle sorting order for next click
table.dataset.sortAsc = !isAscending;
// Reinsert sorted rows
rows.forEach(row => tbody.appendChild(row));
}
function checkAllCheckboxes(tableId, checkAllCheckboxId) {
const table = document.getElementById(tableId);
const checkboxes = table.querySelectorAll('input[name="fileCheckbox"]');
const checkAllCheckbox = document.getElementById(checkAllCheckboxId);
checkboxes.forEach(checkbox => checkbox.checked = checkAllCheckbox.checked);
}

144
app/static/index.js Normal file
View File

@@ -0,0 +1,144 @@
class LogScraperApp {
constructor() {
this.form = document.getElementById('scrapingForm');
this.stopButton = document.getElementById('stopButton');
this.logsElement = document.getElementById('logs');
this.prevPageButton = document.getElementById('prevPage');
this.nextPageButton = document.getElementById('nextPage');
this.pageInfo = document.getElementById('pageInfo');
this.startButton = document.getElementById('startButton');
this.currentPage = 0;
this.linesPerPage = null;
this.autoRefreshInterval = null;
this.init();
}
async init() {
await this.fetchConfig();
await this.checkScrapingStatus();
this.addEventListeners();
}
async fetchConfig() {
try {
const response = await fetch('/config/lines_per_page');
const data = await response.json();
this.linesPerPage = data.lines_per_page;
this.fetchLogs(this.currentPage);
} catch (error) {
console.error('Error fetching config:', error);
}
}
async fetchLogs(page) {
try {
const response = await fetch(`/logfile?page=${page}&lines_per_page=${this.linesPerPage}`);
const data = await response.json();
if (data.error) {
this.logsElement.textContent = data.error;
} else {
this.logsElement.innerHTML = data.log.map((line, index) => {
const lineNumber = data.start_line - index;
return `<span class="line-number">${lineNumber}</span> ${line}`;
}).join('');
this.updatePagination(data.total_lines);
}
} catch (error) {
console.error('Error fetching logs:', error);
}
}
updatePagination(totalLines) {
this.prevPageButton.disabled = this.currentPage === 0;
this.nextPageButton.disabled = (this.currentPage + 1) * this.linesPerPage >= totalLines;
this.pageInfo.textContent = `Page ${this.currentPage + 1} of ${Math.ceil(totalLines / this.linesPerPage)}`;
}
startAutoRefresh() {
this.autoRefreshInterval = setInterval(() => this.fetchLogs(this.currentPage), 5000);
}
stopAutoRefresh() {
clearInterval(this.autoRefreshInterval);
}
async checkScrapingStatus() {
try {
const response = await fetch('/scraping_status');
const data = await response.json();
if (data.scraping_active) {
this.startButton.disabled = true;
this.stopButton.disabled = false;
this.startAutoRefresh();
} else {
this.startButton.disabled = false;
this.stopButton.disabled = true;
}
this.fetchLogs(this.currentPage);
} catch (error) {
console.error('Error checking scraping status:', error);
}
}
async startScraping(event) {
event.preventDefault();
const formData = new FormData(this.form);
try {
const response = await fetch('/start_scraping', {
method: 'POST',
body: formData
});
const data = await response.json();
console.log(data);
if (data.status === "Scraping started") {
this.startButton.disabled = true;
this.stopButton.disabled = false;
this.startAutoRefresh();
}
} catch (error) {
console.error('Error starting scraping:', error);
}
}
async stopScraping() {
try {
const response = await fetch('/stop_scraping', { method: 'POST' });
const data = await response.json();
console.log(data);
if (data.status === "Scraping stopped") {
this.startButton.disabled = false;
this.stopButton.disabled = true;
this.stopAutoRefresh();
}
} catch (error) {
console.error('Error stopping scraping:', error);
}
}
addEventListeners() {
this.prevPageButton.addEventListener('click', () => {
if (this.currentPage > 0) {
this.currentPage--;
this.fetchLogs(this.currentPage);
}
});
this.nextPageButton.addEventListener('click', () => {
this.currentPage++;
this.fetchLogs(this.currentPage);
});
this.form.addEventListener('submit', (event) => this.startScraping(event));
this.stopButton.addEventListener('click', () => this.stopScraping());
}
}
// Initialize the application when DOM is fully loaded
document.addEventListener('DOMContentLoaded', () => new LogScraperApp());

217
app/static/style.css Normal file
View File

@@ -0,0 +1,217 @@
/* LIGHT MODE (default) */
:root {
--bs-body-bg: #f8f9fa; /* Light background */
--bs-body-color: #212529; /* Dark text */
--bs-primary: #007bff;
--bs-primary-bg-subtle: #cce5ff;
--bs-primary-border-subtle: #80bdff;
--bs-primary-text-emphasis: #004085;
--bs-secondary: #6c757d;
--bs-secondary-bg-subtle: #e2e3e5;
--bs-secondary-border-subtle: #c8cbcf;
--bs-secondary-text-emphasis: #383d41;
--bs-success: #198754;
--bs-success-bg-subtle: #d4edda;
--bs-success-border-subtle: #a3cfbb;
--bs-success-text-emphasis: #155724;
--bs-danger: #dc3545;
--bs-danger-bg-subtle: #f8d7da;
--bs-danger-border-subtle: #f1aeb5;
--bs-danger-text-emphasis: #721c24;
--bs-warning: #ffc107;
--bs-warning-bg-subtle: #fff3cd;
--bs-warning-border-subtle: #ffeeba;
--bs-warning-text-emphasis: #856404;
--bs-info: #17a2b8;
--bs-info-bg-subtle: #d1ecf1;
--bs-info-border-subtle: #bee5eb;
--bs-info-text-emphasis: #0c5460;
--bs-light: #f8f9fa;
--bs-light-bg-subtle: #ffffff;
--bs-light-border-subtle: #d6d8db;
--bs-light-text-emphasis: #6c757d;
--bs-dark: #343a40;
--bs-dark-bg-subtle: #212529;
--bs-dark-border-subtle: #1d2124;
--bs-dark-text-emphasis: #ffffff;
--bs-border-color: #dee2e6; /* Default border color */
}
/* DARK MODE */
[data-bs-theme="dark"] {
--bs-body-bg: #121212;
--bs-body-color: #e9ecef;
--bs-primary: #1e90ff;
--bs-primary-bg-subtle: #1c2b36;
--bs-primary-border-subtle: #374b58;
--bs-primary-text-emphasis: #a0c4ff;
--bs-secondary: #adb5bd;
--bs-secondary-bg-subtle: #2d3238;
--bs-secondary-border-subtle: #3e444a;
--bs-secondary-text-emphasis: #ced4da;
--bs-success: #00c851;
--bs-success-bg-subtle: #1b3425;
--bs-success-border-subtle: #3b6147;
--bs-success-text-emphasis: #b9f6ca;
--bs-danger: #ff4444;
--bs-danger-bg-subtle: #381717;
--bs-danger-border-subtle: #633030;
--bs-danger-text-emphasis: #ffcccb;
--bs-warning: #ffbb33;
--bs-warning-bg-subtle: #3a2b19;
--bs-warning-border-subtle: #67512e;
--bs-warning-text-emphasis: #ffd700;
--bs-info: #33b5e5;
--bs-info-bg-subtle: #182e38;
--bs-info-border-subtle: #305564;
--bs-info-text-emphasis: #66d1ff;
--bs-light: #343a40;
--bs-light-bg-subtle: #2c3137;
--bs-light-border-subtle: #464b50;
--bs-light-text-emphasis: #e9ecef;
--bs-dark: #ffffff;
--bs-dark-bg-subtle: #f8f9fa;
--bs-dark-border-subtle: #e9ecef;
--bs-dark-text-emphasis: #121212;
--bs-border-color: #495057;
}
[data-bs-theme="dark"] .shadow {
box-shadow: var(--bs-box-shadow) !important;
}
[data-bs-theme="dark"] .shadow-sm {
box-shadow: var(--bs-box-shadow-sm) !important;
}
[data-bs-theme="dark"] .shadow-lg {
box-shadow: var(--bs-box-shadow-lg) !important;
}
:root {
--bs-primary: var(--primary);
--bs-secondary: var(--secondary);
--bs-body-bg: var(--background);
--bs-body-color: var(--text-color);
}
[data-bs-theme="dark"] {
--bs-primary: var(--primary);
--bs-secondary: var(--secondary);
--bs-body-bg: var(--background);
--bs-body-color: var(--text-color);
}
/* Dark Mode Toggle Button */
/* Hide the default checkbox */
#color-mode-toggle input[type=checkbox] {
height: 0;
width: 0;
visibility: hidden;
}
/* Style the switch */
#color-mode-toggle label {
cursor: pointer;
width: 70px;
height: 30px;
background: grey;
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 30px;
position: relative;
padding: 5px 15px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.3);
}
/* The moving toggle circle */
#color-mode-toggle label:after {
content: '';
position: absolute;
top: 5px;
left: 5px;
width: 20px;
height: 20px;
background: white;
border-radius: 50%;
transition: 0.3s;
}
/* Sun and Moon Icons */
.icon {
font-size: 15px;
position: absolute;
transition: 0.3s;
}
/* Position Sun on the left */
.sun {
left: 10px;
/* color: var(--bs-dark) */
color: var(--sun-color);
}
/* Position Moon on the right */
.moon {
right: 10px;
/* color: var(--bs-light); */
color: var(--sun-color);
}
/* Move the toggle circle when checked */
#color-mode-toggle input:checked + label {
background: var(--bs-light);
}
#color-mode-toggle input:checked + label:after {
left: calc(100% - 25px);
background: var(--bs-dark);
}
/* Hide moon when in dark mode */
#color-mode-toggle input:checked + label .sun {
opacity: 100;
}
#color-mode-toggle input:checked + label .moon {
opacity: 0;
}
/* Hide sun when in light mode */
#color-mode-toggle input:not(:checked) + label .moon {
opacity: 100;
}
#color-mode-toggle input:not(:checked) + label .sun {
opacity: 0;
}
.line-number {
display: inline-block;
width: 30px;
text-align: right;
margin-right: 10px;
color: #888;
}