UI improvements

This commit is contained in:
rdavidek 2026-03-28 16:44:46 +01:00
parent 7b5c827635
commit e908f53e8b

View File

@ -184,12 +184,8 @@
</select> </select>
</div> </div>
<div class="filter-group" style="grid-column: 4;"> <div class="filter-group" style="grid-column: 4;">
<label for="dateFrom">Od:</label> <label for="selectedDate">Den:</label>
<input type="datetime-local" id="dateFrom"> <input type="date" id="selectedDate">
</div>
<div class="filter-group" style="grid-column: 5;">
<label for="dateTo">Do:</label>
<input type="datetime-local" id="dateTo">
</div> </div>
<div class="filter-buttons"> <div class="filter-buttons">
<button onclick="applyFilters()">Použít filtry</button> <button onclick="applyFilters()">Použít filtry</button>
@ -220,28 +216,16 @@
<div class="charts-grid"> <div class="charts-grid">
<div class="chart-container"> <div class="chart-container">
<h3>Stav procesů</h3> <h3>Doba procesů vs. čas bez nalezených procesů</h3>
<div class="chart-wrapper"> <div class="chart-wrapper">
<canvas id="statusChart"></canvas> <canvas id="processTimeChart"></canvas>
</div>
</div>
<div class="chart-container">
<h3>Stavy podle strojů</h3>
<div class="chart-wrapper">
<canvas id="machineChart"></canvas>
</div>
</div>
<div class="chart-container" style="grid-column: 1 / -1;">
<h3>Dostupnost v čase</h3>
<div class="chart-wrapper" style="height: 400px;">
<canvas id="timelineChart"></canvas>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script> <script>
let statusChart, machineChart, timelineChart; let processTimeChart;
const apiKey = '%API_KEY%'; const apiKey = '%API_KEY%';
function normalizeStatus(status) { function normalizeStatus(status) {
@ -284,9 +268,7 @@
}); });
const now = new Date(); const now = new Date();
const sevenDaysAgo = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); document.getElementById('selectedDate').value = now.toISOString().slice(0, 10);
document.getElementById('dateTo').value = now.toISOString().slice(0, 16);
document.getElementById('dateFrom').value = sevenDaysAgo.toISOString().slice(0, 16);
applyFilters(); applyFilters();
} catch (error) { } catch (error) {
@ -300,8 +282,7 @@
const machine = document.getElementById('machine').value; const machine = document.getElementById('machine').value;
const process = document.getElementById('process').value; const process = document.getElementById('process').value;
const status = document.getElementById('status').value; const status = document.getElementById('status').value;
const dateFrom = document.getElementById('dateFrom').value; const selectedDate = document.getElementById('selectedDate').value;
const dateTo = document.getElementById('dateTo').value;
const params = new URLSearchParams(); const params = new URLSearchParams();
params.append('type', 'stats'); params.append('type', 'stats');
@ -309,8 +290,12 @@
if (machine) params.append('machine', machine); if (machine) params.append('machine', machine);
if (process) params.append('process', process); if (process) params.append('process', process);
if (status) params.append('status', status); if (status) params.append('status', status);
if (dateFrom) params.append('from', new Date(dateFrom).toISOString()); if (selectedDate) {
if (dateTo) params.append('to', new Date(dateTo).toISOString()); const from = selectedDate + 'T00:00:00Z';
const to = selectedDate + 'T23:59:59Z';
params.append('from', from);
params.append('to', to);
}
const response = await axios.get('/hb/api/data?' + params.toString()); const response = await axios.get('/hb/api/data?' + params.toString());
const data = response.data; const data = response.data;
@ -337,21 +322,62 @@
function updateCharts(data) { function updateCharts(data) {
const records = data.records; const records = data.records;
const statusCounts = {}; // Agregace času podle procesů
records.forEach(r => { const processTimeMap = {};
const normalized = normalizeStatus(r.status);
statusCounts[normalized] = (statusCounts[normalized] || 0) + 1; // Seřadit záznamy podle procesu a času
const sortedRecords = records.sort((a, b) =>
new Date(a.detected_at) - new Date(b.detected_at)
);
// Seskupit podle procesu a spočítat časy
const processByName = {};
sortedRecords.forEach(r => {
const processName = r.process_name || '(bez procesu)';
if (!processByName[processName]) {
processByName[processName] = [];
}
processByName[processName].push(r);
}); });
if (statusChart) statusChart.destroy(); // Spočítat čas běhu pro každý proces
const statusCtx = document.getElementById('statusChart').getContext('2d'); Object.keys(processByName).forEach(processName => {
statusChart = new Chart(statusCtx, { const records = processByName[processName];
let totalTimeMs = 0;
for (let i = 0; i < records.length - 1; i++) {
const current = new Date(records[i].detected_at);
const next = new Date(records[i + 1].detected_at);
totalTimeMs += (next - current);
}
// Převést na minuty
const totalMinutes = Math.round(totalTimeMs / 60000);
processTimeMap[processName] = totalMinutes;
});
if (processTimeChart) processTimeChart.destroy();
const processTimeCtx = document.getElementById('processTimeChart').getContext('2d');
// Příprava labels s časy
const labels = Object.keys(processTimeMap).map(processName => {
const minutes = processTimeMap[processName];
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
const timeStr = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
return `${processName} (${timeStr})`;
});
processTimeChart = new Chart(processTimeCtx, {
type: 'doughnut', type: 'doughnut',
data: { data: {
labels: Object.keys(statusCounts), labels: labels,
datasets: [{ datasets: [{
data: Object.values(statusCounts), data: Object.values(processTimeMap),
backgroundColor: ['#4CAF50', '#FF6B6B'], backgroundColor: [
'#FF6B6B', '#4ECDC4', '#45B7D1', '#FFA07A', '#98D8C8',
'#F7DC6F', '#BB8FCE', '#85C1E2', '#F8B88B', '#ABEBC6'
],
borderColor: '#fff', borderColor: '#fff',
borderWidth: 2 borderWidth: 2
}] }]
@ -360,90 +386,21 @@
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
plugins: { plugins: {
legend: { position: 'bottom' } legend: { position: 'bottom' },
tooltip: {
callbacks: {
label: function(context) {
const minutes = context.parsed;
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
if (hours > 0) {
return `${hours}h ${mins}m`;
} else {
return `${mins}m`;
} }
} }
});
const statusByMachine = {};
records.forEach(r => {
const normalized = normalizeStatus(r.status);
if (!statusByMachine[r.machine_name]) {
statusByMachine[r.machine_name] = { UP: 0, DOWN: 0 };
}
if (normalized === 'UP') {
statusByMachine[r.machine_name].UP++;
} else if (normalized === 'DOWN') {
statusByMachine[r.machine_name].DOWN++;
}
});
if (machineChart) machineChart.destroy();
const machineCtx = document.getElementById('machineChart').getContext('2d');
machineChart = new Chart(machineCtx, {
type: 'bar',
data: {
labels: Object.keys(statusByMachine),
datasets: [
{
label: 'UP',
backgroundColor: '#4CAF50',
data: Object.values(statusByMachine).map(s => s.UP)
},
{
label: 'DOWN',
backgroundColor: '#FF6B6B',
data: Object.values(statusByMachine).map(s => s.DOWN)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: { stacked: true },
y: { stacked: true }
} }
} }
});
const sortedRecords = records.sort((a, b) =>
new Date(a.detected_at) - new Date(b.detected_at)
);
const timeLabels = sortedRecords.map(r =>
new Date(r.detected_at).toLocaleString('cs-CZ')
);
const upCounts = [];
let upCount = 0;
sortedRecords.forEach(r => {
if (normalizeStatus(r.status) === 'UP') upCount++;
upCounts.push(upCount);
});
if (timelineChart) timelineChart.destroy();
const timelineCtx = document.getElementById('timelineChart').getContext('2d');
timelineChart = new Chart(timelineCtx, {
type: 'line',
data: {
labels: timeLabels,
datasets: [{
label: 'Procesy UP',
borderColor: '#4CAF50',
backgroundColor: 'rgba(76, 175, 80, 0.1)',
data: upCounts,
fill: true,
tension: 0.4
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { position: 'bottom' }
},
scales: {
y: { beginAtZero: true }
} }
} }
}); });
@ -453,6 +410,8 @@
document.getElementById('machine').value = ''; document.getElementById('machine').value = '';
document.getElementById('process').value = ''; document.getElementById('process').value = '';
document.getElementById('status').value = ''; document.getElementById('status').value = '';
const now = new Date();
document.getElementById('selectedDate').value = now.toISOString().slice(0, 10);
applyFilters(); applyFilters();
} }
@ -470,6 +429,7 @@
} }
window.addEventListener('load', loadFilters); window.addEventListener('load', loadFilters);
document.getElementById('selectedDate')?.addEventListener('change', applyFilters);
</script> </script>
</body> </body>
</html> </html>