Files
mario_scraper/templates/index.html
2025-04-17 13:56:40 +03:00

1098 lines
41 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<title>Парсер mariotexno</title>
<style>
:root {
/* Catppuccin Mocha */
--rosewater: #f5e0dc;
--flamingo: #f2cdcd;
--pink: #f5c2e7;
--mauve: #cba6f7;
--red: #f38ba8;
--maroon: #eba0ac;
--peach: #fab387;
--yellow: #f9e2af;
--green: #a6e3a1;
--teal: #94e2d5;
--sky: #89dceb;
--sapphire: #74c7ec;
--blue: #89b4fa;
--lavender: #b4befe;
--text: #cdd6f4;
--subtext1: #bac2de;
--subtext0: #a6adc8;
--overlay2: #9399b2;
--overlay1: #7f849c;
--overlay0: #6c7086;
--surface2: #585b70;
--surface1: #45475a;
--surface0: #313244;
--base: #1e1e2e;
--mantle: #181825;
--crust: #11111b;
}
* {
box-sizing: border-box;
}
body {
font-family: 'Inter', sans-serif;
max-width: 800px;
margin: 0 auto;
padding: 20px;
color: var(--text);
background: var(--base);
}
h1,
h2,
h3 {
color: var(--text);
margin-bottom: 1.5rem;
}
.form-group {
margin-bottom: 1.5rem;
}
input[type="text"],
input[type="number"] {
width: 100%;
padding: 0.75rem;
margin-top: 0.5rem;
border: 1px solid var(--surface1);
border-radius: 8px;
font-size: 1rem;
background: var(--surface0);
color: var(--text);
transition: border-color 0.2s;
}
input[type="text"]:focus,
input[type="number"]:focus {
outline: none;
border-color: var(--blue);
}
/* Убираем стрелки для input number */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
input[type="number"] {
-moz-appearance: textfield;
}
button {
padding: 0.75rem 1.5rem;
background-color: var(--blue);
color: var(--base);
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
transition: all 0.2s;
}
button:hover {
background-color: var(--lavender);
}
button:disabled {
background-color: var(--overlay0);
cursor: not-allowed;
}
.status {
margin: 1.5rem 0;
padding: 1rem;
border: 1px solid var(--surface1);
border-radius: 8px;
background-color: var(--surface0);
}
.error {
color: var(--red);
}
.files {
margin-top: 2rem;
}
.files ul {
list-style: none;
padding: 0;
}
.files li {
margin: 0.75rem 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 1rem;
border: 1px solid var(--surface1);
border-radius: 8px;
transition: all 0.2s;
background: var(--surface0);
}
.files li:hover {
background-color: var(--surface1);
}
.file-info {
display: flex;
align-items: center;
justify-content: flex-end;
gap: 1.5rem;
width: 100%;
}
.download-link {
color: var(--text);
text-decoration: none;
position: relative;
padding-left: 2rem;
margin-right: auto;
font-weight: 500;
transition: color 0.2s;
}
.download-link:hover {
color: var(--blue);
}
.download-link::before {
content: "📥";
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
}
.file-date,
.file-size {
color: var(--subtext0);
font-size: 0.9rem;
}
.delete-btn {
background: none;
border: none;
color: var(--red);
cursor: pointer;
padding: 0.5rem;
font-size: 1.2rem;
transition: all 0.2s;
}
.delete-btn:hover {
color: var(--maroon);
}
/* Стилі для вкладок */
.tabs {
margin-bottom: 20px;
}
.tab-buttons {
display: flex;
gap: 10px;
margin-bottom: 20px;
align-items: center;
justify-content: flex-start;
}
.tab-button {
height: 44px;
padding: 0 24px;
background-color: var(--surface0);
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 14px;
color: var(--subtext0);
transition: all 0.2s;
}
.tab-button:hover {
background-color: var(--surface1);
color: var(--text);
}
.tab-button.active {
background-color: var(--blue);
color: var(--base);
}
.tab-content {
display: none;
background: var(--surface0);
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
.tab-content.active {
display: block;
}
.file-select {
width: 100%;
padding: 0.75rem;
margin-top: 0.5rem;
border: 1px solid var(--surface1);
border-radius: 8px;
font-size: 1rem;
background: var(--surface0);
color: var(--text);
transition: border-color 0.2s;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23cdd6f4' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 0.75rem center;
background-size: 1em;
}
.file-select:focus {
outline: none;
border-color: var(--blue);
}
.items-limit-group {
display: flex;
align-items: center;
gap: 0.5rem;
margin-left: auto;
height: 44px;
}
.items-limit-group label {
color: var(--subtext0);
font-size: 14px;
}
#items-limit {
margin: 0;
text-align: center;
max-width: 4rem;
}
.items-limit-group input[type="number"] {
height: 44px;
width: 4rem;
padding: 0 0.75rem;
flex-shrink: 0;
text-align: center;
}
.categories-section {
margin-bottom: 2rem;
background: var(--surface0);
border-radius: 8px;
}
.category-form {
display: flex;
gap: 1rem;
align-items: flex-end;
margin-bottom: 1rem;
}
.category-form .form-group {
margin-bottom: 0;
}
.category-form button {
flex: 0 0 auto;
}
.secondary-button {
background-color: var(--surface1);
color: var(--text);
}
.secondary-button:hover {
background-color: var(--surface2);
}
.categories-list ul {
list-style: none;
padding: 0;
}
.categories-list li {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0.5rem;
margin: 0.5rem 0;
background: var(--surface1);
border-radius: 4px;
}
.yml-generation-section {
margin-bottom: 2rem;
background: var(--surface0);
border-radius: 8px;
}
.loading {
position: relative;
pointer-events: none;
opacity: 0.7;
}
.loading::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
width: 20px;
height: 20px;
margin: -10px 0 0 -10px;
border: 2px solid var(--blue);
border-top-color: transparent;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
to { transform: rotate(360deg); }
}
.error-message {
position: fixed;
top: 20px;
right: 20px;
padding: 1rem;
background: var(--red);
color: var(--base);
border-radius: 8px;
animation: slideIn 0.3s ease-out;
}
@keyframes slideIn {
from { transform: translateX(100%); }
to { transform: translateX(0); }
}
</style>
</head>
<body>
<h1>Парсер euro.com.pl</h1>
<div class="tabs">
<div class="tab-buttons">
<button class="tab-button active" onclick="openTab('parser')">Парсер</button>
<button class="tab-button" onclick="openTab('processor')">Переклад</button>
<button class="tab-button" onclick="openTab('generator')">YML Фід</button>
<button class="tab-button" onclick="openTab('categories')">Категорії</button>
<div class="items-limit-group">
<label for="items-limit">Ліміт:</label>
<input type="number" id="items-limit" value="{{ app_settings.items_limit }}" min="-1"
onchange="updateItemsLimit(this.value)" title="Встановіть -1 для зняття обмежень">
</div>
<button onclick="refreshOldestCategory()">🔄</button>
</div>
<!-- Вкладка парсера -->
<div id="parser" class="tab-content active">
<div class="form-group">
<label for="url">URL категорії:</label>
<input type="text" id="url" placeholder="https://www.euro.com.pl/odkurzacze-automatyczne.bhtml">
</div>
<button onclick="startParsing()" id="parseButton">Почати парсинг</button>
<div class="status" id="status">
{% if status.is_running %}
<p>Парсинг запущено для категорії: {{ status.current_category }}</p>
{% else %}
<p>Парсер не активний</p>
{% endif %}
</div>
<div class="files">
<h3>Збережені результати:</h3>
<ul>
{% for file in parsed_files %}
<li>
<div class="file-info">
<a href="{{ url_for('download_file', filename=file.name, directory='output') }}"
class="download-link" download>
{{ file.name }}
</a>
<span class="file-date">{{ file.modified }}</span>
<span class="file-size">{{ file.size }}</span>
</div>
<button class="delete-btn" onclick="deleteFile('{{ file.name }}', 'output')"
title="Видалити файл">
🗑️
</button>
</li>
{% endfor %}
</ul>
</div>
</div>
<!-- Вкладка оробки даних -->
<div id="processor" class="tab-content">
<div class="form-group">
<label for="file-select">Виберіть файл для обробки:</label>
<select id="file-select" class="file-select">
<option value="">Виберіть файл...</option>
{% for file in parsed_files %}
<option value="{{ file.name }}">{{ file.name }}</option>
{% endfor %}
</select>
</div>
<button onclick="startTranslation()" id="translateButton">Почати переклад</button>
<div class="status" id="translation-status">
{% if translation_status.is_running %}
<p>Переклад в процесі...<br>
Оброблено: {{ translation_status.processed_items }} з {{ translation_status.total_items }}</p>
{% else %}
<p>Переклад не активний</p>
{% endif %}
</div>
<div class="files">
<h3>Перекладені файли:</h3>
<ul>
{% for file in translated_files %}
<li>
<div class="file-info">
<a href="{{ url_for('download_file', filename=file.name, directory='translated') }}"
class="download-link" download>
{{ file.name }}
</a>
<span class="file-date">{{ file.modified }}</span>
<span class="file-size">{{ file.size }}</span>
</div>
<button class="delete-btn" onclick="deleteFile('{{ file.name }}', 'translated')"
title="Видалити файл">
🗑️
</button>
</li>
{% endfor %}
</ul>
</div>
</div>
<!-- Вкладка генератора YML -->
<div id="generator" class="tab-content">
<div class="yml-generation-section">
<div class="form-group">
<label for="yml-category-select">Категорія:</label>
<select id="yml-category-select" class="file-select">
<option value="">Виберіть категорію...</option>
{% for category in categories %}
<option value="{{ category.id }}">{{ category.name }}</option>
{% endfor %}
</select>
</div>
<div class="form-group">
<label for="yml-file-select">Файл для генерації:</label>
<select id="yml-file-select" class="file-select">
<option value="">Виберіть файл...</option>
{% for file in translated_files %}
<option value="{{ file.name }}">{{ file.name }}</option>
{% endfor %}
</select>
</div>
<button onclick="generateYML()" id="generateButton" class="action-button">Згенерувати YML</button>
</div>
<div class="status" id="yml-status">
<p>Генератор не активний</p>
</div>
<div class="files">
<h3>Згенеровані YML файли:</h3>
<ul>
{% for file in yml_files %}
<li>
<div class="file-info">
<a href="{{ url_for('download_file', filename=file.name|urlencode, directory='yml') }}"
class="download-link" download>
{{ file.name }}
</a>
<span class="file-date">{{ file.modified }}</span>
<span class="file-size">{{ file.size }}</span>
</div>
<button class="delete-btn" onclick="deleteFile('{{ file.name }}', 'yml')"
title="Видалити файл">
🗑️
</button>
</li>
{% endfor %}
</ul>
</div>
</div>
<!-- Вкладка управления категориями -->
<div id="categories" class="tab-content">
<div class="categories-section">
<div class="category-form">
<div class="form-group">
<label for="category-id">ID категорії:</label>
<input type="text" id="category-id" placeholder="1">
</div>
<div class="form-group">
<label for="category-name">Назва категорії:</label>
<input type="text" id="category-name" placeholder="Роботи-пилососи">
</div>
<div class="form-group">
<label for="portal-id">ID категорії Prom.ua:</label>
<input type="text" id="portal-id" placeholder="12345">
</div>
<button onclick="addCategory()" class="secondary-button">Додати категорію</button>
</div>
<div class="categories-list">
<h4>Збережені категорії:</h4>
<ul id="categories-list">
{% for category in categories %}
<li>
<span>{{ category.id }} - {{ category.name }} {% if category.portal_id %} (portal_id: {{ category.portal_id }}){% endif %}</span>
<button class="delete-btn" onclick="deleteCategory('{{ category.id }}')" title="Видалити категорію">
🗑️
</button>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/BIHPThkBvs0OEvtFFmqPF/lYI/Cxo=" crossorigin="anonymous"></script>
<script>
let statusCheckInterval;
function startStatusCheck() {
// Очищаем предыдущий интервал, если он существует
if (statusCheckInterval) {
clearInterval(statusCheckInterval);
}
// Проверяем статус каждые 500мс
statusCheckInterval = setInterval(checkStatus, 500);
// Сразу делаем первую проверку
checkStatus();
}
function checkStatus() {
fetch('/status')
.then(response => response.json())
.then(data => {
const status = document.getElementById('status');
const button = document.getElementById('parseButton');
if (data.is_running) {
let statusHtml = `
<p>
Парсинг запущено для категорії: ${data.current_category}<br>
`;
if (data.total_products > 0) {
statusHtml += `
<strong>Товар ${data.current_product} із ${data.total_products}</strong><br>
Прогрес: ${data.progress}%
`;
} else {
statusHtml += `Отримання інформації про кількість товарів...`;
}
statusHtml += `</p>`;
status.innerHTML = statusHtml;
setTimeout(checkStatus, 500);
} else {
if (data.error) {
status.innerHTML = `<p class="error">Помилка: ${data.error}</p>`;
} else if (data.total_products > 0) {
status.innerHTML = `
<p>
Парсинг завершено<br>
<strong>Всього оброблено товарів: ${data.total_products}</strong>
</p>
`;
updateFilesList('parsed', 'parser', 'file-select');
}
button.disabled = false;
}
})
.catch(error => {
console.error('Status check error:', error);
status.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
button.disabled = false;
});
}
function stopStatusCheck() {
if (statusCheckInterval) {
clearInterval(statusCheckInterval);
statusCheckInterval = null;
}
}
function deleteFile(filename, type) {
if (confirm('Ви впевнені, що хочете видалити цей файл?')) {
fetch(`/delete/${filename}`, {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
} else {
// Удаляем файл из списка
const fileItem = document.querySelector(`a[href*="${filename}"]`).closest('li');
fileItem.remove();
// Обновляем соответствующий select в зависимости от типа файла
if (type === 'parsed') {
const fileSelect = document.getElementById('file-select');
const optionToRemove = Array.from(fileSelect.options).find(opt => opt.value === filename);
if (optionToRemove) {
optionToRemove.remove();
}
} else if (type === 'translated') {
const ymlFileSelect = document.getElementById('yml-file-select');
const optionToRemove = Array.from(ymlFileSelect.options).find(opt => opt.value === filename);
if (optionToRemove) {
optionToRemove.remove();
}
}
}
})
.catch(error => {
console.error('Error:', error);
alert('Помилка при видаленні файлу');
});
}
}
function openTab(tabName) {
// Приховуємо всі вкладки
const tabContents = document.getElementsByClassName('tab-content');
for (let content of tabContents) {
content.classList.remove('active');
}
// Деактивуємо всі кнопки
const tabButtons = document.getElementsByClassName('tab-button');
for (let button of tabButtons) {
button.classList.remove('active');
}
// Показуємо вибрану вкладку
document.getElementById(tabName).classList.add('active');
// Активуємо потрібну кнопку
event.currentTarget.classList.add('active');
}
function startTranslation() {
const fileSelect = document.getElementById('file-select');
const button = document.getElementById('translateButton');
const status = document.getElementById('translation-status');
if (!fileSelect.value) {
status.innerHTML = '<p class="error">Будь ласка, виберіть файл для обробки</p>';
return;
}
button.disabled = true;
status.innerHTML = '<p>Починаємо переклад...</p>';
fetch('/translate', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `filename=${encodeURIComponent(fileSelect.value)}`
})
.then(response => response.json())
.then(data => {
if (data.error) {
status.innerHTML = `<p class="error">${data.error}</p>`;
button.disabled = false;
} else {
checkTranslationStatus();
}
});
}
function checkTranslationStatus() {
fetch('/translation-status')
.then(response => response.json())
.then(data => {
const status = document.getElementById('translation-status');
const button = document.getElementById('translateButton');
if (data.is_running) {
if (data.total_items > 0) {
const percent = Math.round((data.processed_items / data.total_items) * 100);
status.innerHTML = `
<p>
Переклад в процесі...<br>
Оброблено: ${data.processed_items} з ${data.total_items}<br>
Прогрес: ${percent}%
</p>
`;
} else {
status.innerHTML = '<p>Підготовка до перекладу...</p>';
}
setTimeout(checkTranslationStatus, 1000);
} else {
if (data.error) {
status.innerHTML = `<p class="error">Помилка: ${data.error}</p>`;
} else if (data.total_items > 0) {
status.innerHTML = `
<p>
Переклад завершено<br>
Всього оброблено товарів: ${data.total_items}
</p>
`;
updateFilesList('translated', 'processor', 'yml-file-select');
}
button.disabled = false;
}
})
.catch(error => {
console.error('Translation status check error:', error);
status.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
document.getElementById('translateButton').disabled = false;
});
}
function updateItemsLimit(value) {
fetch('/update-settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
items_limit: parseInt(value)
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
}
});
}
function addCategory() {
const categoryId = document.getElementById('category-id').value;
const categoryName = document.getElementById('category-name').value;
const portalId = document.getElementById('portal-id').value;
if (!categoryId || !categoryName) {
alert('Будь ласка, заповніть всі поля');
return;
}
fetch('/add-category', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: categoryId,
name: categoryName,
portal_id: portalId
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
} else {
// Очищаем поля ввода
document.getElementById('category-id').value = '';
document.getElementById('category-name').value = '';
document.getElementById('portal-id').value = '';
// Добавляем новую категорию в список
const categoriesList = document.getElementById('categories-list');
const li = document.createElement('li');
li.innerHTML = `
<span>${categoryId} - ${categoryName}</span>
<button class="delete-btn" onclick="deleteCategory('${categoryId}')" title="Видалити категорію">
</button>
`;
categoriesList.appendChild(li);
// Обновляем select в генераторе YML
const ymlSelect = document.getElementById('yml-category-select');
const option = document.createElement('option');
option.value = categoryId;
option.text = categoryName;
ymlSelect.appendChild(option);
}
});
}
function deleteCategory(categoryId) {
if (confirm('Ви впевнені, що хочете видалити цю категорію?')) {
fetch('/delete-category', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
id: categoryId
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
alert(data.error);
} else {
// Удаляем категорию из списка
const categoryItems = document.querySelectorAll('#categories-list li');
categoryItems.forEach(item => {
if (item.querySelector('span').textContent.startsWith(categoryId + ' -')) {
item.remove();
}
});
// Удаляем опцию из select в генераторе YML
const ymlSelect = document.getElementById('yml-category-select');
const option = Array.from(ymlSelect.options).find(opt => opt.value === categoryId);
if (option) option.remove();
}
});
}
}
function generateYML() {
console.log('generateYML called'); // Отладка
const categoryId = document.getElementById('yml-category-select').value;
const fileSelect = document.getElementById('yml-file-select');
console.log('Selected category:', categoryId); // Отладка
console.log('Selected file:', fileSelect.value); // Отладка
const button = document.getElementById('generateButton');
const status = document.getElementById('yml-status');
if (!categoryId || !fileSelect.value) {
status.innerHTML = '<p class="error">Будь ласка, виберіть категорію та файл</p>';
return;
}
button.disabled = true;
status.innerHTML = '<p>Генерація YML...</p>';
fetch('/generate-yml', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
filename: fileSelect.value,
category_id: categoryId
})
})
.then(response => response.json())
.then(data => {
if (data.error) {
status.innerHTML = `<p class="error">${data.error}</p>`;
} else {
status.innerHTML = '<p>YML файл успішно згенеровано</p>';
updateFilesList('yml', 'generator', null);
}
button.disabled = false;
})
.catch(error => {
status.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
button.disabled = false;
});
}
// Добавляем вспомогательную функцию для поиска по тексту
jQuery.expr[':'].contains = function(a, i, m) {
return jQuery(a).text().toUpperCase()
.indexOf(m[3].toUpperCase()) >= 0;
};
function updateFilesList(fileType, containerId, selectId = null) {
fetch(`/get-files/${fileType}`)
.then(response => response.json())
.then(files => {
// Обновляем список файлов
const filesList = document.querySelector(`#${containerId} .files ul`);
filesList.innerHTML = files.map(file => `
<li>
<div class="file-info">
<a href="/download/${file.name}" class="download-link" download>
${file.name}
</a>
<span class="file-date">${file.modified}</span>
<span class="file-size">${file.size}</span>
</div>
<button class="delete-btn" onclick="deleteFile('${file.name}', '${fileType}')" title="Видалити файл">
🗑️
</button>
</li>
`).join('');
// Обновляем select, если указан
if (selectId) {
const select = document.getElementById(selectId);
select.innerHTML = '<option value="">Виберіть файл...</option>' +
files.map(file => `
<option value="${file.name}">${file.name}</option>
`).join('');
}
});
}
function showLoader(element) {
element.classList.add('loading');
}
function hideLoader(element) {
element.classList.remove('loading');
}
function showError(message) {
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.textContent = message;
document.body.appendChild(errorDiv);
setTimeout(() => errorDiv.remove(), 3000);
}
// Использование:
async function someAction() {
const element = document.getElementById('someElement');
showLoader(element);
try {
const response = await fetch('/some-endpoint');
const data = await response.json();
if (data.error) throw new Error(data.error);
// обработка успешного ответа
} catch (error) {
showError(error.message);
} finally {
hideLoader(element);
}
}
function startParsing() {
const url = document.getElementById('url').value;
const button = document.getElementById('parseButton');
const status = document.getElementById('status');
if (!url) {
status.innerHTML = '<p class="error">Будь ласка, введіть URL</p>';
return;
}
button.disabled = true;
status.innerHTML = '<p>Починаємо парсинг...</p>';
fetch('/parse', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `url=${encodeURIComponent(url)}`
})
.then(response => response.json())
.then(data => {
if (data.error) {
status.innerHTML = `<p class="error">${data.error}</p>`;
button.disabled = false;
} else {
checkParsingStatus();
}
})
.catch(error => {
status.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
button.disabled = false;
});
}
function checkParsingStatus() {
fetch('/status')
.then(response => response.json())
.then(data => {
const status = document.getElementById('status');
const button = document.getElementById('parseButton');
if (data.is_running) {
if (data.total_items > 0) {
const percent = Math.round((data.processed_items / data.total_items) * 100);
status.innerHTML = `
<p>
Парсинг в процесі...<br>
Оброблено: ${data.processed_items} з ${data.total_items}<br>
Прогрес: ${percent}%
</p>
`;
} else {
status.innerHTML = '<p>Отримання інформації про товари...</p>';
}
setTimeout(checkParsingStatus, 1000);
} else {
if (data.error) {
status.innerHTML = `<p class="error">Помилка: ${data.error}</p>`;
} else {
status.innerHTML = `
<p>
Парсинг завершено<br>
Всього оброблено товарів: ${data.total_items}
</p>
`;
updateFilesList('parsed', 'parser', 'file-select');
}
button.disabled = false;
}
})
.catch(error => {
console.error('Status check error:', error);
status.innerHTML = `<p class="error">Помилка: ${error.message}</p>`;
document.getElementById('parseButton').disabled = false;
});
}
function refreshOldestCategory() {
fetch('/auto-refresh', {
method: 'POST'
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert(`Оновлено: ${data.category}`);
} else {
alert(data.error || 'Помилка під час оновлення');
}
});
}
</script>
</body>
</html>