v0.4.1: app filtering + clickable dashboard stat cards
Add filter bar (Mind/Futó/Leállítva/Telepíthető) to Alkalmazások page with URL-based filter persistence. Dashboard stat cards are now clickable links that navigate to the filtered stacks view. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,9 +8,16 @@
|
||||
</div>
|
||||
<div id="sync-toast" class="sync-toast" style="display:none"></div>
|
||||
|
||||
<div class="filter-bar" id="filter-bar">
|
||||
<button class="filter-btn active" data-filter="all">Mind <span class="filter-count" id="count-all"></span></button>
|
||||
<button class="filter-btn" data-filter="running">Futó <span class="filter-count" id="count-running"></span></button>
|
||||
<button class="filter-btn" data-filter="stopped">Leállítva <span class="filter-count" id="count-stopped"></span></button>
|
||||
<button class="filter-btn" data-filter="available">Telepíthető <span class="filter-count" id="count-available"></span></button>
|
||||
</div>
|
||||
|
||||
<div class="stack-grid">
|
||||
{{range .Stacks}}
|
||||
<div class="stack-detail-card stack-state-{{stateColor .State}}"{{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
<div class="stack-detail-card stack-state-{{stateColor .State}}" data-filter-state="{{filterCategory .State .Deployed}}"{{if not .Protected}} data-href="/apps/{{.Meta.Slug}}"{{end}}>
|
||||
<div class="stack-detail-header">
|
||||
<div class="stack-title-row">
|
||||
<img class="stack-logo-lg" src="{{logoURL .Meta.Slug}}"
|
||||
@@ -72,5 +79,55 @@
|
||||
{{end}}
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var cards = document.querySelectorAll('.stack-detail-card[data-filter-state]');
|
||||
var counts = {all: cards.length, running: 0, stopped: 0, available: 0};
|
||||
|
||||
cards.forEach(function(card) {
|
||||
var cat = card.getAttribute('data-filter-state');
|
||||
if (counts[cat] !== undefined) counts[cat]++;
|
||||
});
|
||||
|
||||
['all', 'running', 'stopped', 'available'].forEach(function(f) {
|
||||
var el = document.getElementById('count-' + f);
|
||||
if (el) el.textContent = '(' + counts[f] + ')';
|
||||
});
|
||||
|
||||
function applyFilter(filter) {
|
||||
cards.forEach(function(card) {
|
||||
if (filter === 'all' || card.getAttribute('data-filter-state') === filter) {
|
||||
card.style.display = '';
|
||||
} else {
|
||||
card.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.filter-btn').forEach(function(btn) {
|
||||
btn.classList.toggle('active', btn.getAttribute('data-filter') === filter);
|
||||
});
|
||||
|
||||
var url = new URL(window.location);
|
||||
if (filter === 'all') {
|
||||
url.searchParams.delete('filter');
|
||||
} else {
|
||||
url.searchParams.set('filter', filter);
|
||||
}
|
||||
history.replaceState(null, '', url);
|
||||
}
|
||||
|
||||
document.querySelectorAll('.filter-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
applyFilter(this.getAttribute('data-filter'));
|
||||
});
|
||||
});
|
||||
|
||||
var urlFilter = new URLSearchParams(window.location.search).get('filter');
|
||||
if (urlFilter && ['running', 'stopped', 'available'].indexOf(urlFilter) !== -1) {
|
||||
applyFilter(urlFilter);
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
|
||||
{{template "layout_end" .}}
|
||||
{{end}}
|
||||
|
||||
Reference in New Issue
Block a user