Treuebonus-System: 10 abgeschlossene Bestellungen = 20% Rabatt auf einen frei wählbaren Artikel
Neue eigene Kiste "Treuebonus" im Kundenkonto (5. Nav-Tab, grüner Akzent)
zeigt den Fortschritt ("X von 10 Bestellungen") mit Fortschrittsbalken und
Restanzahl-Hinweis, nach Freischaltung eine Erfolgsmeldung mit Link zum
Warenkorb. Es zählen ausschließlich erfolgreich abgeschickte, bezahlte
Bestellungen (in Phase 1: jede erfolgreich abgeschlossene Demo-Bestellung
im Checkout) — abgebrochene Bestellungen zählen nie mit.
Sobald der Bonus aktiv ist, erscheint im Warenkorb ein Hinweis-Banner und
bei jedem Artikel eine Auswahl ("Für 20% Treuebonus auswählen") — es kann
immer nur EIN Artikel gewählt werden (Radiobutton-Verhalten über alle
Positionen hinweg). Der Rabatt fließt automatisch in Warenkorb- und
Checkout-Summe ein (neue treuebonusDiscount()-Funktion in scripts/cart.ts,
in totalDiscount() eingerechnet). Nach erfolgreichem Bestellabschluss wird
die Belohnung als eingelöst markiert und der Fortschrittszähler beginnt
wieder bei 0 — beides dauerhaft im Konto (localStorage, Phase 1) gespeichert
und eindeutig diesem Kundenkonto zugeordnet.
Neue Funktionen in scripts/account.ts: registriereAbgeschlosseneBestellung(),
setTreuebonusArtikel(), treuebonusEinloesen(), treueFortschritt(). Beide
Zustandsübergänge (Fortschritt zählen vs. Belohnung einlösen) end-to-end
über den echten Checkout-Ablauf getestet und verifiziert.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d7ba1b9bff
commit
0cb7d3ca65
@@ -109,6 +109,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
<span class="account-nav-icon" aria-hidden="true">👤</span>
|
||||
<span class="account-nav-label">{t.accountDash.statData}</span>
|
||||
</button>
|
||||
<button type="button" class="account-nav-item account-nav-loyalty" data-tab="treuebonus">
|
||||
<span class="account-nav-sparkle" aria-hidden="true">✨</span>
|
||||
<span class="account-nav-icon" aria-hidden="true">🎁</span>
|
||||
<span class="account-nav-value" id="loyalty-nav-value">–</span>
|
||||
<span class="account-nav-label">{t.accountDash.statLoyalty}</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
</section>
|
||||
@@ -290,10 +296,28 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Treuebonus: 10 gültige, abgeschlossene Bestellungen → 20% auf einen frei wählbaren Artikel
|
||||
(siehe scripts/account.ts). Fortschritt/Status kommen komplett aus dem Konto und werden per
|
||||
JS befüllt (siehe Skript unten) — hier nur das Gerüst mit sinnvollen Leerzuständen. */}
|
||||
<section class="section-tight" id="treuebonus">
|
||||
<div class="container">
|
||||
<h2>{t.accountDash.loyaltyTitle}</h2>
|
||||
<div class="card loyalty-card">
|
||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
||||
<div class="loyalty-progress-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||
<p id="loyalty-status-text"></p>
|
||||
<a class="btn btn-primary btn-sm" id="loyalty-cart-link" href="/warenkorb/" style="display:none; align-self:flex-start;">{t.accountDash.loyaltyGoToCart}</a>
|
||||
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
<script define:vars={{ loginPath: "/en/konto/", fallbackName: "Customer", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }}}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels };
|
||||
<script define:vars={{ loginPath: "/en/konto/", fallbackName: "Customer", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText}}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels , loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText };
|
||||
</script>
|
||||
<script>
|
||||
// Schnellnavigation als echte Filter-Tabs: Klick zeigt NUR den passenden Bereich, blendet den
|
||||
@@ -306,6 +330,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
const wunschlisteSection = document.getElementById("wunschliste");
|
||||
const recentSection = document.getElementById("zuletzt-angesehen");
|
||||
const datenSection = document.getElementById("meine-daten");
|
||||
const treuebonusSection = document.getElementById("treuebonus");
|
||||
const orderCards = Array.from(document.querySelectorAll(".order-card"));
|
||||
const emptyNote = document.getElementById("orders-empty-note") as HTMLElement | null;
|
||||
|
||||
@@ -317,6 +342,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
if (bestellungenSection) bestellungenSection.style.display = "none";
|
||||
if (wunschlisteSection) wunschlisteSection.style.display = "none";
|
||||
if (datenSection) datenSection.style.display = "none";
|
||||
if (treuebonusSection) treuebonusSection.style.display = "none";
|
||||
if (recentSection) recentSection.style.display = "none";
|
||||
|
||||
if (tab === null) {
|
||||
@@ -334,6 +360,11 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
return;
|
||||
}
|
||||
|
||||
if (tab === "treuebonus") {
|
||||
if (treuebonusSection) treuebonusSection.style.display = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (bestellungenSection) bestellungenSection.style.display = "";
|
||||
let visibleCount = 0;
|
||||
orderCards.forEach((card) => {
|
||||
@@ -359,9 +390,9 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
setActiveTab(null);
|
||||
</script>
|
||||
<script>
|
||||
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName } from "../../../scripts/account";
|
||||
import { getAccount, isLoggedIn, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt } from "../../../scripts/account";
|
||||
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels } = (window as any).__accountDashVars;
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText } = (window as any).__accountDashVars;
|
||||
|
||||
// Zugriffsschutz: diese Seite ist nur für angemeldete Kund:innen gedacht. Es gibt noch kein
|
||||
// echtes Backend, das den Zugriff serverseitig verweigern könnte (Phase 2) — deshalb hier per
|
||||
@@ -403,6 +434,38 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
renderProfil();
|
||||
window.addEventListener("account:changed", renderProfil);
|
||||
|
||||
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
||||
function renderLoyalty() {
|
||||
const f = treueFortschritt(getAccount());
|
||||
const navValue = document.getElementById("loyalty-nav-value");
|
||||
if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`;
|
||||
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv);
|
||||
|
||||
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
||||
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
||||
|
||||
const progressText = document.getElementById("loyalty-progress-text");
|
||||
if (progressText) {
|
||||
progressText.textContent = loyaltyProgressTemplate
|
||||
.replace("{cur}", String(f.aktuelle))
|
||||
.replace("{ziel}", String(f.ziel));
|
||||
}
|
||||
|
||||
const statusText = document.getElementById("loyalty-status-text");
|
||||
const cartLink = document.getElementById("loyalty-cart-link") as HTMLElement | null;
|
||||
if (statusText) {
|
||||
if (f.aktiv) {
|
||||
statusText.innerHTML = `<strong>${loyaltyUnlockedTitle}</strong><br />${loyaltyUnlockedText}`;
|
||||
if (cartLink) cartLink.style.display = "inline-flex";
|
||||
} else {
|
||||
statusText.textContent = f.fehlende === 1 ? loyaltyRemainingOne : loyaltyRemainingOther.replace("{n}", String(f.fehlende));
|
||||
if (cartLink) cartLink.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
renderLoyalty();
|
||||
window.addEventListener("account:changed", renderLoyalty);
|
||||
|
||||
// Benutzername ändern
|
||||
const saveBtn = document.getElementById("profile-username-save");
|
||||
const usernameInput = document.getElementById("profile-username") as HTMLInputElement | null;
|
||||
|
||||
Reference in New Issue
Block a user