Simplify dashboard service layout
All checks were successful
Build & Push Docker Image to Gitea Registry / build-and-push (push) Successful in 12s

This commit is contained in:
Kroonk
2026-05-21 17:27:31 +02:00
parent fddb2e3544
commit 2af7e65d4a
3 changed files with 51 additions and 22 deletions

53
app.js
View File

@@ -17,6 +17,7 @@ let cards = [];
let sections = []; let sections = [];
let config = null; let config = null;
let activeFilter = 'all'; let activeFilter = 'all';
let categoryById = new Map();
const searchInput = document.querySelector('#serviceSearch'); const searchInput = document.querySelector('#serviceSearch');
const segments = [...document.querySelectorAll('.segment')]; const segments = [...document.querySelectorAll('.segment')];
@@ -56,6 +57,7 @@ function serviceSearchText(service) {
} }
function createServiceCard(service) { function createServiceCard(service) {
const category = categoryById.get(service.category);
const card = document.createElement('a'); const card = document.createElement('a');
card.href = service.url; card.href = service.url;
card.target = '_blank'; card.target = '_blank';
@@ -72,6 +74,7 @@ function createServiceCard(service) {
</svg> </svg>
</div> </div>
<div class="card-content"> <div class="card-content">
<span class="card-category">${category?.label || 'Dienst'}</span>
<h3>${service.name}</h3> <h3>${service.name}</h3>
<p>${service.description}</p> <p>${service.description}</p>
</div> </div>
@@ -83,32 +86,41 @@ function createServiceCard(service) {
function renderDashboard(nextConfig) { function renderDashboard(nextConfig) {
dashboardSections.innerHTML = ''; dashboardSections.innerHTML = '';
serviceCount.textContent = String(nextConfig.services.length); serviceCount.textContent = String(nextConfig.services.length);
categoryById = new Map(nextConfig.categories.map((category) => [category.id, category]));
nextConfig.categories.forEach((category) => { const section = document.createElement('section');
const services = nextConfig.services.filter((service) => service.category === category.id); section.className = 'section service-overview';
if (!services.length) return; section.innerHTML = `
<div class="section-heading">
const section = document.createElement('section'); <div>
section.className = 'section'; <p class="section-kicker" id="overviewKicker">Dashboard</p>
section.dataset.category = category.id; <h2 id="overviewTitle">Alle Dienste</h2>
section.innerHTML = `
<div class="section-heading">
<div>
<p class="section-kicker">${category.label}</p>
<h2>${category.title}</h2>
</div>
<span class="section-count">${services.length} ${services.length === 1 ? 'Dienst' : 'Dienste'}</span>
</div> </div>
<div class="grid"></div> <span class="section-count" id="overviewCount">${nextConfig.services.length} Dienste</span>
`; </div>
<div class="grid"></div>
`;
const grid = section.querySelector('.grid'); const grid = section.querySelector('.grid');
services.forEach((service) => grid.appendChild(createServiceCard(service))); nextConfig.services.forEach((service) => grid.appendChild(createServiceCard(service)));
dashboardSections.appendChild(section); dashboardSections.appendChild(section);
});
cards = [...document.querySelectorAll('.service-card')]; cards = [...document.querySelectorAll('.service-card')];
sections = [...document.querySelectorAll('.section')]; sections = [...document.querySelectorAll('.section')];
updateOverviewHeading(nextConfig.services.length);
}
function updateOverviewHeading(visibleCards) {
const activeCategory = categoryById.get(activeFilter);
const term = normalize(searchInput.value);
const kicker = document.querySelector('#overviewKicker');
const title = document.querySelector('#overviewTitle');
const count = document.querySelector('#overviewCount');
if (!kicker || !title || !count) return;
kicker.textContent = activeCategory ? activeCategory.label : 'Dashboard';
title.textContent = activeCategory ? activeCategory.title : 'Alle Dienste';
count.textContent = `${visibleCards} ${visibleCards === 1 ? 'Dienst' : 'Dienste'}${term ? ' gefunden' : ''}`;
} }
function updateVisibility() { function updateVisibility() {
@@ -128,6 +140,7 @@ function updateVisibility() {
section.hidden = !hasVisibleCards; section.hidden = !hasVisibleCards;
}); });
updateOverviewHeading(visibleCards);
emptyState.hidden = visibleCards !== 0; emptyState.hidden = visibleCards !== 0;
} }

View File

@@ -13,7 +13,7 @@
<link rel="apple-touch-icon" href="/icons/apple-touch-icon-v2.png"> <link rel="apple-touch-icon" href="/icons/apple-touch-icon-v2.png">
<link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png"> <link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png">
<link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png"> <link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png">
<link rel="stylesheet" href="style.css?v=13"> <link rel="stylesheet" href="style.css?v=18">
</head> </head>
<body> <body>
<header class="shell hero"> <header class="shell hero">
@@ -73,6 +73,6 @@
<span id="lastChecked">Status wird im Hintergrund geprueft.</span> <span id="lastChecked">Status wird im Hintergrund geprueft.</span>
</footer> </footer>
<script src="/app.js?v=3" defer></script> <script src="/app.js?v=4" defer></script>
</body> </body>
</html> </html>

View File

@@ -217,6 +217,10 @@ main {
margin-top: 18px; margin-top: 18px;
} }
.service-overview {
margin-top: 16px;
}
.section[hidden], .section[hidden],
.service-card[hidden] { .service-card[hidden] {
display: none; display: none;
@@ -342,6 +346,18 @@ main {
min-width: 0; min-width: 0;
} }
.card-category {
display: inline-flex;
width: max-content;
max-width: 100%;
margin-bottom: 5px;
color: var(--dim);
font-size: 0.68rem;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.card-content h3 { .card-content h3 {
color: #fff; color: #fff;
font-size: 1.04rem; font-size: 1.04rem;