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

+98 -58
View File
@@ -982,77 +982,117 @@
<!-- Provisionen Tab -->
<div id="provisionen" class="tab-content">
<h2>Provisions-Übersicht</h2>
<!-- Filter für Provisionen -->
<div class="filter-section" style="margin-bottom: 20px; padding: 20px; background: #f8f9fa; border-radius: 8px;">
<h3 style="margin-bottom: 15px; color: #2c3e50;">Filter</h3>
<div style="display: grid; grid-template-columns: repeat(6, 1fr) auto; gap: 15px; align-items: end;">
<div class="form-group" style="margin-bottom: 0;">
<label for="filterAnbieterProvisionen">Anbieter</label>
<select id="filterAnbieterProvisionen">
<div class="flex flex-col gap-lg">
<!-- Page header -->
<div class="flex justify-between items-end flex-wrap gap-md">
<div>
<h1 class="text-headline-md text-on-background mb-xs">Provisions-Übersicht</h1>
<p class="text-body-sm text-on-surface-variant">Auswertung der berechneten Shop- und Mitarbeiter-Provisionen.</p>
</div>
<button onclick="exportProvisionenToPdf()" class="bg-primary-container text-on-primary hover:bg-primary transition-colors rounded-lg px-md py-sm text-body-sm flex items-center gap-sm shadow-sm">
<span class="material-symbols-outlined text-[18px]">download</span>
PDF-Export
</button>
</div>
<!-- Filter bar -->
<div class="bg-surface-container-lowest border border-outline-variant rounded-lg p-md shadow-[0_2px_4px_rgba(0,0,0,0.04)] flex flex-wrap items-end gap-md">
<div class="flex flex-col gap-xs w-[180px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterAnbieterProvisionen">Anbieter</label>
<select id="filterAnbieterProvisionen" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<option value="">Alle Anbieter</option>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<label for="filterTarifProvisionen">Tarif</label>
<select id="filterTarifProvisionen">
<div class="flex flex-col gap-xs w-[180px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterTarifProvisionen">Tarif</label>
<select id="filterTarifProvisionen" class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm text-body-sm text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<option value="">Alle Tarife</option>
</select>
</div>
<div class="form-group" style="margin-bottom: 0;">
<label for="filterSchaltungsIdProvisionen">Schaltungs-ID</label>
<input type="text" id="filterSchaltungsIdProvisionen" placeholder="ID1, ID2, ID3... (kommagetrennt)">
<div class="flex flex-col gap-xs w-[160px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterSchaltungsIdProvisionen">Schaltungs-ID</label>
<input type="text" id="filterSchaltungsIdProvisionen" placeholder="ID1, ID2…"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div>
<div class="form-group" style="margin-bottom: 0;">
<label for="filterRufnummerProvisionen">Rufnummer</label>
<input type="text" id="filterRufnummerProvisionen" placeholder="Rufnummer suchen...">
<div class="flex flex-col gap-xs w-[160px]">
<label class="text-label-caps text-on-surface-variant uppercase" for="filterRufnummerProvisionen">Rufnummer</label>
<input type="text" id="filterRufnummerProvisionen" placeholder="Suche…"
class="w-full bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-label-numeric text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div>
<div class="form-group" style="margin-bottom: 0;">
<label for="filterDatumVonProvisionen">Datum von</label>
<input type="date" id="filterDatumVonProvisionen">
<div class="flex flex-col gap-xs w-[320px]">
<label class="text-label-caps text-on-surface-variant uppercase">Zeitraum</label>
<div class="flex items-center gap-sm">
<input type="date" id="filterDatumVonProvisionen"
class="flex-1 min-w-0 bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
<span class="text-outline-variant shrink-0">–</span>
<input type="date" id="filterDatumBisProvisionen"
class="flex-1 min-w-0 bg-surface-container-lowest border border-outline-variant rounded h-8 px-sm font-label-numeric text-[12px] text-on-surface focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary">
</div>
</div>
<div class="form-group" style="margin-bottom: 0;">
<label for="filterDatumBisProvisionen">Datum bis</label>
<input type="date" id="filterDatumBisProvisionen">
<button type="button" onclick="clearFiltersProvisionen()"
class="bg-surface-container-high text-on-surface border border-outline-variant hover:bg-surface-variant transition-colors rounded px-md h-8 text-body-sm flex items-center gap-xs">
<span class="material-symbols-outlined text-[16px]">restart_alt</span>
Zurücksetzen
</button>
</div>
<!-- KPI cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-md">
<!-- Shop -->
<div class="bg-surface-container-lowest border border-outline-variant rounded-lg p-md shadow-[0_2px_4px_rgba(0,0,0,0.04)] flex flex-col gap-sm relative overflow-hidden">
<div class="absolute right-0 top-0 w-16 h-16 bg-surface-container-low rounded-bl-full opacity-50 pointer-events-none"></div>
<h3 class="text-label-caps text-on-surface-variant uppercase tracking-widest flex items-center gap-xs">
<span class="material-symbols-outlined text-outline text-[14px]">storefront</span>
Shop-Provision gesamt
</h3>
<div class="flex justify-end items-baseline gap-xs">
<span id="totalShopProvision" class="font-label-numeric text-display-lg text-on-surface leading-none">0,00 €</span>
</div>
</div>
<div>
<button type="button" onclick="clearFiltersProvisionen()" class="btn-secondary" style="padding: 12px 20px;">Zurücksetzen</button>
<!-- Mitarbeiter -->
<div class="bg-surface-container-lowest border border-outline-variant rounded-lg p-md shadow-[0_2px_4px_rgba(0,0,0,0.04)] flex flex-col gap-sm relative overflow-hidden">
<div class="absolute right-0 top-0 w-16 h-16 bg-surface-container-low rounded-bl-full opacity-50 pointer-events-none"></div>
<h3 class="text-label-caps text-on-surface-variant uppercase tracking-widest flex items-center gap-xs">
<span class="material-symbols-outlined text-outline text-[14px]">badge</span>
Mitarbeiter-Provision gesamt
</h3>
<div class="flex justify-end items-baseline gap-xs">
<span id="totalMitarbeiterProvision" class="font-label-numeric text-display-lg text-on-surface leading-none">0,00 €</span>
</div>
</div>
<!-- Gesamt highlighted -->
<div class="bg-surface-bright border-2 border-primary/20 rounded-lg p-md shadow-[0_4px_12px_rgba(37,99,235,0.08)] flex flex-col gap-sm relative overflow-hidden">
<div class="absolute right-0 top-0 w-24 h-24 bg-primary/5 rounded-bl-full pointer-events-none"></div>
<h3 class="text-label-caps text-primary uppercase tracking-widest flex items-center gap-xs">
<span class="material-symbols-outlined text-primary text-[14px]">account_balance_wallet</span>
Gesamt-Provision
</h3>
<div class="flex justify-end items-baseline gap-xs">
<span id="totalGesamtProvision" class="font-label-numeric text-display-lg text-primary leading-none font-bold">0,00 €</span>
</div>
</div>
</div>
</div>
<div class="admin-section">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 20px;">
<div>
<h3>Shop-Provision: <span id="totalShopProvision">0,00 €</span></h3>
<h3>Mitarbeiter-Provision: <span id="totalMitarbeiterProvision">0,00 €</span></h3>
<!-- Data table -->
<div class="bg-surface-container-lowest border border-outline-variant rounded-lg shadow-[0_2px_4px_rgba(0,0,0,0.04)] overflow-hidden">
<div class="overflow-x-auto">
<table id="provisionenTable" class="w-full text-left border-collapse">
<thead class="bg-surface-container-high border-b-2 border-outline-variant">
<tr>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase text-right w-[80px]">ID</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase w-[110px]">Datum</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase">Kunde</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase">Anbieter</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase">Tarif</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase text-right w-[140px]">Shop-Prov.</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase text-right w-[140px]">MA-Prov.</th>
<th class="px-md py-sm text-label-caps text-on-surface-variant uppercase text-center w-[60px]">Quelle</th>
</tr>
</thead>
<tbody class="text-body-sm text-on-surface"></tbody>
</table>
</div>
<div>
<button onclick="exportProvisionenToPdf()" class="btn-success" style="padding: 12px 20px; font-size: 14px;">
📄 PDF Export
</button>
</div>
</div>
<div class="table-container">
<table id="provisionenTable">
<thead>
<tr>
<th rowspan="2">Schaltungs-ID</th>
<th rowspan="2">Datum</th>
<th rowspan="2">Kunde</th>
<th rowspan="2">Anbieter</th>
<th rowspan="2">Tarif</th>
<th style="text-align: center; border-bottom: 1px solid #ccc;">Shop-Provision</th>
<th style="text-align: center; border-bottom: 1px solid #ccc;">Mitarbeiter-Provision</th>
</tr>
<tr>
<th>Betrag</th>
<th>Betrag</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
+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