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 = ''; 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 = `Brak serii w wybranej bazie danych`; 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 = ` ${isDuplicate ? '
Duplikat entity_id
' : ''} `; 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(); }