Mateusz Gruszczyński e382fab275 first commit
2025-05-18 19:30:50 +02:00

290 lines
10 KiB
JavaScript

let currentSeriesData = [];
let darkMode = true;
function toggleTheme() {
darkMode = !darkMode;
document.body.classList.toggle('bg-dark', darkMode);
document.body.classList.toggle('text-light', darkMode);
document.querySelector('.theme-switch').className = `bi ${darkMode ? 'bi-moon-stars-fill' : 'bi-sun-fill'} theme-switch`;
document.querySelectorAll('.form-select, .form-control').forEach(el => {
el.classList.toggle('bg-dark', darkMode);
el.classList.toggle('text-light', darkMode);
el.classList.toggle('border-secondary', darkMode);
});
}
async function fetchDatabases() {
const res = await fetch('/databases');
const dbs = await res.json();
const select = document.getElementById('db-select');
select.innerHTML = '<option value="">Wybierz bazę danych...</option>';
dbs.forEach(db => {
const option = document.createElement('option');
option.value = db;
option.textContent = db;
select.appendChild(option);
});
const params = new URLSearchParams(window.location.search);
const dbParam = params.get('db');
if (dbParam && dbs.includes(dbParam)) {
select.value = dbParam;
fetchSeries();
}
}
async function fetchSeries() {
const db = document.getElementById('db-select').value;
if (!db) return;
const params = new URLSearchParams(window.location.search);
params.set('db', db);
window.history.replaceState({}, '', `${location.pathname}?${params}`);
showMessage('Ładowanie serii...', 'info');
const res = await fetch(`/series?db=${db}`);
const series = await res.json();
currentSeriesData = series;
renderSeriesTable(series);
showMessage('', 'info');
}
function renderSeriesTable(series) {
const container = document.getElementById('series-list');
container.innerHTML = '';
if (!Array.isArray(series) || series.length === 0) {
container.innerHTML = `<tr><td colspan="3" class="text-center">Brak serii w wybranej bazie danych</td></tr>`;
return;
}
const baseNames = new Map();
series.forEach(s => {
const entity = s.tags?.entity_id;
if (!entity) return;
const base = entity.replace(/_\d+$/, '');
if (baseNames.has(base)) baseNames.get(base).push(s.series);
else baseNames.set(base, [s.series]);
});
const duplicates = new Set();
for (const [_, entries] of baseNames.entries()) {
if (entries.length > 1) entries.forEach(ser => duplicates.add(ser));
}
series.forEach((s, i) => {
const id = `series-${i}`;
const isDuplicate = duplicates.has(s.series);
const row = document.createElement('tr');
row.className = isDuplicate ? 'table-warning' : '';
row.innerHTML = `
<td><input class="form-check-input" type="checkbox" value="${s.series}" id="${id}" onchange="updateCount()"></td>
<td class="series-item">
<label class="form-check-label" for="${id}">${s.series}</label>
${isDuplicate ? '<div class="text-warning small">Duplikat entity_id</div>' : ''}
</td>
<td>
<button class="btn btn-sm btn-outline-danger me-1" onclick="deleteSingleSeries('${s.series}')"><i class="bi bi-trash"></i></button>
<button class="btn btn-sm btn-outline-warning" onclick="openTimeDeleteModal('${s.series}')"><i class="bi bi-clock-history"></i></button>
</td>
`;
container.appendChild(row);
});
updateCount();
}
function filterSeries() {
const term = document.getElementById('series-filter').value.toLowerCase();
const filtered = currentSeriesData.filter(s => s.series.toLowerCase().includes(term));
renderSeriesTable(filtered);
}
function updateCount() {
const count = document.querySelectorAll('input[type="checkbox"]:checked').length;
document.getElementById('selected-count').innerText = `Zaznaczono: ${count}`;
}
function toggleSelectAll() {
const checkboxes = document.querySelectorAll('input[type="checkbox"]:not(#select-all)');
const selectAll = document.getElementById('select-all').checked;
checkboxes.forEach(checkbox => checkbox.checked = selectAll);
updateCount();
}
function showMessage(message, type) {
const el = document.getElementById('info-message');
el.textContent = message;
el.className = `alert alert-${type} ${message ? 'd-block' : 'd-none'}`;
}
async function deleteSelected() {
const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked:not(#select-all)');
if (checkboxes.length === 0) return showMessage('Nie zaznaczono żadnych serii', 'warning');
const series = Array.from(checkboxes).map(cb => cb.value);
const db = document.getElementById('db-select').value;
const preview = series.map(s => `DROP SERIES FROM "${s.split(',')[0]}" WHERE ...`).join('\n');
document.getElementById('confirmMessage').textContent = `Czy na pewno chcesz usunąć ${series.length} serii?`;
document.getElementById('queryPreview').textContent = preview;
const modal = new bootstrap.Modal(document.getElementById('confirmModal'));
modal.show();
document.getElementById('confirmDeleteBtn').onclick = async () => {
modal.hide();
await deleteSeries(series);
};
}
async function deleteSingleSeries(seriesId) {
document.getElementById('confirmMessage').textContent = `Czy na pewno chcesz usunąć serię:`;
document.getElementById('queryPreview').textContent = `DROP SERIES FROM "${seriesId.split(',')[0]}" WHERE ...`;
const modal = new bootstrap.Modal(document.getElementById('confirmModal'));
modal.show();
document.getElementById('confirmDeleteBtn').onclick = async () => {
modal.hide();
await deleteSeries([seriesId]);
};
}
async function deleteSeries(series) {
const db = document.getElementById('db-select').value;
showMessage('Usuwanie serii...', 'info');
const res = await fetch('/delete', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ db, series })
});
const data = await res.json();
if (data.status === 'success') {
showMessage(`Usunięto ${data.deleted} serii`, 'success');
setTimeout(fetchSeries, 1000);
} else {
showMessage('Wystąpił błąd podczas usuwania serii', 'danger');
}
}
fetchDatabases();
let currentSeriesForTimeDelete = null;
function openTimeDeleteModal(seriesId) {
currentSeriesForTimeDelete = seriesId;
document.getElementById('time-from').value = '';
document.getElementById('time-to').value = '';
document.getElementById('time-query-preview').textContent = '';
const modal = new bootstrap.Modal(document.getElementById('timeDeleteModal'));
modal.show();
}
async function confirmTimeDelete() {
const from = document.getElementById('time-from').value;
const to = document.getElementById('time-to').value;
const db = document.getElementById('db-select').value;
const series = currentSeriesForTimeDelete;
if (!from || !to || !series || !db) {
showMessage('Uzupełnij wszystkie pola i wybierz bazę danych', 'warning');
return;
}
showMessage('Usuwanie danych z serii...', 'info');
const res = await fetch('/delete_range', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ db, series, from, to })
});
const data = await res.json();
if (data.status === 'success') {
showMessage('Dane zostały usunięte', 'success');
setTimeout(fetchSeries, 1000);
} else {
showMessage(`Błąd: ${data.error}`, 'danger');
}
}
function setPredefinedRange(value) {
if (!value) return;
const now = new Date();
const to = now.toISOString().slice(0, 16);
let fromDate = new Date(now);
if (value.endsWith('h')) {
fromDate.setHours(fromDate.getHours() - parseInt(value));
} else if (value.endsWith('d')) {
fromDate.setDate(fromDate.getDate() - parseInt(value));
}
const from = fromDate.toISOString().slice(0, 16);
document.getElementById('time-from').value = from;
document.getElementById('time-to').value = to;
}
function setPredefinedRange(value) {
if (!value) return;
const now = new Date();
const to = now.toISOString().slice(0, 16);
let fromDate = new Date(now);
if (value.endsWith('h')) {
fromDate.setHours(fromDate.getHours() - parseInt(value));
} else if (value.endsWith('d')) {
fromDate.setDate(fromDate.getDate() - parseInt(value));
} else if (value === 'before-current-year') {
const start = new Date(now.getFullYear(), 0, 1);
document.getElementById('time-from').value = '2000-01-01T00:00';
document.getElementById('time-to').value = start.toISOString().slice(0, 16);
return;
} else if (value === 'keep-2y') {
fromDate.setFullYear(fromDate.getFullYear() - 2);
} else if (value === 'keep-3y') {
fromDate.setFullYear(fromDate.getFullYear() - 3);
}
const from = fromDate.toISOString().slice(0, 16);
document.getElementById('time-from').value = from;
document.getElementById('time-to').value = to;
}
async function deleteRangeForSelected() {
const checkboxes = document.querySelectorAll('input[type="checkbox"]:checked:not(#select-all)');
if (checkboxes.length === 0) {
showMessage('Zaznacz przynajmniej jedną serię', 'warning');
return;
}
const fromInput = document.getElementById('time-from').value;
const toInput = document.getElementById('time-to').value;
const db = document.getElementById('db-select').value;
if (!fromInput || !toInput || !db) {
showMessage('Wybierz zakres czasu i bazę danych', 'warning');
return;
}
const from = new Date(fromInput).toISOString();
const to = new Date(toInput).toISOString();
const series = Array.from(checkboxes).map(cb => cb.value);
showMessage('Usuwanie danych z wielu serii...', 'info');
for (const s of series) {
await fetch('/delete_range', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ db, series: s, from, to })
});
}
showMessage('Dane zostały usunięte', 'success');
setTimeout(fetchSeries, 1000);
}
function openTimeDeleteModalForMany() {
currentSeriesForTimeDelete = null; // żeby nie używać pojedynczej serii
document.getElementById('time-from').value = '';
document.getElementById('time-to').value = '';
document.getElementById('time-query-preview').textContent = '';
const modal = new bootstrap.Modal(document.getElementById('timeDeleteModal'));
modal.show();
}