Files
felhom.eu/hub/internal/web/templates/app_detail.html
T
admin 38f3a1e01e feat: per-app telemetry reset button on app detail page
Adds "Telemetria törlése" button that deletes all telemetry records and
known issues for a specific app. Useful after major app updates when old
data is no longer representative.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 15:05:46 +01:00

221 lines
9.9 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="hu">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{.AppName}} — Felhom Hub</title>
<link rel="stylesheet" href="/style.css">
<script src="/static/chart.min.js"></script>
</head>
<body>
<div class="container">
<header>
<h1>Felhom Hub</h1>
<nav class="nav-links">
<a href="/" class="nav-link">Dashboard</a>
<a href="/configs" class="nav-link">Customers</a>
<a href="/apps" class="nav-link active">Alkalmazások</a>
</nav>
</header>
<a href="/apps{{if .Period}}?period={{.Period}}{{end}}" class="back-link">&larr; Alkalmazások</a>
<!-- Period selector -->
<div class="period-selector" style="margin-top: 1rem;">
<a href="?period=24h" class="period-btn{{if eq .Period "24h"}} active{{end}}">24 óra</a>
<a href="?period=7d" class="period-btn{{if or (eq .Period "7d") (eq .Period "")}} active{{end}}">7 nap</a>
<a href="?period=30d" class="period-btn{{if eq .Period "30d"}} active{{end}}">30 nap</a>
</div>
{{if eq .Flash "telemetry_reset"}}
<div class="flash flash-success" style="margin-top: 1rem;">Telemetria sikeresen törölve.</div>
{{end}}
<!-- Overview card -->
<section class="card">
<div style="display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 0.5rem;">
<h2 style="margin: 0;">{{if .Summary}}{{if .Summary.DisplayName}}{{.Summary.DisplayName}}{{else}}{{.AppName}}{{end}}{{else}}{{.AppName}}{{end}}</h2>
<form method="POST" action="/apps/{{.AppName}}/reset-telemetry{{if .Period}}?period={{.Period}}{{end}}"
onsubmit="return confirm('Biztosan törlöd a(z) {{.AppName}} összes telemetriai adatát? Ez nem vonható vissza.');">
<input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
<button type="submit" class="btn btn-sm btn-danger">Telemetria törlése</button>
</form>
</div>
<div class="info-grid">
<div class="info-item">
<span class="label">App neve</span>
<span class="value" style="font-family: var(--font-mono)">{{.AppName}}</span>
</div>
{{if .Summary}}
<div class="info-item">
<span class="label">Telepítések</span>
<span class="value">{{.Summary.DeploymentCount}}</span>
</div>
<div class="info-item">
<span class="label">Katalógus becslés</span>
<span class="value">{{if .Summary.CatalogEstimate}}{{.Summary.CatalogEstimate}}{{else}}—{{end}}</span>
</div>
<div class="info-item">
<span class="label">Katalógus limit</span>
<span class="value">{{if .Summary.CatalogLimit}}{{.Summary.CatalogLimit}}{{else}}—{{end}}</span>
</div>
{{if .SuggestedLimit}}
<div class="info-item">
<span class="label">Javasolt limit (P95×1.2)</span>
<span class="value" style="color: var(--yellow)">{{.SuggestedLimit}} MB</span>
</div>
{{end}}
<div class="info-item">
<span class="label">Átl. memória</span>
<span class="value">{{formatFloat .Summary.AvgMemoryMB}} MB</span>
</div>
<div class="info-item">
<span class="label">P95 memória</span>
<span class="value {{accuracyClass .Summary.P95MemoryMB .Summary.CatalogLimit}}">{{formatFloat .Summary.P95MemoryMB}} MB</span>
</div>
<div class="info-item">
<span class="label">Átl. CPU</span>
<span class="value">{{formatFloat .Summary.AvgCPU}}%</span>
</div>
{{end}}
</div>
</section>
<!-- Memory trend chart -->
<section class="card">
<h2>Memória trend</h2>
<div class="chart-container">
<canvas id="memoryChart"></canvas>
</div>
<script>
(function() {
var chartData = {{json .ChartData}};
if (!chartData || !chartData.labels || chartData.labels.length === 0) {
document.getElementById('memoryChart').parentElement.innerHTML = '<p class="text-muted">Nincs elegendő adat a grafikonhoz.</p>';
return;
}
var ctx = document.getElementById('memoryChart').getContext('2d');
var datasets = [
{
label: 'Átl. memória (MB)',
data: chartData.avg_memory,
borderColor: '#60a5fa',
backgroundColor: 'rgba(96,165,250,0.1)',
fill: true,
tension: 0.3,
pointRadius: 2
},
{
label: 'Csúcs memória (MB)',
data: chartData.peak_memory,
borderColor: '#f87171',
backgroundColor: 'transparent',
fill: false,
tension: 0.3,
pointRadius: 2,
borderDash: [4, 2]
}
];
if (chartData.catalog_limit > 0) {
datasets.push({
label: 'Katalógus limit',
data: chartData.labels.map(function() { return chartData.catalog_limit; }),
borderColor: '#4ade80',
backgroundColor: 'transparent',
fill: false,
pointRadius: 0,
borderWidth: 1,
borderDash: [6, 4]
});
}
new Chart(ctx, {
type: 'line',
data: { labels: chartData.labels, datasets: datasets },
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: { labels: { color: '#94a3b8', font: { size: 11 } } }
},
scales: {
x: { ticks: { color: '#64748b', font: { size: 10 }, maxTicksLimit: 10 }, grid: { color: 'rgba(100,116,139,0.15)' } },
y: { ticks: { color: '#64748b', font: { size: 10 } }, grid: { color: 'rgba(100,116,139,0.15)' }, title: { display: true, text: 'MB', color: '#64748b' } }
}
}
});
})();
</script>
</section>
<!-- Customer breakdown -->
{{if .Customers}}
<section class="card">
<h2>Ügyfél bontás</h2>
<table class="data-table">
<thead>
<tr>
<th>Ügyfél</th>
<th>Átl. memória</th>
<th>Csúcs memória</th>
<th>Átl. CPU</th>
<th>Hibák összesen</th>
<th>Utolsó riport</th>
</tr>
</thead>
<tbody>
{{range .Customers}}
<tr>
<td><a href="/customers/{{.CustomerID}}">{{.CustomerID}}</a></td>
<td>{{formatFloat .AvgMemoryMB}} MB</td>
<td>{{formatFloat .PeakMemoryMB}} MB</td>
<td>{{formatFloat .AvgCPU}}%</td>
<td>{{if gt .TotalErrors 0}}<span class="badge badge-error">{{.TotalErrors}}</span>{{else}}0{{end}}</td>
<td>{{timeAgo .LastReport}}</td>
</tr>
{{end}}
</tbody>
</table>
</section>
{{end}}
<!-- Known issues -->
{{if .Issues}}
<section class="card">
<h2>Ismert hibák</h2>
<table class="data-table">
<thead>
<tr>
<th>Súlyosság</th>
<th>Üzenet</th>
<th>Előfordulások</th>
<th>Érintett ügyfelek</th>
<th>Első észlelés</th>
<th>Utolsó észlelés</th>
</tr>
</thead>
<tbody>
{{range .Issues}}
<tr>
<td>
{{if eq .Severity "error"}}<span class="badge badge-error">error</span>
{{else}}<span class="badge badge-warn">warn</span>{{end}}
</td>
<td style="font-family: var(--font-mono); font-size: 0.8rem; max-width: 40ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;" title="{{.Message}}">{{.Message}}</td>
<td>{{.OccurrenceCount}}</td>
<td>{{len .AffectedCustomers}}</td>
<td>{{timeAgo .FirstSeen}}</td>
<td>{{timeAgo .LastSeen}}</td>
</tr>
{{end}}
</tbody>
</table>
</section>
{{end}}
<footer style="margin-top: 2rem; color: var(--text-muted); font-size: 0.8rem; text-align: center;">
Felhom Hub <span style="font-family: var(--font-mono)">v{{hubVersion}}</span>
</footer>
</div>
</body>
</html>