Treuebonus-Karte deutlich detaillierter: Meilensteine, Kennzahlen, Regeln
Die Treuebonus-Karte im Konto zeigte bisher nur einen nackten Fortschrittsbalken mit einem Satz Text. Jetzt: (1) zehn Meilenstein-Striche direkt im Balken plus Geschenk-Symbol am Ziel, (2) vier Kennzahl-Kacheln im gleichen Stil wie beim Abo- Status (Gesammelt, Noch nötig, Bonus bei Ziel, Bisher eingelöst), (3) ein neues Datenfeld "wie oft insgesamt schon eingelöst + wann zuletzt" (account.ts), das in einer eigenen, gold→grün abgewandelten "Bonus bereit"-Karte mit Glow-Effekt landet statt eines einfachen Links, (4) eine kurze Icon-Liste mit den Sammelregeln statt einem einzelnen Erklärsatz. Alles in 4 Sprachen, mit echten Vorschau-Zuständen (Fortschritt/bereit/gesperrt) per Screenshot verifiziert. Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
5c1ebfac60
commit
9170b9bb22
@@ -6,6 +6,7 @@ import type { Locale } from "../../../i18n/config";
|
||||
import { useTranslations } from "../../../i18n/ui";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { ABO_STUFEN, aboStufenKonfig, aboStufeName, aboStufeVorteil, type AboStufe } from "../../../data/abo";
|
||||
import { TREUE_ZIEL, TREUE_RABATT_PROZENT } from "../../../scripts/account";
|
||||
|
||||
const lang: Locale = "ch";
|
||||
const t = useTranslations(lang);
|
||||
@@ -333,11 +334,58 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
<button type="button" class="btn btn-primary btn-sm" id="loyalty-locked-abo-btn" style="align-self:flex-start;">{t.accountDash.loyaltyLockedBtn}</button>
|
||||
</div>
|
||||
<div class="loyalty-inner" id="loyalty-unlocked-content">
|
||||
<div class="loyalty-progress-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||
<div class="loyalty-progress-track">
|
||||
<div class="loyalty-progress-bar">
|
||||
<div class="loyalty-progress-fill" id="loyalty-bar-fill"></div>
|
||||
<div class="loyalty-progress-dots" aria-hidden="true">
|
||||
{Array.from({ length: TREUE_ZIEL - 1 }).map((_, i) => (
|
||||
<span class="loyalty-progress-dot" style={`left:${((i + 1) / TREUE_ZIEL) * 100}%`}></span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span class="loyalty-progress-goal-icon" aria-hidden="true">🎁</span>
|
||||
</div>
|
||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||
|
||||
<div class="abo-stat-grid loyalty-stat-grid">
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">📦</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatCollected}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-collected">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">⏳</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatMissing}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-missing">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile loyalty-stat-tile-highlight">
|
||||
<span class="abo-stat-icon" aria-hidden="true">💰</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatBonus}</span>
|
||||
<span class="abo-stat-value">{TREUE_RABATT_PROZENT}%</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">🏅</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatRedeemed}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-redeemed">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="small loyalty-last-redeemed" id="loyalty-last-redeemed" style="display:none;"></p>
|
||||
|
||||
<div class="loyalty-ready-block" id="loyalty-ready-block" style="display:none;">
|
||||
<span class="loyalty-ready-icon" aria-hidden="true">🎁</span>
|
||||
<div class="loyalty-ready-body">
|
||||
<strong>{t.accountDash.loyaltyUnlockedTitle}</strong>
|
||||
<p class="small">{t.accountDash.loyaltyUnlockedText}</p>
|
||||
<a class="btn btn-primary btn-sm" id="loyalty-cart-link" href="/warenkorb/">{t.accountDash.loyaltyGoToCart}</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<ul class="loyalty-rules">
|
||||
<li><span aria-hidden="true">✅</span> {t.accountDash.loyaltyRuleValid}</li>
|
||||
<li><span aria-hidden="true">❌</span> {t.accountDash.loyaltyRuleInvalid}</li>
|
||||
<li><span aria-hidden="true">🔁</span> {t.accountDash.loyaltyRuleReset}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -480,8 +528,8 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
<script define:vars={{ loginPath: "/ch/konto/", fallbackName: "Kundin", 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, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels };
|
||||
<script define:vars={{ loginPath: "/ch/konto/", fallbackName: "Kundin", 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, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels };
|
||||
</script>
|
||||
<script>
|
||||
// Schnellnavigation als echte Filter-Tabs: Klick zeigt NUR den passenden Bereich, blendet den
|
||||
@@ -573,7 +621,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { aboStufenKonfig } from "../../../data/abo";
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels } = (window as any).__accountDashVars;
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels } = (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
|
||||
@@ -617,6 +665,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
|
||||
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
||||
function renderLoyalty() {
|
||||
const setText = (id: string, text: string) => { const el = document.getElementById(id); if (el) el.textContent = text; };
|
||||
const f = treueFortschritt(getAccount());
|
||||
const navValue = document.getElementById("loyalty-nav-value");
|
||||
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||
@@ -638,15 +687,28 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
.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";
|
||||
setText("loyalty-stat-collected", `${f.aktuelle}/${f.ziel}`);
|
||||
setText("loyalty-stat-missing", String(f.fehlende));
|
||||
setText("loyalty-stat-redeemed", f.eingeloestGesamt > 0 ? `${f.eingeloestGesamt}${loyaltyStatRedeemedSuffix}` : loyaltyStatRedeemedNever);
|
||||
const lastRedeemedEl = document.getElementById("loyalty-last-redeemed");
|
||||
if (lastRedeemedEl) {
|
||||
if (f.letzteEinloesungDatum) {
|
||||
lastRedeemedEl.textContent = loyaltyLastRedeemed.replace("{date}", f.letzteEinloesungDatum);
|
||||
lastRedeemedEl.style.display = "";
|
||||
} else {
|
||||
lastRedeemedEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const readyBlock = document.getElementById("loyalty-ready-block");
|
||||
const statusText = document.getElementById("loyalty-status-text");
|
||||
if (f.aktiv) {
|
||||
if (readyBlock) readyBlock.style.display = "flex";
|
||||
if (statusText) statusText.textContent = "";
|
||||
} else {
|
||||
if (readyBlock) readyBlock.style.display = "none";
|
||||
if (statusText) {
|
||||
statusText.textContent = f.fehlende === 1 ? loyaltyRemainingOne : loyaltyRemainingOther.replace("{n}", String(f.fehlende));
|
||||
if (cartLink) cartLink.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Locale } from "../../../i18n/config";
|
||||
import { useTranslations } from "../../../i18n/ui";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { ABO_STUFEN, aboStufenKonfig, aboStufeName, aboStufeVorteil, type AboStufe } from "../../../data/abo";
|
||||
import { TREUE_ZIEL, TREUE_RABATT_PROZENT } from "../../../scripts/account";
|
||||
|
||||
const lang: Locale = "en";
|
||||
const t = useTranslations(lang);
|
||||
@@ -333,11 +334,58 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
<button type="button" class="btn btn-primary btn-sm" id="loyalty-locked-abo-btn" style="align-self:flex-start;">{t.accountDash.loyaltyLockedBtn}</button>
|
||||
</div>
|
||||
<div class="loyalty-inner" id="loyalty-unlocked-content">
|
||||
<div class="loyalty-progress-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||
<div class="loyalty-progress-track">
|
||||
<div class="loyalty-progress-bar">
|
||||
<div class="loyalty-progress-fill" id="loyalty-bar-fill"></div>
|
||||
<div class="loyalty-progress-dots" aria-hidden="true">
|
||||
{Array.from({ length: TREUE_ZIEL - 1 }).map((_, i) => (
|
||||
<span class="loyalty-progress-dot" style={`left:${((i + 1) / TREUE_ZIEL) * 100}%`}></span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span class="loyalty-progress-goal-icon" aria-hidden="true">🎁</span>
|
||||
</div>
|
||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||
|
||||
<div class="abo-stat-grid loyalty-stat-grid">
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">📦</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatCollected}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-collected">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">⏳</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatMissing}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-missing">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile loyalty-stat-tile-highlight">
|
||||
<span class="abo-stat-icon" aria-hidden="true">💰</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatBonus}</span>
|
||||
<span class="abo-stat-value">{TREUE_RABATT_PROZENT}%</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">🏅</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatRedeemed}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-redeemed">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="small loyalty-last-redeemed" id="loyalty-last-redeemed" style="display:none;"></p>
|
||||
|
||||
<div class="loyalty-ready-block" id="loyalty-ready-block" style="display:none;">
|
||||
<span class="loyalty-ready-icon" aria-hidden="true">🎁</span>
|
||||
<div class="loyalty-ready-body">
|
||||
<strong>{t.accountDash.loyaltyUnlockedTitle}</strong>
|
||||
<p class="small">{t.accountDash.loyaltyUnlockedText}</p>
|
||||
<a class="btn btn-primary btn-sm" id="loyalty-cart-link" href="/warenkorb/">{t.accountDash.loyaltyGoToCart}</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<ul class="loyalty-rules">
|
||||
<li><span aria-hidden="true">✅</span> {t.accountDash.loyaltyRuleValid}</li>
|
||||
<li><span aria-hidden="true">❌</span> {t.accountDash.loyaltyRuleInvalid}</li>
|
||||
<li><span aria-hidden="true">🔁</span> {t.accountDash.loyaltyRuleReset}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -480,8 +528,8 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
</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 }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels };
|
||||
<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, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels };
|
||||
</script>
|
||||
<script>
|
||||
// Schnellnavigation als echte Filter-Tabs: Klick zeigt NUR den passenden Bereich, blendet den
|
||||
@@ -573,7 +621,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { aboStufenKonfig } from "../../../data/abo";
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels } = (window as any).__accountDashVars;
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels } = (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
|
||||
@@ -617,6 +665,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
|
||||
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
||||
function renderLoyalty() {
|
||||
const setText = (id: string, text: string) => { const el = document.getElementById(id); if (el) el.textContent = text; };
|
||||
const f = treueFortschritt(getAccount());
|
||||
const navValue = document.getElementById("loyalty-nav-value");
|
||||
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||
@@ -638,15 +687,28 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
.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";
|
||||
setText("loyalty-stat-collected", `${f.aktuelle}/${f.ziel}`);
|
||||
setText("loyalty-stat-missing", String(f.fehlende));
|
||||
setText("loyalty-stat-redeemed", f.eingeloestGesamt > 0 ? `${f.eingeloestGesamt}${loyaltyStatRedeemedSuffix}` : loyaltyStatRedeemedNever);
|
||||
const lastRedeemedEl = document.getElementById("loyalty-last-redeemed");
|
||||
if (lastRedeemedEl) {
|
||||
if (f.letzteEinloesungDatum) {
|
||||
lastRedeemedEl.textContent = loyaltyLastRedeemed.replace("{date}", f.letzteEinloesungDatum);
|
||||
lastRedeemedEl.style.display = "";
|
||||
} else {
|
||||
lastRedeemedEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const readyBlock = document.getElementById("loyalty-ready-block");
|
||||
const statusText = document.getElementById("loyalty-status-text");
|
||||
if (f.aktiv) {
|
||||
if (readyBlock) readyBlock.style.display = "flex";
|
||||
if (statusText) statusText.textContent = "";
|
||||
} else {
|
||||
if (readyBlock) readyBlock.style.display = "none";
|
||||
if (statusText) {
|
||||
statusText.textContent = f.fehlende === 1 ? loyaltyRemainingOne : loyaltyRemainingOther.replace("{n}", String(f.fehlende));
|
||||
if (cartLink) cartLink.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Locale } from "../../../i18n/config";
|
||||
import { useTranslations } from "../../../i18n/ui";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { ABO_STUFEN, aboStufenKonfig, aboStufeName, aboStufeVorteil, type AboStufe } from "../../../data/abo";
|
||||
import { TREUE_ZIEL, TREUE_RABATT_PROZENT } from "../../../scripts/account";
|
||||
|
||||
const lang: Locale = "fr";
|
||||
const t = useTranslations(lang);
|
||||
@@ -333,11 +334,58 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
<button type="button" class="btn btn-primary btn-sm" id="loyalty-locked-abo-btn" style="align-self:flex-start;">{t.accountDash.loyaltyLockedBtn}</button>
|
||||
</div>
|
||||
<div class="loyalty-inner" id="loyalty-unlocked-content">
|
||||
<div class="loyalty-progress-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||
<div class="loyalty-progress-track">
|
||||
<div class="loyalty-progress-bar">
|
||||
<div class="loyalty-progress-fill" id="loyalty-bar-fill"></div>
|
||||
<div class="loyalty-progress-dots" aria-hidden="true">
|
||||
{Array.from({ length: TREUE_ZIEL - 1 }).map((_, i) => (
|
||||
<span class="loyalty-progress-dot" style={`left:${((i + 1) / TREUE_ZIEL) * 100}%`}></span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span class="loyalty-progress-goal-icon" aria-hidden="true">🎁</span>
|
||||
</div>
|
||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||
|
||||
<div class="abo-stat-grid loyalty-stat-grid">
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">📦</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatCollected}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-collected">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">⏳</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatMissing}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-missing">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile loyalty-stat-tile-highlight">
|
||||
<span class="abo-stat-icon" aria-hidden="true">💰</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatBonus}</span>
|
||||
<span class="abo-stat-value">{TREUE_RABATT_PROZENT}%</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">🏅</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatRedeemed}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-redeemed">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="small loyalty-last-redeemed" id="loyalty-last-redeemed" style="display:none;"></p>
|
||||
|
||||
<div class="loyalty-ready-block" id="loyalty-ready-block" style="display:none;">
|
||||
<span class="loyalty-ready-icon" aria-hidden="true">🎁</span>
|
||||
<div class="loyalty-ready-body">
|
||||
<strong>{t.accountDash.loyaltyUnlockedTitle}</strong>
|
||||
<p class="small">{t.accountDash.loyaltyUnlockedText}</p>
|
||||
<a class="btn btn-primary btn-sm" id="loyalty-cart-link" href="/warenkorb/">{t.accountDash.loyaltyGoToCart}</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<ul class="loyalty-rules">
|
||||
<li><span aria-hidden="true">✅</span> {t.accountDash.loyaltyRuleValid}</li>
|
||||
<li><span aria-hidden="true">❌</span> {t.accountDash.loyaltyRuleInvalid}</li>
|
||||
<li><span aria-hidden="true">🔁</span> {t.accountDash.loyaltyRuleReset}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -480,8 +528,8 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
<script define:vars={{ loginPath: "/fr/konto/", fallbackName: "Cliente", 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, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels };
|
||||
<script define:vars={{ loginPath: "/fr/konto/", fallbackName: "Cliente", 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, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels };
|
||||
</script>
|
||||
<script>
|
||||
// Schnellnavigation als echte Filter-Tabs: Klick zeigt NUR den passenden Bereich, blendet den
|
||||
@@ -573,7 +621,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||
import { formatPrice } from "../../../i18n/format";
|
||||
import { aboStufenKonfig } from "../../../data/abo";
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels } = (window as any).__accountDashVars;
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels } = (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
|
||||
@@ -617,6 +665,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
|
||||
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
||||
function renderLoyalty() {
|
||||
const setText = (id: string, text: string) => { const el = document.getElementById(id); if (el) el.textContent = text; };
|
||||
const f = treueFortschritt(getAccount());
|
||||
const navValue = document.getElementById("loyalty-nav-value");
|
||||
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||
@@ -638,15 +687,28 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
.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";
|
||||
setText("loyalty-stat-collected", `${f.aktuelle}/${f.ziel}`);
|
||||
setText("loyalty-stat-missing", String(f.fehlende));
|
||||
setText("loyalty-stat-redeemed", f.eingeloestGesamt > 0 ? `${f.eingeloestGesamt}${loyaltyStatRedeemedSuffix}` : loyaltyStatRedeemedNever);
|
||||
const lastRedeemedEl = document.getElementById("loyalty-last-redeemed");
|
||||
if (lastRedeemedEl) {
|
||||
if (f.letzteEinloesungDatum) {
|
||||
lastRedeemedEl.textContent = loyaltyLastRedeemed.replace("{date}", f.letzteEinloesungDatum);
|
||||
lastRedeemedEl.style.display = "";
|
||||
} else {
|
||||
lastRedeemedEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const readyBlock = document.getElementById("loyalty-ready-block");
|
||||
const statusText = document.getElementById("loyalty-status-text");
|
||||
if (f.aktiv) {
|
||||
if (readyBlock) readyBlock.style.display = "flex";
|
||||
if (statusText) statusText.textContent = "";
|
||||
} else {
|
||||
if (readyBlock) readyBlock.style.display = "none";
|
||||
if (statusText) {
|
||||
statusText.textContent = f.fehlende === 1 ? loyaltyRemainingOne : loyaltyRemainingOther.replace("{n}", String(f.fehlende));
|
||||
if (cartLink) cartLink.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import type { Locale } from "../../i18n/config";
|
||||
import { useTranslations } from "../../i18n/ui";
|
||||
import { formatPrice } from "../../i18n/format";
|
||||
import { ABO_STUFEN, aboStufenKonfig, aboStufeName, aboStufeVorteil, type AboStufe } from "../../data/abo";
|
||||
import { TREUE_ZIEL, TREUE_RABATT_PROZENT } from "../../scripts/account";
|
||||
|
||||
const lang: Locale = "de";
|
||||
const t = useTranslations(lang);
|
||||
@@ -337,11 +338,63 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
<button type="button" class="btn btn-primary btn-sm" id="loyalty-locked-abo-btn" style="align-self:flex-start;">{t.accountDash.loyaltyLockedBtn}</button>
|
||||
</div>
|
||||
<div class="loyalty-inner" id="loyalty-unlocked-content">
|
||||
<div class="loyalty-progress-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||
<div class="loyalty-progress-track">
|
||||
<div class="loyalty-progress-bar">
|
||||
<div class="loyalty-progress-fill" id="loyalty-bar-fill"></div>
|
||||
<div class="loyalty-progress-dots" aria-hidden="true">
|
||||
{Array.from({ length: TREUE_ZIEL - 1 }).map((_, i) => (
|
||||
<span class="loyalty-progress-dot" style={`left:${((i + 1) / TREUE_ZIEL) * 100}%`}></span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<span class="loyalty-progress-goal-icon" aria-hidden="true">🎁</span>
|
||||
</div>
|
||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||
|
||||
{/* Vier Kennzahlen statt trockenem Fließtext — gleiche "besondere" Kachel-Formsprache
|
||||
wie beim Abo-Status (siehe .abo-stat-grid), nur in der Treuebonus-eigenen Grünfarbe. */}
|
||||
<div class="abo-stat-grid loyalty-stat-grid">
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">📦</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatCollected}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-collected">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">⏳</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatMissing}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-missing">–</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile loyalty-stat-tile-highlight">
|
||||
<span class="abo-stat-icon" aria-hidden="true">💰</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatBonus}</span>
|
||||
<span class="abo-stat-value">{TREUE_RABATT_PROZENT}%</span>
|
||||
</div>
|
||||
<div class="abo-stat-tile">
|
||||
<span class="abo-stat-icon" aria-hidden="true">🏅</span>
|
||||
<span class="abo-stat-label">{t.accountDash.loyaltyStatRedeemed}</span>
|
||||
<span class="abo-stat-value" id="loyalty-stat-redeemed">–</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="small loyalty-last-redeemed" id="loyalty-last-redeemed" style="display:none;"></p>
|
||||
|
||||
{/* Bonus bereit: eigene, leicht pulsierende Karte statt einem simplen Link — soll klar
|
||||
als "hier wartet etwas Kostenloses auf dich" auffallen (gleiches Prinzip wie beim
|
||||
Abo-Prämien-Bereich, nur grün statt gold). */}
|
||||
<div class="loyalty-ready-block" id="loyalty-ready-block" style="display:none;">
|
||||
<span class="loyalty-ready-icon" aria-hidden="true">🎁</span>
|
||||
<div class="loyalty-ready-body">
|
||||
<strong>{t.accountDash.loyaltyUnlockedTitle}</strong>
|
||||
<p class="small">{t.accountDash.loyaltyUnlockedText}</p>
|
||||
<a class="btn btn-primary btn-sm" id="loyalty-cart-link" href="/warenkorb/">{t.accountDash.loyaltyGoToCart}</a>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
|
||||
<ul class="loyalty-rules">
|
||||
<li><span aria-hidden="true">✅</span> {t.accountDash.loyaltyRuleValid}</li>
|
||||
<li><span aria-hidden="true">❌</span> {t.accountDash.loyaltyRuleInvalid}</li>
|
||||
<li><span aria-hidden="true">🔁</span> {t.accountDash.loyaltyRuleReset}</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -484,8 +537,8 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
</section>
|
||||
</Layout>
|
||||
|
||||
<script define:vars={{ loginPath: "/konto/", fallbackName: "Kundin", 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, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels };
|
||||
<script define:vars={{ loginPath: "/konto/", fallbackName: "Kundin", 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, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } } }}>
|
||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels };
|
||||
</script>
|
||||
<script>
|
||||
// Schnellnavigation als echte Filter-Tabs: Klick zeigt NUR den passenden Bereich, blendet den
|
||||
@@ -581,7 +634,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
import { fuegeTeddyGratisHinzu } from "../../scripts/cart";
|
||||
import { formatPrice } from "../../i18n/format";
|
||||
import { aboStufenKonfig } from "../../data/abo";
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, aboLang, aboLabels } = (window as any).__accountDashVars;
|
||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels } = (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
|
||||
@@ -627,6 +680,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
// Ist an ein aktives Abo gekoppelt (siehe treueFortschritt().gesperrt in account.ts) — ohne
|
||||
// aktives Abo zeigt die Karte nur den gesperrten Hinweis samt Sprung zum Abo-Bereich.
|
||||
function renderLoyalty() {
|
||||
const setText = (id: string, text: string) => { const el = document.getElementById(id); if (el) el.textContent = text; };
|
||||
const f = treueFortschritt(getAccount());
|
||||
const navValue = document.getElementById("loyalty-nav-value");
|
||||
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||
@@ -648,15 +702,29 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
||||
.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";
|
||||
// Vier Kennzahlen-Kacheln + "Zuletzt eingelöst"-Hinweis (nur, wenn schon mal eingelöst wurde).
|
||||
setText("loyalty-stat-collected", `${f.aktuelle}/${f.ziel}`);
|
||||
setText("loyalty-stat-missing", String(f.fehlende));
|
||||
setText("loyalty-stat-redeemed", f.eingeloestGesamt > 0 ? `${f.eingeloestGesamt}${loyaltyStatRedeemedSuffix}` : loyaltyStatRedeemedNever);
|
||||
const lastRedeemedEl = document.getElementById("loyalty-last-redeemed");
|
||||
if (lastRedeemedEl) {
|
||||
if (f.letzteEinloesungDatum) {
|
||||
lastRedeemedEl.textContent = loyaltyLastRedeemed.replace("{date}", f.letzteEinloesungDatum);
|
||||
lastRedeemedEl.style.display = "";
|
||||
} else {
|
||||
lastRedeemedEl.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
const readyBlock = document.getElementById("loyalty-ready-block");
|
||||
const statusText = document.getElementById("loyalty-status-text");
|
||||
if (f.aktiv) {
|
||||
if (readyBlock) readyBlock.style.display = "flex";
|
||||
if (statusText) statusText.textContent = "";
|
||||
} else {
|
||||
if (readyBlock) readyBlock.style.display = "none";
|
||||
if (statusText) {
|
||||
statusText.textContent = f.fehlende === 1 ? loyaltyRemainingOne : loyaltyRemainingOther.replace("{n}", String(f.fehlende));
|
||||
if (cartLink) cartLink.style.display = "none";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user