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:
1 parent
45c0c0047f
commit
b88938a1f6
2 files changed
+144
-91
No files matched your search
+98
-58
@@ -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
@@ -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
|
||||
|
||||
Reference in new issue
Block a user