Erfahrungen mit langen HTML-Skripten als Dashboard
Hallo liebes Forum,
ich sitze gerade daran unsere Daten für das neue Jahr etwas übersichtlicher nachzuhalten und vor allem anzeigen zu lassen.Dafür arbeite ich an einem Html-Dashboard. Das läuft eigentlich ziemlich gut. Ich frage mich nur, ob man damit irgendwann ans Leistungslimit kommt.
Daher Frage ich euch nach euren Erfahrungen, laufen auch lange Html-Scripte zuverlässig oder gibt es irgendwann Probleme? Meinen aktuellen Code füge ich gerne an:
Ich freu mich auf euer Feedback.
Beste Grüße
Paul
let nachrichten := ((select Nachrichten) order by -'Gesendet am');
let nachrichten_ungelesen := ((select Nachrichten where Status = "Ungelesen") order by -'Gesendet am');
let nachrichten_gelesen := ((select Nachrichten where Status = "Gelesen") order by -'Gesendet am');
if count(nachrichten_ungelesen) = 0 and count(nachrichten) > 0 then
nachrichten_ungelesen := ((select Nachrichten where Status = "ungelesen") order by -'Gesendet am');
nachrichten_gelesen := ((select Nachrichten where Status = "gelesen") order by -'Gesendet am');
if count(nachrichten_ungelesen) = 0 then
nachrichten_ungelesen := ((select Nachrichten where Status = 1) order by -'Gesendet am');
nachrichten_gelesen := ((select Nachrichten where Status = 0) order by -'Gesendet am')
end
end;
html("
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'Poppins', Arial, sans-serif;
background: #e5e5e5;
padding: 100px 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
}
.cards-row {
display: grid;
grid-template-columns: 300px 1fr 1fr;
grid-template-rows: 1fr 1fr;
gap: 30px;
align-items: stretch;
margin-bottom: 40px;
}
.card {
background: white;
border-radius: 15px;
padding: 40px 30px;
text-align: center;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 30px rgba(0, 0, 0, 0.15);
}
.card.large {
grid-row: 1 / 3;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
padding: 40px 30px;
}
.card.large:hover {
transform: none;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
}
.profile-image {
width: 120px;
height: 120px;
border-radius: 50%;
object-fit: cover;
margin-bottom: 25px;
border: 3px solid #f0f0f0;
}
.welcome-title {
font-size: 48px !important;
font-weight: 600;
color: #009FE3;
margin-bottom: 80px !important;
font-family: 'Poppins', Arial, sans-serif;
}
.quicklinks-title {
font-size: 16px !important;
font-weight: 600;
color: #333;
margin-bottom: 20px;
align-self: flex-start;
font-family: 'Poppins', Arial, sans-serif;
}
.quicklinks-list {
list-style: none;
width: 100%;
margin: 0;
padding: 0;
}
.quicklinks-item {
padding: 0px 0;
color: #666;
font-size: 15px;
font-family: 'Poppins', Arial, sans-serif;
cursor: pointer;
transition: color 0.3s ease;
text-align: left;
}
.quicklinks-item:hover {
color: #009FE3;
}
.card-icon {
font-size: 48px;
color: #7dd3fc;
margin-bottom: 25px;
display: block;
}
.card-text {
font-size: 15px;
color: #6b7280;
line-height: 1.6;
margin: 0;
}
/* Tab System */
.main-tab {
flex: 1;
padding: 12px 20px;
text-align: center;
border-radius: 8px;
background: transparent;
color: #6b7280;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
}
.main-tab.active {
background: white;
color: #009FE3;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.tab-content {
display: none;
}
.tab-content.active {
display: block;
}
/* Filter Tabs */
.filter-tab {
padding: 10px 20px;
background: transparent;
color: #6b7280;
border-radius: 25px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
margin-left: 10px;
}
.filter-tab.active {
background: #3b82f6;
color: white;
}
.filter-tab:first-child {
margin-left: 0;
}
/* Nachrichten */
.message-item {
background: #fefefe;
border-radius: 12px;
padding: 20px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
cursor: pointer;
transition: all 0.2s ease;
margin-bottom: 15px;
}
.message-item:hover {
transform: translateX(5px);
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}
.message-item.ungelesen {
border-left: 4px solid #ef4444;
}
.message-item.gelesen {
border-left: 4px solid #10b981;
}
.message-item.hidden {
display: none !important;
}
@media (max-width: 1200px) {
.cards-row {
grid-template-columns: 280px 1fr 1fr;
}
}
@media (max-width: 1024px) {
.cards-row {
grid-template-columns: 1fr;
grid-template-rows: auto;
}
.card.large {
grid-row: auto;
}
}
</style> <div class='container'>
<!-- Obere 5 Karten -->
<div class='cards-row'>
<div class='card large'>
<img src='https://recycle-motorradtransport.de/wp-content/uploads/manueltasch.webp' alt='Profilbild' class='profile-image'>
<h2 class='welcome-title'>Hey Paul!</h2>
<h3 class='quicklinks-title'>Quicklinks</h3>
<ul class='quicklinks-list'>
<li class='quicklinks-item'>Angebote</li>
<li class='quicklinks-item'>Aufträge</li>
<li class='quicklinks-item'>Rechnungen</li>
<li class='quicklinks-item'>Werbemedien</li>
<li class='quicklinks-item'>Lager</li>
<li class='quicklinks-item'>Nachricht schreiben</li>
<li class='quicklinks-item'>Aufgabe hinterlegen</li>
</ul>
</div>
<div class='card'>
<div style='position: relative; display: inline-block;'>
<span class='card-icon'>
</span>
<div style='position: absolute; top: -8px; right: -2px; background: #dc2626; color: white; border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: bold;'>" +
count(nachrichten_ungelesen) +
"</div>
</div>
<h3 style='font-size: 32px !important; font-weight: 600 !important; color: #009FE3; margin-bottom: 20px; line-height: 1.2; text-align: center;'>Inbox</h3>
<p class='card-text'>Du hast <strong>" +
count(nachrichten_ungelesen) +
" ungelesene Nachrichten</strong> in deinem Posteingang</p>
</div>
<div class='card'>
<span class='card-icon'>
</span>
<h3 style='font-size: 32px !important; font-weight: 600 !important; color: #009FE3; margin-bottom: 20px; line-height: 1.2; text-align: center;'>5. KW</h3>
<p class='card-text'>20.01. bis 26.01.2025</p>
</div>
<div class='card'>
<div style='position: relative; display: inline-block;'>
<span class='card-icon'>
</span>
<div style='position: absolute; top: -8px; right: -2px; background: #dc2626; color: white; border-radius: 50%; width: 20px; height: 20px; display: flex; align-items: center; justify-content: center; font-size: 11px; font-weight: bold;'>5</div>
</div>
<h3 style='font-size: 32px !important; font-weight: 600 !important; color: #009FE3; margin-bottom: 20px; line-height: 1.2; text-align: center;'>To-Dos</h3>
<p class='card-text'>Du hast <strong>5 offene Aufgaben</strong></p>
</div>
<div class='card'>
<span class='card-icon'>
</span>
<h3 style='font-size: 32px !important; font-weight: 600 !important; color: #009FE3; margin-bottom: 20px; line-height: 1.2; text-align: center;'>Dein Aushang</h3>
<div class='card-text' style='text-align: center; line-height: 1.8;'>
<strong>100</strong> Rahmen an Straßenlaternen, <strong>100</strong> Werbetafeln, <strong>10</strong> Banner
</div>
</div>
</div>
</div> <!-- Großes Tab-Modul -->
<div style='max-width: 1200px; margin: 40px auto 0 auto; padding: 0 20px;'>
<div style='background: white; border-radius: 15px; padding: 30px; box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);'>
<!-- Tab Navigation -->
<div style='display: flex; background: #f3f4f6; border-radius: 10px; padding: 4px; margin-bottom: 30px; gap: 4px;'>
<div class='main-tab active' data-tab='nachrichten'>Nachrichten</div>
<div class='main-tab' data-tab='todos'>To-Dos</div>
<div class='main-tab' data-tab='woche'>Aktuelle Woche</div>
</div>
<!-- NACHRICHTEN TAB -->
<div class='tab-content active' id='nachrichten'>
<!-- Header -->
<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;'>
<h2 style='font-size: 28px; font-weight: 600; color: #111827; margin: 0;'>Nachrichten</h2>
<div style='display: flex; align-items: center; gap: 20px;'>
<div style='display: flex; align-items: center; gap: 8px; color: #6b7280;'>
<span style='font-size: 18px;'>
</span>
<span style='font-size: 16px; color: #3b82f6;' id='message-count'>" +
count(nachrichten) +
" Nachrichten</span>
</div>
<span style='color: #3b82f6; font-size: 14px; cursor: pointer;'>View All</span>
</div>
</div>
<!-- Filter Tabs -->
<div style='display: flex; gap: 0; margin-bottom: 30px;'>
<div class='filter-tab active' data-filter='alle' data-count='" +
count(nachrichten) +
"'>Alle (" +
count(nachrichten) +
")</div>
<div class='filter-tab' data-filter='ungelesen' data-count='" +
count(nachrichten_ungelesen) +
"'>Ungelesen (" +
count(nachrichten_ungelesen) +
")</div>
</div>
<!-- Nachrichten Grid -->
<div style='display: grid; grid-template-columns: 1fr 1fr; gap: 20px;' id='messages-container'>
" +
join(for nachricht in nachrichten do
let statusValue := text(nachricht.Status);
let isUngelesen := statusValue = "Ungelesen" or statusValue = "ungelesen" or statusValue = "1";
let statusClass := if isUngelesen then "ungelesen" else "gelesen" end;
let statusColor := if isUngelesen then "#ef4444" else "#10b981" end;
let statusText := if isUngelesen then "Ungelesen" else "Gelesen" end;
"<div class='message-item " + statusClass + "' data-status='" + statusClass +
"' onclick=ui.popupRecord('" +
text(raw(nachricht)) +
"')>" +
"<div style='display: flex; align-items: flex-start; gap: 15px;'>" +
"<div style='background: #3b82f6; border-radius: 8px; padding: 8px; flex-shrink: 0;'>" +
"<span style='color: white; font-size: 16px;'>
</span></div>" +
"<div style='flex: 1;'>" +
"<div style='font-weight: 600; color: #111827; font-size: 16px; margin-bottom: 5px;'>" +
text(nachricht.Betreff) +
"</div>" +
"<div style='color: #6b7280; font-size: 14px; margin-bottom: 8px;'>" +
text(nachricht.Absender) +
" • " +
text(nachricht.'Gesendet am') +
"</div>" +
"<div style='display: flex; align-items: center; gap: 5px;'>" +
"<div style='width: 8px; height: 8px; border-radius: 50%; background-color: " +
statusColor +
";'></div>" +
"<span style='color: #6b7280; font-size: 13px;'>" +
statusText +
"</span>" +
"<span style='color: #9ca3af; font-size: 11px; margin-left: 10px;'>(Original: " +
statusValue +
")</span>" +
"</div></div></div></div>"
end, "") +
"
</div>
</div>
<!-- TO-DOS TAB -->
<div class='tab-content' id='todos'>
<div style='text-align: center; padding: 40px;'>
<h2 style='font-size: 28px; font-weight: 600; color: #111827; margin-bottom: 20px;'>
To-Dos</h2>
<p style='color: #6b7280; margin-bottom: 10px;'>To-Dos Bereich ist aktiv!</p>
<p style='color: #6b7280; font-size: 14px;'>Hier werden Ihre offenen Aufgaben angezeigt</p>
</div>
</div>
<!-- AKTUELLE WOCHE TAB -->
<div class='tab-content' id='woche'>
<div style='text-align: center; padding: 40px;'>
<h2 style='font-size: 28px; font-weight: 600; color: #111827; margin-bottom: 20px;'>
Aktuelle Woche</h2>
<p style='color: #6b7280; margin-bottom: 10px;'>Wochenplanung ist aktiv!</p>
<p style='color: #6b7280; font-size: 14px;'>Hier sehen Sie Ihre Wochenübersicht</p>
</div>
</div>
</div>
</div> <script>
// Nur Tab-Switching und Filter - KEINE message-item Events
document.addEventListener('click', function(e) {
// Main Tabs
if (e.target.classList.contains('main-tab')) {
document.querySelectorAll('.main-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
e.target.classList.add('active');
const tabName = e.target.getAttribute('data-tab');
const content = document.getElementById(tabName);
if (content) {
content.classList.add('active');
}
return;
}
// Filter Tabs für Nachrichten
if (e.target.classList.contains('filter-tab')) {
document.querySelectorAll('.filter-tab').forEach(t => t.classList.remove('active'));
e.target.classList.add('active');
const selectedFilter = e.target.getAttribute('data-filter');
const selectedCount = e.target.getAttribute('data-count');
// Update counter
const counterElement = document.getElementById('message-count');
if (counterElement) {
counterElement.textContent = selectedCount + ' Nachrichten';
}
// Show/hide messages based on status
document.querySelectorAll('.message-item').forEach(item => {
const itemStatus = item.getAttribute('data-status');
let shouldShow = false;
if (selectedFilter === 'alle') {
shouldShow = true;
} else if (selectedFilter === 'ungelesen') {
shouldShow = (itemStatus === 'ungelesen');
}
if (shouldShow) {
item.classList.remove('hidden');
item.style.display = 'block';
} else {
item.classList.add('hidden');
item.style.display = 'none';
}
});
return;
}
});
</script>
")
3 Antworten
-
I use this HTML/JAVA and there's no problem, there aren't many records, about 2500 without problems. Dummy screen
-
Moin,
html-Code wird in Ninox Clientseitig ausgeführt. Das heißt, die "Last" der Berechnung trägt dein Browser/Gerät, nicht der Server. Ausnahmen sind hier viele (>>10) database.update oder database.create Calls zur gleichen Zeit, die Ninox dann doch mal in die Knie zwingen können.
Was den Server betrifft müssen die Abfragen der Daten, also die Selects optimiert werden. In deinem Code sehen die aber ok aus.
Wir haben mit unseren Modulen schon Fälle abgebildet, wo wir über 30.000 Records zugleich geladen und angezeigt haben, bei einer Ladezeit < 10 Sekunden.
Die Grenzen sind fließend und hängen von unzähligen Parametern ab. Es ist ein sehr spannendes wenn auch zugleich extrem komplexes Feld. Für deine Anwendung sehe ich aber keine Probleme.
Content aside
- vor 3 TagenZuletzt aktiv
- 3Antworten
- 39Ansichten
-
3
Folge bereits