poprawki i optymalizacje kodu

This commit is contained in:
Mateusz Gruszczyński
2026-02-05 09:06:01 +01:00
parent c0f82fa3f3
commit 0910b4ddb4
7 changed files with 381 additions and 63 deletions

20
static/js/gauge.js Normal file
View File

@@ -0,0 +1,20 @@
function setupGauges() {
const config = {
type: 'doughnut',
data: { datasets: [{ data: [0, 100], backgroundColor: ['#198754', '#1a1d20'], borderWidth: 0, circumference: 180, rotation: 270 }] },
options: { responsive: true, maintainAspectRatio: true, cutout: '75%', plugins: { legend: { display: false }, tooltip: { enabled: false } } }
};
Object.keys(phasesConfig).forEach(id => {
const canvas = document.getElementById('gauge' + id);
if (canvas) gauges[id] = new Chart(canvas, JSON.parse(JSON.stringify(config)));
});
}
function updateGaugeUI(id, val) {
if (!gauges[id]) return;
const pct = Math.max(0, Math.min(100, ((val - 190) / 80) * 100));
let color = (val < THRESHOLDS.min || val > THRESHOLDS.max) ? '#dc3545' : ((val < 212 || val > 248) ? '#ffc107' : '#198754');
gauges[id].data.datasets[0].data = [pct, 100 - pct];
gauges[id].data.datasets[0].backgroundColor = [color, '#1a1d20'];
gauges[id].update('none');
}

96
static/js/modal.js Normal file
View File

@@ -0,0 +1,96 @@
/**
* Otwiera modal z wyborem własnego zakresu dat
*/
function openCustomRangePicker() {
const modal = document.getElementById('customRangeModal');
if (!modal) return;
// Ustaw domyślne wartości - od 24h temu do teraz
const now = new Date();
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
document.getElementById('customStartDate').value = formatDateTimeLocal(yesterday);
document.getElementById('customEndDate').value = formatDateTimeLocal(now);
modal.style.display = 'flex';
}
/**
* Zamyka modal z wyborem zakresu
*/
function closeCustomRangePicker() {
const modal = document.getElementById('customRangeModal');
if (modal) modal.style.display = 'none';
}
/**
* Stosuje wybrany własny zakres
*/
async function applyCustomRange() {
const startInput = document.getElementById('customStartDate');
const endInput = document.getElementById('customEndDate');
if (!startInput.value || !endInput.value) {
alert('Wybierz obie daty');
return;
}
const startDate = new Date(startInput.value);
const endDate = new Date(endInput.value);
const now = new Date();
// Walidacja
if (startDate >= endDate) {
alert('Data początkowa musi być wcześniejsza niż końcowa');
return;
}
if (endDate > now) {
alert('Data końcowa nie może być w przyszłości');
return;
}
const diffDays = (endDate - startDate) / (1000 * 60 * 60 * 24);
if (diffDays > 30) {
alert('Maksymalny zakres to 30 dni');
return;
}
closeCustomRangePicker();
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
document.getElementById('customRangeBtn').classList.add('active');
currentTimeRange = 'custom';
const startTime = startDate.getTime();
const endTime = endDate.getTime();
await reloadDataForRange(startTime, endTime);
if (voltageChart) {
voltageChart.options.scales.x.min = startTime;
voltageChart.options.scales.x.max = endTime;
voltageChart.update('none');
}
}
/**
* Formatuje datę do formatu datetime-local input
*/
function formatDateTimeLocal(date) {
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return `${year}-${month}-${day}T${hours}:${minutes}`;
}
// Zamknij modal po kliknięciu poza nim
document.addEventListener('click', function(event) {
const modal = document.getElementById('customRangeModal');
if (modal && event.target === modal) {
closeCustomRangePicker();
}
});

View File

