0

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

null
    • Rafael_Sanchis
    • vor 3 Tagen
    • Gemeldet - anzeigen

    I use this HTML/JAVA and there's no problem, there aren't many records, about 2500 without problems. Dummy screen

    • Gotje_Ing
    • vor 3 Tagen
    • Gemeldet - anzeigen

    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.

      • Paul_J_Herberhold
      • vor 3 Tagen
      • Gemeldet - anzeigen

          

      Vielen Dank für eure Einschätzung – das beruhigt mich sehr. 😊
      Aktuell verbringe ich meine Nächte damit, Ninox schöner und funktionaler zu machen, und bin wirklich begeistert von den Möglichkeiten (gestern habe ich sogar einen WhatsApp-ähnlichen Chat hinbekommen 😄). Es wäre schade gewesen, wenn man das am Ende nicht hätte nutzen können. Aber jetzt bin ich zuversichtlich.

      Danke euch nochmals und einen wunderbaren Tag! 

      ------

      Thank you so much for your feedback – that’s really reassuring. 😊
      Lately, I’ve been spending my nights making Ninox nicer and more functional, and I’m truly amazed by what’s possible (yesterday I even managed to build a WhatsApp-like chat 😄). It would have been a pity if we couldn’t use it in the end, but now I’m feeling confident.

      Thanks again, and have a wonderful day!

Content aside

  • vor 3 TagenZuletzt aktiv
  • 3Antworten
  • 39Ansichten
  • 3 Folge bereits