Verwaltung: alles in eigene, farblich unterschiedliche "Kisten" gepackt

Auf Feedback ("das ist mir zu einfach, ich will das alles in Kisten ist") die komplette
Auswertungsseite umgebaut: jede zusammengehörige Gruppe (Bestellstatus, Kennzahlen, Alle
Bestellungen, Bestseller je Kategorie, Herkunft der Bestellungen, Stammkundschaft) steckt
jetzt in einer eigenen, klar abgegrenzten Box mit eigenem Icon-Abzeichen, eigener
Akzentfarbe (Cyan/Lila/Gold/Grün/Pink) und dezent pulsierendem Glanzrand — statt lose auf
der Seite zu stehen.

Genauso wichtig: die Leerzustände (bevor die erste echte Bestellung eingeht) sehen jetzt
nicht mehr trostlos aus — großes Icon + freundlicher Text statt nacktem Fließtext, jeweils
passend zur Box (📭 Bestellungen, 🧶 Bestseller, 🗺️ Herkunft, 💌 Stammkundschaft). Mit
echtem Screenshot-Vergleich (befüllt UND leer, per fetch-Mock simuliert) verifiziert.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-03 22:25:09 +02:00
co-authored by Claude Sonnet 5
parent af729ba571
commit e2b89483c2
2 changed files with 230 additions and 100 deletions
+78 -17
View File
@@ -62,6 +62,14 @@ const lang: Locale = "de";
<div id="verwaltung-content" style="display:none;">
<section class="section-tight">
<div class="container">
{/* Jede zusammengehörige Gruppe bekommt jetzt ihre eigene, farblich eigenständige "Kiste"
(siehe .verwaltung-box in global.css) — statt loser Kacheln/Absätze auf der Seite. */}
<div class="verwaltung-box" style="--box-color: var(--c-accent);">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">📊</span>
<h2>Bestellstatus</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-stat-grid">
<button type="button" class="card verwaltung-stat-tile" data-filter="offen">
<span class="verwaltung-stat-icon" aria-hidden="true">🆕</span>
@@ -89,11 +97,19 @@ const lang: Locale = "de";
<span class="verwaltung-stat-label" id="stat-umsatz-label">Gesamtumsatz</span>
</div>
</div>
</div>
</div>
{/* Zweite Kennzahlen-Reihe — Dinge, die im ersten Entwurf fehlten, aber im Alltag sehr
{/* Zweite Kennzahlen-Kiste — Dinge, die im ersten Entwurf fehlten, aber im Alltag sehr
wichtig sind: wie viel kommt gerade neu rein, und wie hoch ist der durchschnittliche
Bestellwert (wichtig für Preis-/Werbeentscheidungen). "Heute"/"7 Tage" sind zusätzlich
anklickbar und setzen direkt den Zeitraum-Filter unten. */}
<div class="verwaltung-box" style="--box-color: #9b7fd6;">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">📈</span>
<h2>Kennzahlen</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-stat-grid verwaltung-stat-grid-secondary">
<button type="button" class="card verwaltung-stat-tile verwaltung-stat-tile-mini" data-zeitraum="heute">
<span class="verwaltung-stat-icon" aria-hidden="true">📅</span>
@@ -117,12 +133,18 @@ const lang: Locale = "de";
</div>
</div>
</div>
</div>
</div>
</section>
<section class="section-tight">
<div class="container">
<h2>📦 Alle Bestellungen</h2>
<div class="verwaltung-box" style="--box-color: var(--c-accent);">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">📦</span>
<h2>Alle Bestellungen</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-new-banner" id="verwaltung-new-banner" style="display:none;">
<span>🔔 <strong id="verwaltung-new-count"></strong> neue Bestellung(en) seit dem letzten Laden</span>
<button type="button" class="btn btn-primary btn-sm" id="verwaltung-new-refresh-btn">Jetzt aktualisieren</button>
@@ -154,37 +176,73 @@ const lang: Locale = "de";
<button type="button" class="verwaltung-filter-btn" data-filter="storniert">Storniert (<span id="filter-count-storniert">0</span>)</button>
</nav>
<p class="small" id="verwaltung-empty" style="display:none;">Noch keine Bestellungen eingegangen.</p>
<div class="verwaltung-box-empty" id="verwaltung-empty" style="display:none;">
<span class="verwaltung-box-empty-icon" aria-hidden="true">📭</span>
<p>Noch keine Bestellungen eingegangen.</p>
<p class="small">Sobald die erste Bestellung im Checkout abgeschickt wird, erscheint sie sofort hier.</p>
</div>
<p class="small" id="verwaltung-no-match" style="display:none;">Keine Bestellung passt zur aktuellen Suche/Filterung.</p>
<div class="order-list" id="verwaltung-order-list"></div>
</div>
</div>
</div>
</section>
<section class="section-tight" id="verwaltung-auswertung-section">
<div class="container">
<h2>🏆 Meistverkaufte Produkte je Kategorie</h2>
<p class="small" id="verwaltung-kat-empty" style="display:none;">Noch keine ausgewerteten Verkäufe (stornierte Bestellungen zählen nicht mit).</p>
<div class="verwaltung-box" style="--box-color: #e3b23c;">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">🏆</span>
<h2>Bestseller je Kategorie</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-box-empty" id="verwaltung-kat-empty" style="display:none;">
<span class="verwaltung-box-empty-icon" aria-hidden="true">🧶</span>
<p>Noch keine ausgewerteten Verkäufe.</p>
<p class="small">Hier siehst du bald, welche Produkte je Kategorie am besten laufen (stornierte Bestellungen zählen nicht mit).</p>
</div>
<div class="verwaltung-auswertung-grid" id="verwaltung-kat-grid"></div>
</div>
</div>
</div>
</section>
<section class="section-tight">
<div class="container">
<h2>🌍 Bestellungen nach Land</h2>
<div class="card verwaltung-kat-card">
<div class="verwaltung-box" style="--box-color: #6ecb8f;">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">🌍</span>
<h2>Herkunft der Bestellungen</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-box-empty" id="verwaltung-land-empty" style="display:none;">
<span class="verwaltung-box-empty-icon" aria-hidden="true">🗺️</span>
<p>Noch keine Bestellungen.</p>
<p class="small">Sobald Bestellungen eingehen, siehst du hier den Anteil je Land (DE/AT/CH/LU).</p>
</div>
<div class="verwaltung-bar-list" id="verwaltung-land-list"></div>
</div>
</div>
</div>
</section>
<section class="section-tight">
<div class="container">
<h2>👑 Beste Kundinnen</h2>
<p class="small" id="verwaltung-kunden-empty" style="display:none;">Noch keine wiederkehrende Kundschaft.</p>
<div class="card verwaltung-kat-card" id="verwaltung-kunden-card" style="display:none;">
<div class="verwaltung-box" style="--box-color: var(--c-sale);">
<div class="verwaltung-box-head">
<span class="verwaltung-box-icon" aria-hidden="true">👑</span>
<h2>Stammkundschaft</h2>
</div>
<div class="verwaltung-box-body">
<div class="verwaltung-box-empty" id="verwaltung-kunden-empty" style="display:none;">
<span class="verwaltung-box-empty-icon" aria-hidden="true">💌</span>
<p>Noch keine wiederkehrende Kundschaft.</p>
<p class="small">Sobald jemand zum zweiten Mal bestellt, erscheint sie hier — sortiert nach bisherigem Umsatz.</p>
</div>
<div class="verwaltung-bar-list" id="verwaltung-kunden-list"></div>
</div>
</div>
</div>
</section>
</div>
</Layout>
@@ -482,14 +540,19 @@ const lang: Locale = "de";
}));
const landList = document.getElementById("verwaltung-land-list");
landList.innerHTML = auswertungLaender.length === 0
? `<p class="small">Noch keine Bestellungen.</p>`
: auswertungLaender.map((l) => `
const landEmpty = document.getElementById("verwaltung-land-empty");
if (auswertungLaender.length === 0) {
landList.innerHTML = "";
landEmpty.style.display = "";
} else {
landEmpty.style.display = "none";
landList.innerHTML = auswertungLaender.map((l) => `
<div class="verwaltung-bar-row">
<div class="verwaltung-bar-head"><span>${l.flagge} ${escapeHtml(l.name)}</span><span class="small">${l.count} Bestellungen · ${l.prozent}%</span></div>
<div class="verwaltung-bar-track"><div class="verwaltung-bar-fill verwaltung-bar-fill-land" style="width:${l.balken}%"></div></div>
</div>
`).join("");
}
// ── NEU: Beste Kundinnen (Umsatz pro E-Mail, nur wer mehr als 1× bestellt hat) ──
const umsatzProKundin = new Map(); // email -> { name, summe, anzahl }
@@ -505,14 +568,12 @@ const lang: Locale = "de";
.sort((a, b) => b.summe - a.summe)
.slice(0, 8);
const maxKundenUmsatz = Math.max(1, ...besteKunden.map((k) => k.summe));
const kundenCard = document.getElementById("verwaltung-kunden-card");
const kundenEmpty = document.getElementById("verwaltung-kunden-empty");
const kundenList = document.getElementById("verwaltung-kunden-list");
if (besteKunden.length === 0) {
kundenCard.style.display = "none";
kundenList.innerHTML = "";
kundenEmpty.style.display = "";
} else {
kundenCard.style.display = "";
kundenEmpty.style.display = "none";
kundenList.innerHTML = besteKunden.map((k) => `
<div class="verwaltung-bar-row">
+69
View File
@@ -2861,6 +2861,75 @@ a:focus-visible, button:focus-visible {
/* Interne Bestellverwaltung (/verwaltung/) — Status-Kacheln, Filterleiste, Auswertungs-Balken. */
/* "Kisten" — jede zusammengehörige Gruppe (Bestellstatus, Kennzahlen, Bestellliste, Bestseller,
Herkunft, Stammkundschaft) bekommt ihre eigene, klar abgegrenzte Box mit eigener Akzentfarbe
(--box-color, pro Kiste per inline style gesetzt) statt lose auf der Seite zu stehen — auf
Wunsch ("alles was zueinander passt in einer Kiste, richtig geil speziell") deutlich
eigenständiger gestaltet als eine normale .card: eigener farbiger Eckenschein, eigenes
Icon-Abzeichen vor dem Titel, dezent pulsierender Rand. */
.verwaltung-box {
position: relative;
overflow: hidden;
border-radius: var(--radius-l);
border: 1.5px solid color-mix(in srgb, var(--box-color, var(--c-accent)) 38%, transparent);
background: linear-gradient(155deg, color-mix(in srgb, var(--box-color, var(--c-accent)) 9%, var(--c-anthracite-light)), var(--c-anthracite));
padding: 1.6rem 1.7rem;
margin: 1.4rem 0;
animation: verwaltung-box-glow 3.4s ease-in-out infinite;
}
@keyframes verwaltung-box-glow {
0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, var(--box-color, var(--c-accent)) 22%, transparent); }
50% { box-shadow: 0 0 22px -8px color-mix(in srgb, var(--box-color, var(--c-accent)) 55%, transparent); }
}
.verwaltung-box::before {
content: "";
position: absolute;
top: -45%;
right: -15%;
width: 260px;
height: 260px;
background: radial-gradient(circle, color-mix(in srgb, var(--box-color, var(--c-accent)) 30%, transparent), transparent 70%);
pointer-events: none;
}
.verwaltung-box-head {
display: flex;
align-items: center;
gap: 0.8rem;
margin-bottom: 1.1rem;
position: relative;
z-index: 1;
}
.verwaltung-box-icon {
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
width: 2.6rem;
height: 2.6rem;
border-radius: 50%;
font-size: 1.3rem;
background: color-mix(in srgb, var(--box-color, var(--c-accent)) 24%, transparent);
border: 1px solid color-mix(in srgb, var(--box-color, var(--c-accent)) 55%, transparent);
}
.verwaltung-box-head h2 { margin: 0; font-size: 1.3rem; }
.verwaltung-box-body { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .verwaltung-box { animation: none; } }
/* Leerzustände innerhalb einer Kiste — großes Icon + kurzer, freundlicher Text statt nacktem
Fließtext, damit auch "noch nichts da" nicht trostlos wirkt. */
.verwaltung-box-empty {
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
gap: 0.4rem;
padding: 2.2rem 1rem;
color: var(--c-text-muted);
}
.verwaltung-box-empty-icon { font-size: 2.6rem; opacity: 0.75; }
.verwaltung-box-empty p { margin: 0; }
.verwaltung-box-empty p:first-of-type { color: var(--c-text); font-weight: 600; font-size: 1.02rem; }
/* Hero-Kopf: dieselbe "besondere" Formsprache wie die anderen Premium-Karten der Seite
(wanderndes Verlaufslicht + pulsierender Rand + Funkeln), passend zu einer Seite, die "alles
übertrifft" — nur dezenter/edler gehalten, weil es ein Arbeits-Dashboard ist, kein Verkaufs-