better UI results
This commit is contained in:
parent
e908f53e8b
commit
30eed4c3e7
@ -190,6 +190,61 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String extractMainName(String processName, java.util.List<String> allProcessNames) {
|
||||||
|
if (processName == null || processName.isBlank()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Odstranit .exe
|
||||||
|
String name = processName;
|
||||||
|
int dotIndex = name.lastIndexOf('.');
|
||||||
|
if (dotIndex > 0) {
|
||||||
|
name = name.substring(0, dotIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seřadit všechny názvy podle délky (od nejkratšího)
|
||||||
|
java.util.List<String> sortedNames = new java.util.ArrayList<>(allProcessNames);
|
||||||
|
sortedNames.sort((a, b) -> {
|
||||||
|
int lenA = a != null ? a.length() : 0;
|
||||||
|
int lenB = b != null ? b.length() : 0;
|
||||||
|
return Integer.compare(lenA, lenB);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Pro každý název najít jeho unikátní základ
|
||||||
|
// Pokud nějaký název začíná kratším názvem, odstraníme ten delší
|
||||||
|
java.util.Set<String> baseNames = new java.util.TreeSet<>(String.CASE_INSENSITIVE_ORDER);
|
||||||
|
|
||||||
|
for (String procName : sortedNames) {
|
||||||
|
if (procName == null || procName.isBlank()) continue;
|
||||||
|
String cleanName = procName;
|
||||||
|
int dotIdx = cleanName.lastIndexOf('.');
|
||||||
|
if (dotIdx > 0) {
|
||||||
|
cleanName = cleanName.substring(0, dotIdx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zkontrolovat, zda tento název začíná nějakým již přidaným základem
|
||||||
|
boolean foundBase = false;
|
||||||
|
for (String base : baseNames) {
|
||||||
|
if (cleanName.toLowerCase().startsWith(base.toLowerCase())) {
|
||||||
|
foundBase = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundBase) {
|
||||||
|
baseNames.add(cleanName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Najít základ pro tento konkrétní název
|
||||||
|
for (String base : baseNames) {
|
||||||
|
if (name.toLowerCase().startsWith(base.toLowerCase())) {
|
||||||
|
return base.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return name.trim();
|
||||||
|
}
|
||||||
|
|
||||||
private StatsResponse getStats(String machine, String process, String status, String from, String to) throws SQLException {
|
private StatsResponse getStats(String machine, String process, String status, String from, String to) throws SQLException {
|
||||||
StringBuilder sql = new StringBuilder("SELECT id, machine_name, status, detected_at, process_name FROM process_heartbeat WHERE 1=1");
|
StringBuilder sql = new StringBuilder("SELECT id, machine_name, status, detected_at, process_name FROM process_heartbeat WHERE 1=1");
|
||||||
java.util.List<String> params = new java.util.ArrayList<>();
|
java.util.List<String> params = new java.util.ArrayList<>();
|
||||||
@ -224,15 +279,29 @@ public class Main {
|
|||||||
stmt.setString(i + 1, params.get(i));
|
stmt.setString(i + 1, params.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nejprve načíst všechny unikátní názvy procesů pro určení hlavního názvu
|
||||||
|
java.util.List<String> allProcessNames = new java.util.ArrayList<>();
|
||||||
|
try (PreparedStatement allStmt = connection.prepareStatement(
|
||||||
|
"SELECT DISTINCT process_name FROM process_heartbeat WHERE process_name IS NOT NULL")) {
|
||||||
|
try (java.sql.ResultSet rs = allStmt.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
allProcessNames.add(rs.getString(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
java.util.List<Record> records = new java.util.ArrayList<>();
|
java.util.List<Record> records = new java.util.ArrayList<>();
|
||||||
try (java.sql.ResultSet rs = stmt.executeQuery()) {
|
try (java.sql.ResultSet rs = stmt.executeQuery()) {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
|
String processName = rs.getString(5);
|
||||||
|
String mainName = extractMainName(processName, allProcessNames);
|
||||||
records.add(new Record(
|
records.add(new Record(
|
||||||
rs.getLong(1),
|
rs.getLong(1),
|
||||||
rs.getString(2),
|
rs.getString(2),
|
||||||
rs.getString(3),
|
rs.getString(3),
|
||||||
rs.getTimestamp(4),
|
rs.getTimestamp(4),
|
||||||
rs.getString(5)
|
processName,
|
||||||
|
mainName
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -240,6 +309,21 @@ public class Main {
|
|||||||
return new StatsResponse(records);
|
return new StatsResponse(records);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private LastRecordTimeResponse getLastRecordTime() throws SQLException {
|
||||||
|
String sql = "SELECT detected_at FROM process_heartbeat ORDER BY detected_at DESC LIMIT 1";
|
||||||
|
|
||||||
|
try (Connection connection = DriverManager.getConnection(config.jdbcUrl, config.dbUser, config.dbPassword);
|
||||||
|
PreparedStatement stmt = connection.prepareStatement(sql);
|
||||||
|
java.sql.ResultSet rs = stmt.executeQuery()) {
|
||||||
|
|
||||||
|
if (rs.next()) {
|
||||||
|
Timestamp ts = rs.getTimestamp(1);
|
||||||
|
return new LastRecordTimeResponse(ts.toInstant().toString());
|
||||||
|
}
|
||||||
|
return new LastRecordTimeResponse(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final class FilterOptions {
|
private static final class FilterOptions {
|
||||||
@ -260,13 +344,15 @@ public class Main {
|
|||||||
String status;
|
String status;
|
||||||
String detected_at;
|
String detected_at;
|
||||||
String process_name;
|
String process_name;
|
||||||
|
String main_name;
|
||||||
|
|
||||||
Record(long id, String machine_name, String status, Timestamp detected_at, String process_name) {
|
Record(long id, String machine_name, String status, Timestamp detected_at, String process_name, String main_name) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.machine_name = machine_name;
|
this.machine_name = machine_name;
|
||||||
this.status = status;
|
this.status = status;
|
||||||
this.detected_at = detected_at != null ? detected_at.toInstant().toString() : null;
|
this.detected_at = detected_at != null ? detected_at.toInstant().toString() : null;
|
||||||
this.process_name = process_name;
|
this.process_name = process_name;
|
||||||
|
this.main_name = main_name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,6 +364,14 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class LastRecordTimeResponse {
|
||||||
|
String lastRecordTime;
|
||||||
|
|
||||||
|
LastRecordTimeResponse(String lastRecordTime) {
|
||||||
|
this.lastRecordTime = lastRecordTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class HeartbeatRequest {
|
private static final class HeartbeatRequest {
|
||||||
private String machine_name;
|
private String machine_name;
|
||||||
private String status;
|
private String status;
|
||||||
@ -419,6 +513,9 @@ public class Main {
|
|||||||
if ("filters".equals(type)) {
|
if ("filters".equals(type)) {
|
||||||
String response = GSON.toJson(database.getFilterOptions());
|
String response = GSON.toJson(database.getFilterOptions());
|
||||||
sendJson(exchange, 200, response);
|
sendJson(exchange, 200, response);
|
||||||
|
} else if ("lastRecordTime".equals(type)) {
|
||||||
|
String response = GSON.toJson(database.getLastRecordTime());
|
||||||
|
sendJson(exchange, 200, response);
|
||||||
} else if ("stats".equals(type)) {
|
} else if ("stats".equals(type)) {
|
||||||
String machine = getParam(query, "machine");
|
String machine = getParam(query, "machine");
|
||||||
String process = getParam(query, "process");
|
String process = getParam(query, "process");
|
||||||
|
|||||||
@ -27,12 +27,12 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
.header h1 {
|
.header h1 {
|
||||||
margin-bottom: 10px;
|
margin-bottom: 5px;
|
||||||
}
|
}
|
||||||
.header-content {
|
.header-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.logout-btn {
|
.logout-btn {
|
||||||
background: rgba(255,255,255,0.2);
|
background: rgba(255,255,255,0.2);
|
||||||
border: 1px solid rgba(255,255,255,0.3);
|
border: 1px solid rgba(255,255,255,0.3);
|
||||||
color: white;
|
color: white;
|
||||||
@ -46,7 +46,7 @@
|
|||||||
background: rgba(255,255,255,0.3);
|
background: rgba(255,255,255,0.3);
|
||||||
}
|
}
|
||||||
.container {
|
.container {
|
||||||
max-width: 1400px;
|
max-width: 600px;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
@ -54,11 +54,10 @@
|
|||||||
background: white;
|
background: white;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
margin-bottom: 20px;
|
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
grid-template-columns: 1fr 1fr;
|
||||||
gap: 15px;
|
gap: 10px;
|
||||||
}
|
}
|
||||||
.filter-group {
|
.filter-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -103,19 +102,14 @@
|
|||||||
.filter-buttons button.reset:hover {
|
.filter-buttons button.reset:hover {
|
||||||
background: #777;
|
background: #777;
|
||||||
}
|
}
|
||||||
.charts-grid {
|
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(500px, 1fr));
|
|
||||||
gap: 20px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
background: white;
|
background: white;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
margin-top: 20px;
|
||||||
}
|
}
|
||||||
.chart-container h3 {
|
.chart-container h3 {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
color: #333;
|
color: #333;
|
||||||
}
|
}
|
||||||
@ -123,27 +117,18 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
}
|
}
|
||||||
.stats-grid {
|
.info-panel {
|
||||||
display: grid;
|
|
||||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
||||||
gap: 15px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
.stat-card {
|
|
||||||
background: white;
|
background: white;
|
||||||
padding: 20px;
|
padding: 15px 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
}
|
margin-top: 20px;
|
||||||
.stat-card h4 {
|
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #666;
|
color: #666;
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
}
|
||||||
.stat-card .value {
|
.info-panel span {
|
||||||
font-size: 28px;
|
font-weight: 600;
|
||||||
font-weight: bold;
|
color: #333;
|
||||||
color: #667eea;
|
|
||||||
}
|
}
|
||||||
.error {
|
.error {
|
||||||
background: #fee;
|
background: #fee;
|
||||||
@ -157,72 +142,51 @@
|
|||||||
<body>
|
<body>
|
||||||
<div class="header">
|
<div class="header">
|
||||||
<div class="header-content">
|
<div class="header-content">
|
||||||
<h1>📊 Process Monitor Dashboard</h1>
|
<h1>Process Monitor Dashboard</h1>
|
||||||
<p>Real-time monitoring of processes across machines</p>
|
|
||||||
</div>
|
</div>
|
||||||
<button class="logout-btn" onclick="logout()">Odhlášení</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<div class="filters">
|
<div class="filters">
|
||||||
<div class="filter-group" style="grid-column: 1;">
|
<div class="filter-group">
|
||||||
<label for="machine">Stroj:</label>
|
<label for="machine">Stroj:</label>
|
||||||
<select id="machine">
|
<select id="machine">
|
||||||
<option value="">-- Všechny stroje --</option>
|
<option value="">-- Všechny stroje --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-group" style="grid-column: 2;">
|
<div class="filter-group">
|
||||||
<label for="process">Proces:</label>
|
<label for="process">Proces:</label>
|
||||||
<select id="process">
|
<select id="process">
|
||||||
<option value="">-- Všechny procesy --</option>
|
<option value="">-- Všechny procesy --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-group" style="grid-column: 3;">
|
<div class="filter-group">
|
||||||
<label for="status">Stav:</label>
|
<label for="status">Stav:</label>
|
||||||
<select id="status">
|
<select id="status">
|
||||||
<option value="">-- Všechny stavy --</option>
|
<option value="">-- Všechny stavy --</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-group" style="grid-column: 4;">
|
<div class="filter-group">
|
||||||
<label for="selectedDate">Den:</label>
|
<label for="selectedDate">Den:</label>
|
||||||
<input type="date" id="selectedDate">
|
<input type="date" id="selectedDate">
|
||||||
</div>
|
</div>
|
||||||
<div class="filter-buttons">
|
<div class="filter-buttons" style="grid-column: 1 / -1;">
|
||||||
<button onclick="applyFilters()">Použít filtry</button>
|
<button onclick="applyFilters()">Použít filtry</button>
|
||||||
<button class="reset" onclick="resetFilters()">Reset</button>
|
<button class="reset" onclick="resetFilters()">Reset</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="error"></div>
|
<div class="info-panel">
|
||||||
|
Poslední záznam: <span id="lastRecordTime">Načítání...</span>
|
||||||
<div class="stats-grid">
|
|
||||||
<div class="stat-card">
|
|
||||||
<h4>Celkem záznamů</h4>
|
|
||||||
<div class="value" id="statTotal">-</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<h4>Procesy UP</h4>
|
|
||||||
<div class="value" id="statUp">-</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<h4>Procesy DOWN</h4>
|
|
||||||
<div class="value" id="statDown">-</div>
|
|
||||||
</div>
|
|
||||||
<div class="stat-card">
|
|
||||||
<h4>Dostupnost</h4>
|
|
||||||
<div class="value" id="statAvailability">-</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="charts-grid">
|
<div class="chart-container">
|
||||||
<div class="chart-container">
|
<h3>Doba procesů</h3>
|
||||||
<h3>Doba procesů vs. čas bez nalezených procesů</h3>
|
<div class="chart-wrapper">
|
||||||
<div class="chart-wrapper">
|
<canvas id="processTimeChart"></canvas>
|
||||||
<canvas id="processTimeChart"></canvas>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
let processTimeChart;
|
let processTimeChart;
|
||||||
@ -308,15 +272,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateStats(data) {
|
function updateStats(data) {
|
||||||
const total = data.records.length;
|
// Stats cards removed - function kept for compatibility
|
||||||
const up = data.records.filter(r => normalizeStatus(r.status) === 'UP').length;
|
|
||||||
const down = data.records.filter(r => normalizeStatus(r.status) === 'DOWN').length;
|
|
||||||
const availability = total > 0 ? ((up / total) * 100).toFixed(1) : 0;
|
|
||||||
|
|
||||||
document.getElementById('statTotal').textContent = total;
|
|
||||||
document.getElementById('statUp').textContent = up;
|
|
||||||
document.getElementById('statDown').textContent = down;
|
|
||||||
document.getElementById('statAvailability').textContent = availability + '%';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateCharts(data) {
|
function updateCharts(data) {
|
||||||
@ -330,19 +286,19 @@
|
|||||||
new Date(a.detected_at) - new Date(b.detected_at)
|
new Date(a.detected_at) - new Date(b.detected_at)
|
||||||
);
|
);
|
||||||
|
|
||||||
// Seskupit podle procesu a spočítat časy
|
// Seskupit podle hlavního názvu a spočítat časy
|
||||||
const processByName = {};
|
const processByName = {};
|
||||||
sortedRecords.forEach(r => {
|
sortedRecords.forEach(r => {
|
||||||
const processName = r.process_name || '(bez procesu)';
|
const mainName = r.main_name || r.process_name || '(bez procesu)';
|
||||||
if (!processByName[processName]) {
|
if (!processByName[mainName]) {
|
||||||
processByName[processName] = [];
|
processByName[mainName] = [];
|
||||||
}
|
}
|
||||||
processByName[processName].push(r);
|
processByName[mainName].push(r);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Spočítat čas běhu pro každý proces
|
// Spočítat čas běhu pro každý hlavní název
|
||||||
Object.keys(processByName).forEach(processName => {
|
Object.keys(processByName).forEach(mainName => {
|
||||||
const records = processByName[processName];
|
const records = processByName[mainName];
|
||||||
let totalTimeMs = 0;
|
let totalTimeMs = 0;
|
||||||
|
|
||||||
for (let i = 0; i < records.length - 1; i++) {
|
for (let i = 0; i < records.length - 1; i++) {
|
||||||
@ -353,19 +309,19 @@
|
|||||||
|
|
||||||
// Převést na minuty
|
// Převést na minuty
|
||||||
const totalMinutes = Math.round(totalTimeMs / 60000);
|
const totalMinutes = Math.round(totalTimeMs / 60000);
|
||||||
processTimeMap[processName] = totalMinutes;
|
processTimeMap[mainName] = totalMinutes;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (processTimeChart) processTimeChart.destroy();
|
if (processTimeChart) processTimeChart.destroy();
|
||||||
const processTimeCtx = document.getElementById('processTimeChart').getContext('2d');
|
const processTimeCtx = document.getElementById('processTimeChart').getContext('2d');
|
||||||
|
|
||||||
// Příprava labels s časy
|
// Příprava labels s časy
|
||||||
const labels = Object.keys(processTimeMap).map(processName => {
|
const labels = Object.keys(processTimeMap).map(mainName => {
|
||||||
const minutes = processTimeMap[processName];
|
const minutes = processTimeMap[mainName];
|
||||||
const hours = Math.floor(minutes / 60);
|
const hours = Math.floor(minutes / 60);
|
||||||
const mins = minutes % 60;
|
const mins = minutes % 60;
|
||||||
const timeStr = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
const timeStr = hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
|
||||||
return `${processName} (${timeStr})`;
|
return `${mainName} (${timeStr})`;
|
||||||
});
|
});
|
||||||
|
|
||||||
processTimeChart = new Chart(processTimeCtx, {
|
processTimeChart = new Chart(processTimeCtx, {
|
||||||
@ -415,14 +371,8 @@
|
|||||||
applyFilters();
|
applyFilters();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showError(message) {
|
function showError(message) {}
|
||||||
document.getElementById('error').innerHTML =
|
function clearError() {}
|
||||||
'<div class="error">' + message + '</div>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearError() {
|
|
||||||
document.getElementById('error').innerHTML = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
window.location.href = '/';
|
window.location.href = '/';
|
||||||
@ -430,6 +380,27 @@
|
|||||||
|
|
||||||
window.addEventListener('load', loadFilters);
|
window.addEventListener('load', loadFilters);
|
||||||
document.getElementById('selectedDate')?.addEventListener('change', applyFilters);
|
document.getElementById('selectedDate')?.addEventListener('change', applyFilters);
|
||||||
|
|
||||||
|
async function loadLastRecordTime() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get('/hb/api/data?type=lastRecordTime&apiKey=' + encodeURIComponent(apiKey));
|
||||||
|
const data = response.data;
|
||||||
|
if (data.lastRecordTime) {
|
||||||
|
const date = new Date(data.lastRecordTime);
|
||||||
|
const timeStr = date.toLocaleTimeString('cs-CZ', { hour: '2-digit', minute: '2-digit', second: '2-digit' });
|
||||||
|
document.getElementById('lastRecordTime').textContent = 'Poslední záznam: ' + timeStr;
|
||||||
|
} else {
|
||||||
|
document.getElementById('lastRecordTime').textContent = 'V databázi nejsou žádné záznamy';
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// ignore errors for this info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load last record time on page load
|
||||||
|
loadLastRecordTime();
|
||||||
|
// Refresh last record time every minute
|
||||||
|
setInterval(loadLastRecordTime, 60000);
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -1,61 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="cs">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Process Monitor - Chyba</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.error-container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 40px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #c33;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 25px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
a {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 10px 20px;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
color: white;
|
|
||||||
text-decoration: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
a:hover {
|
|
||||||
opacity: 0.9;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="error-container">
|
|
||||||
<h1>⚠️ Chyba</h1>
|
|
||||||
<p>%MESSAGE%</p>
|
|
||||||
<a href="/hb/dashboard">Zpět na přihlášení</a>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@ -1,101 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="cs">
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Process Monitor - Login</title>
|
|
||||||
<style>
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
min-height: 100vh;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
.login-container {
|
|
||||||
background: white;
|
|
||||||
border-radius: 8px;
|
|
||||||
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.2);
|
|
||||||
padding: 40px;
|
|
||||||
width: 100%;
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
h1 {
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
font-size: 24px;
|
|
||||||
}
|
|
||||||
p {
|
|
||||||
color: #666;
|
|
||||||
margin-bottom: 30px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
.form-group {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
color: #333;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
input {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
border: 2px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: border-color 0.3s;
|
|
||||||
}
|
|
||||||
input:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: #667eea;
|
|
||||||
}
|
|
||||||
button {
|
|
||||||
width: 100%;
|
|
||||||
padding: 12px;
|
|
||||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
cursor: pointer;
|
|
||||||
font-size: 14px;
|
|
||||||
transition: transform 0.2s;
|
|
||||||
}
|
|
||||||
button:hover {
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
button:active {
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="login-container">
|
|
||||||
<h1>📊 Process Monitor</h1>
|
|
||||||
<p>Zadejte API klíč pro přístup na dashboard</p>
|
|
||||||
<form onsubmit="submitLogin(event)">
|
|
||||||
<div class="form-group">
|
|
||||||
<label for="apiKey">API Klíč:</label>
|
|
||||||
<input type="password" id="apiKey" name="apiKey" required autofocus>
|
|
||||||
</div>
|
|
||||||
<button type="submit">Přihlásit</button>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
<script>
|
|
||||||
function submitLogin(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
const apiKey = document.getElementById('apiKey').value;
|
|
||||||
window.location.href = '/hb/dashboard?apiKey=' + encodeURIComponent(apiKey);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Loading…
x
Reference in New Issue
Block a user