UI redesign step 3: Provisionen in Stitch style

- Page header with title/subtitle on the left, primary PDF-Export
  pill on the right.
- Filter bar matches the Übersicht layout: fixed-width fields with
  label-caps labels, mono inputs for IDs/phone/date, range picker
  in a 320px slot, Zurücksetzen pill on the right.
- Three KPI cards above the table: Shop-Provision and
  Mitarbeiter-Provision in plain surface cards, total highlighted
  with a primary-tinted border, shadow and bold accent number. All
  use the JetBrains-Mono display-lg numeric font.
- Data table follows the same shell as Übersicht: sticky surface-
  container-high header, label-caps column titles, 40px rows with
  zebra stripes and hover. Anbieter renders as a primary pill. Shop-
  and MA-amounts right-aligned and monospaced; "Quelle" replaced
  with a single info glyph tooltip showing the source description.
- New formatEuro() helper renders amounts as "1.234,56 €" using
  toLocaleString('de-DE'), so the totals and the cells share a
  consistent German currency format.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kerimatilganandClaude Opus 4.7 committed 2026-05-15 18:18:59 +02:00
1 parent 45c0c0047f
commit b88938a1f6
2 files changed
+144 -91

No files matched your search

+46 -33
View File
@@ -2285,50 +2285,63 @@ function updateSchaltungenTable(schaltungen) {
renderSchaltungenRows(schaltungen);
}
// Update Provisionen table with filtered data
// Format a number as German currency: 1234.5 -> "1.234,50 €"
function formatEuro(value) {
const n = Number(value) || 0;
return n.toLocaleString('de-DE', { minimumFractionDigits: 2, maximumFractionDigits: 2 }) + ' €';
}
// Update Provisionen table with filtered data (Stitch style).
function updateProvisionenTable(schaltungen, totalShopProvision, totalMitarbeiterProvision) {
const tbody = document.querySelector('#provisionenTable tbody');
const totalShopSpan = document.getElementById('totalShopProvision');
const totalMitarbeiterSpan = document.getElementById('totalMitarbeiterProvision');
const totalGesamtSpan = document.getElementById('totalGesamtProvision');
if (!tbody) return;
tbody.innerHTML = '';
if (schaltungen.length === 0) {
// Show "no results" message
const row = document.createElement('tr');
row.innerHTML = `
<td colspan="11" style="text-align: center; padding: 40px; color: #7f8c8d; font-style: italic; font-size: 16px;">
💰 Keine Provisionen entsprechen den gewählten Filterkriterien
</td>
`;
tbody.appendChild(row);
// Reset totals to 0
if (totalShopSpan) totalShopSpan.textContent = '0,00 €';
if (totalMitarbeiterSpan) totalMitarbeiterSpan.textContent = '0,00 €';
if (!schaltungen || schaltungen.length === 0) {
const tr = document.createElement('tr');
tr.innerHTML = '<td colspan="8" class="px-md py-lg text-center text-body-sm text-on-surface-variant">Keine Provisionen entsprechen den Filterkriterien</td>';
tbody.appendChild(tr);
if (totalShopSpan) totalShopSpan.textContent = formatEuro(0);
if (totalMitarbeiterSpan) totalMitarbeiterSpan.textContent = formatEuro(0);
if (totalGesamtSpan) totalGesamtSpan.textContent = formatEuro(0);
return;
}
schaltungen.forEach(s => {
const row = document.createElement('tr');
row.innerHTML = `
<td>${escapeHtml(s.schaltungs_id)}</td>
<td>${escapeHtml(formatDate(s.schaltungs_datum))}</td>
<td>${escapeHtml(s.vorname)} ${escapeHtml(s.nachname)}</td>
<td>${escapeHtml(s.anbieter_name)}</td>
<td>${escapeHtml(s.tarif_name)}</td>
<td><strong>${escapeHtml(s.berechnete_shop_provision || '0.00')} €</strong></td>
<td><strong>${escapeHtml(s.berechnete_mitarbeiter_provision || '0.00')} €</strong></td>
schaltungen.forEach(s => {
const shop = Number(s.berechnete_shop_provision) || 0;
const ma = Number(s.berechnete_mitarbeiter_provision) || 0;
const quelle = s.shop_quelle || s.quelle || '';
const tr = document.createElement('tr');
tr.className = 'border-b border-outline-variant/30 even:bg-surface-container-low/50 hover:bg-surface-variant/30 transition-colors';
tr.innerHTML = `
<td class="px-md py-xs font-label-numeric text-label-numeric text-right text-on-surface-variant">${escapeHtml(s.schaltungs_id)}</td>
<td class="px-md py-xs font-label-numeric text-label-numeric text-on-surface">${escapeHtml(formatDate(s.schaltungs_datum))}</td>
<td class="px-md py-xs">
<div class="font-semibold text-on-background">${escapeHtml((s.vorname || '') + ' ' + (s.nachname || ''))}</div>
<div class="text-[12px] text-on-surface-variant">${escapeHtml(s.firma || '–')}</div>
</td>
<td class="px-md py-xs">
<span class="bg-primary/10 text-primary border border-primary/20 px-2 py-0.5 rounded-full text-label-caps">${escapeHtml(s.anbieter_name || '')}</span>
</td>
<td class="px-md py-xs">${escapeHtml(s.tarif_name || '')}</td>
<td class="px-md py-xs font-label-numeric text-label-numeric text-right text-on-surface font-semibold">${escapeHtml(formatEuro(shop))}</td>
<td class="px-md py-xs font-label-numeric text-label-numeric text-right text-on-surface font-semibold">${escapeHtml(formatEuro(ma))}</td>
<td class="px-md py-xs text-center">
<span class="material-symbols-outlined text-outline hover:text-primary transition-colors text-[18px] cursor-help" title="${escapeHtml(quelle)}">info</span>
</td>
`;
tbody.appendChild(row);
tbody.appendChild(tr);
});
// Update totals
if (totalShopSpan) totalShopSpan.textContent = `${totalShopProvision.toFixed(2)} €`;
if (totalMitarbeiterSpan) totalMitarbeiterSpan.textContent = `${totalMitarbeiterProvision.toFixed(2)} €`;
const totalGesamt = (totalShopProvision || 0) + (totalMitarbeiterProvision || 0);
if (totalShopSpan) totalShopSpan.textContent = formatEuro(totalShopProvision);
if (totalMitarbeiterSpan) totalMitarbeiterSpan.textContent = formatEuro(totalMitarbeiterProvision);
if (totalGesamtSpan) totalGesamtSpan.textContent = formatEuro(totalGesamt);
}
// Clear filters functions