@@ -34,6 +34,7 @@ function initMonitor(phases, defaultRange) {
window.changeTimeRange(currentTimeRange);
}
/**
* Konfiguracja wykresu głównego (Zoom + Pan)
*/
@@ -54,27 +55,47 @@ function setupMainChart() {
},
scales: {
x: {
type: 'time',
time: {
displayFormats: { hour: 'HH:mm', minute: 'HH:mm' },
tooltipFormat: 'yyyy-MM-dd HH:mm:ss'
},
type: 'time',
time: {
displayFormats: {
millisecond: 'HH:mm:ss.SSS',
second: 'HH:mm:ss',
minute: 'HH:mm',
hour: 'HH:mm',
day: 'dd LLL',
week: 'dd LLL',
month: 'LLL yyyy',
quarter: 'LLL yyyy',
year: 'yyyy'
},
tooltipFormat: 'dd.MM.yyyy HH:mm:ss'
},
grid: { color: '#2d3139' },
ticks: { color: '#8b949e' }
},
y: {
beginAtZero: false,
suggestedMin: 200,
suggestedMax: 250,
beginAtZero: false,
min: 190,
max: 255,
grid: { color: '#2d3139' },
ticks: { stepSize: 5, color: '#c9d1d9' }
ticks: {
stepSize: 5,
color: '#c9d1d9'
}
}
},
plugins: {
zoom: {
limits: {
x: {
rangeMax: 30 * 24 * 60 * 60 * 1000
min: 'original',
max: () => Date.now(),
minRange: 60 * 1000
},
y: {
min: 190,
max: 255
}
},
zoom: {
@@ -86,15 +107,30 @@ function setupMainChart() {
},
mode: 'x',
onZoomComplete: async ({chart}) => {
const now = Date.now();
if (chart.scales.x.max > now) {
chart.scales.x.max = now;
chart.update('none');
}
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
currentTimeRange = 'precise';
await reloadDataForRange(chart.scales.x.min, chart.scales.x.max);
}
},
pan: {
enabled: true,
mode: 'x',
mode: 'x',
onPanComplete: async ({chart}) => {
const now = Date.now();
if (chart.scales.x.max > now) {
const rangeWidth = chart.scales.x.max - chart.scales.x.min;
chart.scales.x.max = now;
chart.scales.x.min = now - rangeWidth;
chart.update('none');
}
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
currentTimeRange = 'precise';
await reloadDataForRange(chart.scales.x.min, chart.scales.x.max);
@@ -116,7 +152,7 @@ function setupMainChart() {
const raw = ctx.raw;
const val = (raw && raw.realV !== undefined) ? raw.realV : ctx.parsed.y;
if (datasetLabel.includes('Zanik')) return `ZANIK FAZY`;
if (datasetLabel.includes('Zanik')) return 'ZANIK FAZY';
if (datasetLabel.includes('Powrot')) return `POWROT: ${val.toFixed(1)}V`;
if (datasetLabel.includes('Awaria')) return null;
return `${datasetLabel}: ${val.toFixed(1)}V`;
@@ -126,67 +162,88 @@ function setupMainChart() {
}
}
});
}
/**
* Pobieranie danych i gotowych eventów z backendu
*/
async function reloadDataForRange(min, max, rangeName = null) {
let urlParams = rangeName
? `range=${rangeName}`
: `start=${new Date(min).toISOString()}&end=${new Date(max).toISOString()}`;
const now = Date.now();
if (max && max > now) {
max = now;
}
if (min && min > now) {
min = now - 3600000;
}
let urlParams = rangeName ? `range=${rangeName}` : `start=${new Date(min).toISOString()}&end=${new Date(max).toISOString()}`;
const newDatasets = [];
for (let id of Object.keys(phasesConfig)) {
try {
const raw = await fetch(`/api/timeseries/${id}?${urlParams}`).then(r => r.json());
const proc = processPhaseData(id, raw);
newDatasets.push(proc.lineDataset);
if (proc.outageLine) newDatasets.push(proc.outageLine);
if (proc.outageLine) newDatasets.push(proc.outageLine);
if (proc.outageDataset) newDatasets.push(proc.outageDataset);
if (proc.recoveryDataset) newDatasets.push(proc.recoveryDataset);
} catch (e) {
console.error("Błąd pobierania fazy " + id, e);
} catch (e) {
console.error("Błąd pobierania fazy " + id, e);
}
}
try {
const events = await fetch(`/api/events?${urlParams}`).then(r => r.json());
renderEventLog(events, rangeName || 'precise');
} catch (e) {
console.error("Błąd pobierania zdarzeń", e);
} catch (e) {
console.error("Błąd pobierania zdarzeń", e);
}
voltageChart.data.datasets = newDatasets;
if (rangeName) {
voltageChart.update();
voltageChart.update();
} else {
voltageChart.update('none');
}
const finalMin = rangeName ? voltageChart.scales.x.min : (min || voltageChart.scales.x.min);
const finalMax = rangeName ? voltageChart.scales.x.max : (max || voltageChart.scales.x.max);
updateRangeLabel(finalMin, finalMax);
}
/**
* Dynamiczna aktualizacja napisu zakresu czasu nad logami
*/
function updateRangeLabel(min, max) {
const label = document.getElementById('eventRangeLabel');
if (!label) return;
const start = new Date(min);
const end = new Date(max);
const optTime = { hour: '2-digit', minute: '2-digit', hour12: false };
const optDate = { day: '2-digit', month: '2-digit' };
let rangeText = '';
if (start.toDateString() === end.toDateString()) {
label.textContent = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)}, ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleTimeString('pl-PL', optTime)}`;
rangeText = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)}, ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleTimeString('pl-PL', optTime)}`;
} else {
label.textContent = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleDateString('pl-PL', optDate)} ${end.toLocaleTimeString('pl-PL', optTime)}`;
rangeText = `Zakres: ${start.toLocaleDateString('pl-PL', optDate)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleDateString('pl-PL', optDate)} ${end.toLocaleTimeString('pl-PL', optTime)}`;
}
// Aktualizuj label nad logami
const label = document.getElementById('eventRangeLabel');
if (label) {
label.textContent = rangeText;
}
const chartDisplay = document.getElementById('chartRangeDisplay');
if (chartDisplay) {
const optDateShort = { day: '2-digit', month: '2-digit' };
if (start.toDateString() === end.toDateString()) {
chartDisplay.textContent = `${start.toLocaleDateString('pl-PL', optDateShort)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleTimeString('pl-PL', optTime)}`;
} else {
chartDisplay.textContent = `${start.toLocaleDateString('pl-PL', optDateShort)} ${start.toLocaleTimeString('pl-PL', optTime)} - ${end.toLocaleDateString('pl-PL', optDateShort)} ${end.toLocaleTimeString('pl-PL', optTime)}`;
}
}
}
@@ -332,26 +389,4 @@ window.showEventOnChart = function(startTimeStr) {
currentTimeRange = 'precise';
reloadDataForRange(min, max);
}
};
function setupGauges() {
const config = {
type: 'doughnut',
data: { datasets: [{ data: [0, 100], backgroundColor: ['#198754', '#1a1d20'], borderWidth: 0, circumference: 180, rotation: 270 }] },
options: { responsive: true, maintainAspectRatio: true, cutout: '75%', plugins: { legend: { display: false }, tooltip: { enabled: false } } }
};
Object.keys(phasesConfig).forEach(id => {
const canvas = document.getElementById('gauge' + id);
if (canvas) gauges[id] = new Chart(canvas, JSON.parse(JSON.stringify(config)));
});
}
function updateGaugeUI(id, val) {
if (!gauges[id]) return;
const pct = Math.max(0, Math.min(100, ((val - 190) / 80) * 100));
let color = (val < THRESHOLDS.min || val > THRESHOLDS.max) ? '#dc3545' : ((val < 212 || val > 248) ? '#ffc107' : '#198754');
gauges[id].data.datasets[0].data = [pct, 100 - pct];
gauges[id].data.datasets[0].backgroundColor = [color, '#1a1d20'];
gauges[id].update('none');
}
};