This commit is contained in:
Mateusz Gruszczyński
2026-02-04 14:55:40 +01:00
parent 64887698d2
commit 18c67fd728
4 changed files with 218 additions and 65 deletions

View File

@@ -5,6 +5,7 @@
--text-main: #c9d1d9;
--blue-accent: #58a6ff;
}
body {
background-color: var(--bg-dark);
color: var(--text-main);
@@ -13,12 +14,15 @@ body {
margin: 0;
overflow-x: hidden;
}
/* Siatka wskaźników (gauges) */
.gauge-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
margin-bottom: 15px;
}
.gauge-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
@@ -26,41 +30,53 @@ body {
padding: 10px 5px;
text-align: center;
}
.gauge-canvas-container {
max-width: 80px;
margin: 0 auto;
}
.gauge-label {
font-size: 0.75rem;
font-weight: 600;
color: var(--blue-accent);
margin-top: 2px;
}
.voltage-value {
font-size: 1.1rem;
font-weight: 800;
color: #ffffff;
}
/* Wybór zakresu czasu - Poprawiona responsywność */
.time-btn-container {
display: flex;
flex-wrap: nowrap;
overflow-x: auto;
gap: 6px;
padding-bottom: 10px;
flex-wrap: wrap; /* Pozwala na zawijanie przycisków do nowej linii */
gap: 8px;
padding: 10px 0;
justify-content: center;
}
.time-btn {
font-size: 0.75rem !important;
padding: 5px 12px !important;
padding: 6px 12px !important;
white-space: nowrap;
border-color: var(--border-color) !important;
border: 1px solid var(--border-color) !important;
color: var(--blue-accent) !important;
background: transparent;
border-radius: 6px;
cursor: pointer;
transition: all 0.2s;
}
.time-btn.active {
background-color: #1f6feb !important;
color: white !important;
border-color: #1f6feb !important;
}
/* Wykres główny */
.main-chart-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
@@ -69,15 +85,99 @@ body {
height: 55vh;
min-height: 350px;
}
footer { padding: 20px 0; opacity: 0.7; }
@media (max-width: 576px) {
.voltage-value { font-size: 0.95rem; }
.main-chart-card { height: 50vh; padding: 10px; }
/* Lista zdarzeń (logi) */
.events-card {
background-color: var(--card-bg);
border: 1px solid var(--border-color);
border-radius: 12px;
padding: 15px;
margin-top: 15px;
}
.events-card { background-color: var(--card-bg); border: 1px solid var(--border-color); border-radius: 12px; padding: 15px; }
.event-item { display: flex; align-items: center; padding: 8px 0; border-bottom: 1px solid #30363d; }
.event-item:last-child { border-bottom: none; }
.event-badge { width: 12px; height: 12px; border-radius: 50%; margin-right: 12px; flex-shrink: 0; }
.event-time { font-family: monospace; font-size: 0.85rem; color: #8b949e; margin-right: 15px; }
.event-desc { font-size: 0.9rem; color: #c9d1d9; }
.no-events { color: #8b949e; font-style: italic; text-align: center; padding: 10px; }
.event-item {
display: flex;
align-items: center;
padding: 10px 0;
border-bottom: 1px solid var(--border-color);
}
.event-item:last-child {
border-bottom: none;
}
.event-badge {
width: 12px;
height: 12px;
border-radius: 50%;
margin-right: 12px;
flex-shrink: 0;
}
.event-time {
font-family: monospace;
font-size: 0.85rem;
color: #8b949e;
margin-right: 15px;
white-space: nowrap;
}
.event-desc {
font-size: 0.9rem;
color: var(--text-main);
}
.no-events {
color: #8b949e;
font-style: italic;
text-align: center;
padding: 10px;
}
footer {
padding: 20px 0;
opacity: 0.7;
text-align: center;
}
@media (max-width: 576px) {
.voltage-value {
font-size: 0.95rem;
}
.main-chart-card {
height: 45vh;
padding: 10px;
}
.time-btn {
flex: 1 1 calc(30% - 10px);
min-width: 70px;
font-size: 0.7rem !important;
padding: 8px 4px !important;
}
.event-item {
flex-direction: column;
align-items: flex-start;
gap: 4px;
}
.event-badge {
margin-bottom: 4px;
}
.event-time {
margin-right: 0;
}
}
.event-warning {
background-color: rgba(255, 187, 51, 0.1);
border: 1px dashed #ffbb33;
border-radius: 8px;
padding: 15px;
margin: 10px 0;
text-align: center;
color: #ffbb33;
}

View File

@@ -260,57 +260,88 @@ function renderEventLog(events, range) {
const container = document.getElementById('eventLogContainer');
if (!container) return;
container.innerHTML = '';
if (events && events.error === "range_too_large") {
container.innerHTML = `
<div style="text-align: center; padding: 30px; color: #ffbb33; border: 1px dashed #ffbb33; border-radius: 12px; margin: 10px;">
<div style="font-size: 2rem; margin-bottom: 10px;">⚠️</div>
<p style="margin: 0;">${events.message}</p>
</div>`;
return;
}
if (events && events.length > 0) {
events.forEach(ev => {
const start = new Date(ev.start);
const end = new Date(ev.end);
const dur = ev.duration;
const dur = ev.duration;
const phase = phasesConfig[ev.phase];
const typeConfig = {
'zanik': { label: 'Brak zasilania', color: '#ff4444' },
'niskie': { label: 'Zbyt niskie', color: '#ffbb33' },
'wysokie': { label: 'Zbyt wysokie', color: '#aa66cc' }
};
const config = typeConfig[ev.type] || { label: 'Problem', color: '#888' };
const item = document.createElement('div');
item.className = 'event-item';
item.style.display = 'flex';
item.style.alignItems = 'center';
item.style.gap = '10px';
item.style.justifyContent = 'space-between';
const dateOpt = { day: '2-digit', month: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false };
const timeRangeStr = `${start.toLocaleTimeString('pl-PL', {hour:'2-digit', minute:'2-digit'})} - ${end.toLocaleTimeString('pl-PL', {hour:'2-digit', minute:'2-digit'})}`;
item.innerHTML = `
<div class="event-badge" style="background-color: ${phase.color}"></div>
<div class="event-time">${start.toLocaleString('pl-PL', dateOpt)} - ${end.toLocaleTimeString('pl-PL', {hour:'2-digit', minute:'2-digit'})}</div>
<div class="event-desc" style="flex-grow: 1;">Faza <strong>${phase.label}</strong>: brak przez ${dur} min.</div>
<button class="btn btn-sm btn-outline-info show-event-btn" title="Pokaż 3h wokół zdarzenia"
style="padding: 2px 8px; font-size: 11px; height: 24px; color: #58a6ff; border-color: #30363d; min-width: 50px;">
Pokaż
<div style="display: flex; align-items: center; flex-grow: 1;">
<!-- Badge statusu z pierścieniem w kolorze fazy -->
<div class="event-badge" style="
background-color: ${config.color};
box-shadow: 0 0 0 2px ${phase.color};
margin-left: 2px;
margin-right: 14px;
width: 10px; height: 10px;">
</div>
<div class="event-time">
<div>${start.toLocaleDateString('pl-PL', {day:'2-digit', month:'2-digit'})}, ${timeRangeStr}</div>
</div>
<div class="event-desc">
<span style="color: ${phase.color}; font-weight: bold;">${phase.label}</span>:
<strong>${config.label}</strong> przez ${dur} min.
</div>
</div>
<button onclick="showEventOnChart('${ev.start}')" class="time-btn" style="padding: 4px 8px !important; margin-left: 10px;">
Pokaż 🔍
</button>
`;
const btn = item.querySelector('.show-event-btn');
btn.addEventListener('click', async () => {
const eventCenter = start.getTime() + (end.getTime() - start.getTime()) / 2;
const windowMs = 3 * 60 * 60 * 1000;
const viewStart = eventCenter - (windowMs / 2);
const viewEnd = eventCenter + (windowMs / 2);
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
currentTimeRange = 'precise';
if (voltageChart) {
voltageChart.options.scales.x.min = undefined;
voltageChart.options.scales.x.max = undefined;
}
await reloadDataForRange(viewStart, viewEnd, null);
document.getElementById('voltageChart').scrollIntoView({ behavior: 'smooth' });
});
container.appendChild(item);
});
} else {
container.innerHTML = '<div class="no-events">Brak zarejestrowanych zaników.</div>';
container.innerHTML = '<div class="no-events">Brak zarejestrowanych zdarzeń w tym zakresie.</div>';
}
}
/**
* Funkcja przybliżająca wykres do konkretnego zdarzenia (zakres 3h)
*/
window.showEventOnChart = function(startTimeStr) {
const eventTime = new Date(startTimeStr).getTime();
const padding = 1.5 * 60 * 60 * 1000; // 1.5 godziny w milisekundach
const min = eventTime - padding;
const max = eventTime + padding;
if (voltageChart) {
document.querySelectorAll('.time-btn').forEach(b => b.classList.remove('active'));
voltageChart.options.scales.x.min = min;
voltageChart.options.scales.x.max = max;
currentTimeRange = 'precise';
reloadDataForRange(min, max);
}
};
function setupGauges() {
const config = {