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
@@ -241,6 +241,16 @@ export const ui = {
|
||||
loyaltyUnlockedText: "Du hast zehn Bestellungen abgeschlossen und erhältst bei deiner nächsten Bestellung 20 % Rabatt auf einen Artikel deiner Wahl.",
|
||||
loyaltyGoToCart: "Artikel im Warenkorb auswählen →",
|
||||
loyaltyExplainer: "Es zählen nur vollständig abgeschlossene und bezahlte Bestellungen. Nach der Einlösung beginnt dein Fortschritt wieder bei null.",
|
||||
loyaltyStatCollected: "Gesammelt",
|
||||
loyaltyStatMissing: "Noch nötig",
|
||||
loyaltyStatBonus: "Bonus bei Ziel",
|
||||
loyaltyStatRedeemed: "Bisher eingelöst",
|
||||
loyaltyStatRedeemedNever: "Noch nie",
|
||||
loyaltyStatRedeemedSuffix: "×",
|
||||
loyaltyLastRedeemed: "Zuletzt eingelöst am {date}",
|
||||
loyaltyRuleValid: "Zählt: vollständig abgeschlossene & bezahlte Bestellungen",
|
||||
loyaltyRuleInvalid: "Zählt nicht: stornierte oder noch offene Bestellungen",
|
||||
loyaltyRuleReset: "Nach der Einlösung startet dein Fortschritt wieder bei 0",
|
||||
loyaltyLockedTitle: "🔒 Nur mit aktivem Abo verfügbar",
|
||||
loyaltyLockedText: "Der Treuebonus ist ein Extra für unsere Abo-Mitglieder. Schließe ein Abo ab, um Bestellungen für deine 20 %-Belohnung zu sammeln.",
|
||||
loyaltyLockedBtn: "Zum Abo →",
|
||||
@@ -543,6 +553,16 @@ export const ui = {
|
||||
loyaltyUnlockedText: "You've completed ten orders and get 20% off an item of your choice on your next order.",
|
||||
loyaltyGoToCart: "Choose item in cart →",
|
||||
loyaltyExplainer: "Only fully completed and paid orders count. After redemption your progress starts again from zero.",
|
||||
loyaltyStatCollected: "Collected",
|
||||
loyaltyStatMissing: "Still needed",
|
||||
loyaltyStatBonus: "Reward at goal",
|
||||
loyaltyStatRedeemed: "Redeemed so far",
|
||||
loyaltyStatRedeemedNever: "Never yet",
|
||||
loyaltyStatRedeemedSuffix: "×",
|
||||
loyaltyLastRedeemed: "Last redeemed on {date}",
|
||||
loyaltyRuleValid: "Counts: fully completed & paid orders",
|
||||
loyaltyRuleInvalid: "Doesn't count: cancelled or still open orders",
|
||||
loyaltyRuleReset: "After redemption your progress restarts at 0",
|
||||
loyaltyLockedTitle: "🔒 Only available with an active subscription",
|
||||
loyaltyLockedText: "The loyalty reward is an extra for our subscription members. Subscribe to start collecting orders towards your 20% reward.",
|
||||
loyaltyLockedBtn: "Go to subscription →",
|
||||
@@ -845,6 +865,16 @@ export const ui = {
|
||||
loyaltyUnlockedText: "Du hesch zäh Bstellige abgschlosse und übercho bi dinere nächschte Bstellig 20 % Rabatt uf en Artikel dinere Wahl.",
|
||||
loyaltyGoToCart: "Artikel im Warenchorb uswähle →",
|
||||
loyaltyExplainer: "Es zelle nume vollständig abgschlosseni und bezahlti Bstellige. Nach dr Ilösig fangt din Fortschritt wieder bi null a.",
|
||||
loyaltyStatCollected: "Gsammlet",
|
||||
loyaltyStatMissing: "No nötig",
|
||||
loyaltyStatBonus: "Bonus bim Ziel",
|
||||
loyaltyStatRedeemed: "Bisher iglöst",
|
||||
loyaltyStatRedeemedNever: "No nie",
|
||||
loyaltyStatRedeemedSuffix: "×",
|
||||
loyaltyLastRedeemed: "Zletscht iglöst am {date}",
|
||||
loyaltyRuleValid: "Zellt: vollständig abgschlosseni & bezahlti Bstellige",
|
||||
loyaltyRuleInvalid: "Zellt nid: stornierti oder no offeni Bstellige",
|
||||
loyaltyRuleReset: "Nach dr Ilösig fangt din Fortschritt wieder bi 0 a",
|
||||
loyaltyLockedTitle: "🔒 Nume mit aktivem Abo verfüegbar",
|
||||
loyaltyLockedText: "Dr Treuebonus isch es Extra für üsi Abo-Mitglieder. Schliess es Abo ab, um Bstellige für dini 20 %-Belohnig z sammle.",
|
||||
loyaltyLockedBtn: "Zum Abo →",
|
||||
@@ -1147,6 +1177,16 @@ export const ui = {
|
||||
loyaltyUnlockedText: "Vous avez validé dix commandes et bénéficiez de 20 % de réduction sur l'article de votre choix lors de votre prochaine commande.",
|
||||
loyaltyGoToCart: "Choisir l'article dans le panier →",
|
||||
loyaltyExplainer: "Seules les commandes entièrement finalisées et payées comptent. Après utilisation, votre progression repart de zéro.",
|
||||
loyaltyStatCollected: "Cumulées",
|
||||
loyaltyStatMissing: "Encore nécessaires",
|
||||
loyaltyStatBonus: "Récompense à l'objectif",
|
||||
loyaltyStatRedeemed: "Utilisées jusqu'ici",
|
||||
loyaltyStatRedeemedNever: "Jamais encore",
|
||||
loyaltyStatRedeemedSuffix: "×",
|
||||
loyaltyLastRedeemed: "Dernière utilisation le {date}",
|
||||
loyaltyRuleValid: "Compte : commandes entièrement finalisées et payées",
|
||||
loyaltyRuleInvalid: "Ne compte pas : commandes annulées ou encore ouvertes",
|
||||
loyaltyRuleReset: "Après utilisation, votre progression repart de 0",
|
||||
loyaltyLockedTitle: "🔒 Disponible uniquement avec un abonnement actif",
|
||||
loyaltyLockedText: "La récompense fidélité est un bonus réservé à nos abonné(e)s. Abonnez-vous pour commencer à cumuler des commandes vers votre récompense de 20 %.",
|
||||
loyaltyLockedBtn: "Voir l'abonnement →",
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,6 +42,12 @@ export interface Account {
|
||||
/** Slug des Artikels, den die Person im Warenkorb für die -20%-Belohnung ausgewählt hat
|
||||
* (siehe scripts/cart.ts / warenkorb-Seite). Wird bei Einlösung wieder geleert. */
|
||||
treuebonusArtikelSlug: string | null;
|
||||
/** Wie oft die Treuebelohnung insgesamt schon eingelöst wurde (über alle Zyklen hinweg,
|
||||
* wird bei treuebonusEinloesen() hochgezählt, NIE zurückgesetzt) — für die "Bisher X× eingelöst"-
|
||||
* Anzeige im Konto, macht den langfristigen Nutzen des Treuebonus sichtbar. */
|
||||
treuebonusEingeloestGesamt: number;
|
||||
/** ISO-Datum der letzten Einlösung, oder null, wenn noch nie eingelöst wurde. */
|
||||
treuebonusLetzteEinloesungDatum: string | null;
|
||||
/** Monatliches Abo (siehe data/abo.ts für Preise/Regeln + weiter unten für die Aktionen). */
|
||||
abo: AboZustand;
|
||||
/** Kurze In-Konto-Benachrichtigungen (Phase 1: kein E-Mail-/Push-Versand, siehe Hinweis bei
|
||||
@@ -134,6 +140,8 @@ function leeresKonto(): Account {
|
||||
treueBestellungen: 0,
|
||||
treuebonusAktiv: false,
|
||||
treuebonusArtikelSlug: null,
|
||||
treuebonusEingeloestGesamt: 0,
|
||||
treuebonusLetzteEinloesungDatum: null,
|
||||
abo: leeresAbo(),
|
||||
benachrichtigungen: [],
|
||||
};
|
||||
@@ -261,6 +269,8 @@ export function treuebonusEinloesen(): Account {
|
||||
treueBestellungen: 0,
|
||||
treuebonusAktiv: false,
|
||||
treuebonusArtikelSlug: null,
|
||||
treuebonusEingeloestGesamt: getAccount().treuebonusEingeloestGesamt + 1,
|
||||
treuebonusLetzteEinloesungDatum: heutigesDatumISO(),
|
||||
};
|
||||
saveAccount(updated);
|
||||
return updated;
|
||||
@@ -274,6 +284,13 @@ export interface TreueFortschritt {
|
||||
/** true, solange KEIN aktives Abo besteht — der Treuebonus ist dann als Anreiz sichtbar, aber
|
||||
* gesperrt (sammelt nicht weiter, kann nicht eingelöst werden), bis ein Abo abgeschlossen wird. */
|
||||
gesperrt: boolean;
|
||||
/** Rabatt-Prozentsatz der Belohnung (aktuell fest 20%, siehe TREUE_RABATT_PROZENT) — als Teil
|
||||
* des Fortschritts mitgegeben, damit die Anzeige nicht selbst importieren/duplizieren muss. */
|
||||
rabattProzent: number;
|
||||
/** Wie oft die Belohnung insgesamt schon eingelöst wurde + wann zuletzt (siehe Account-Felder
|
||||
* oben) — für die erweiterte Fortschrittskarte im Konto. */
|
||||
eingeloestGesamt: number;
|
||||
letzteEinloesungDatum: string | null;
|
||||
}
|
||||
|
||||
/** Aufbereiteter Fortschritt fürs Anzeigen im Kundenkonto ("7 von 10", "Dir fehlen noch 3 …"). */
|
||||
@@ -284,6 +301,9 @@ export function treueFortschritt(a: Account): TreueFortschritt {
|
||||
fehlende: Math.max(0, TREUE_ZIEL - a.treueBestellungen),
|
||||
aktiv: a.treuebonusAktiv,
|
||||
gesperrt: !aboStatus(a).aktiv,
|
||||
rabattProzent: TREUE_RABATT_PROZENT,
|
||||
eingeloestGesamt: a.treuebonusEingeloestGesamt,
|
||||
letzteEinloesungDatum: a.treuebonusLetzteEinloesungDatum,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1505,6 +1505,7 @@ a:focus-visible, button:focus-visible {
|
||||
Schloss-Titel in Gold (passend zur Abo-Farbwelt) und direktem Call-to-Action zum Abo. */
|
||||
.loyalty-locked-title { margin: 0; font-weight: 800; font-size: 1.1rem; color: #e3b23c; }
|
||||
.loyalty-progress-bar {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 12px;
|
||||
border-radius: 999px;
|
||||
@@ -1519,8 +1520,58 @@ a:focus-visible, button:focus-visible {
|
||||
background: linear-gradient(90deg, #6ecb8f, #5fd3ec);
|
||||
transition: width 0.5s var(--ease);
|
||||
}
|
||||
/* Balken + Ziel-Symbol als Reihe — das 🎁 sitzt bewusst AUSSERHALB des Balkens (statt per
|
||||
::after am Rand), damit "overflow:hidden" (für die inneren Meilenstein-Punkte nötig) es nicht
|
||||
anschneidet. */
|
||||
.loyalty-progress-track { display: flex; align-items: center; gap: 0.5rem; }
|
||||
.loyalty-progress-track .loyalty-progress-bar { flex: 1; }
|
||||
.loyalty-progress-goal-icon { flex-shrink: 0; font-size: 1.05rem; }
|
||||
/* Zehn Meilenstein-Striche im Balken selbst — machen "6 von 10" auf einen Blick als echte
|
||||
Sammelkarte greifbar, statt nur eine abstrakte Prozent-Füllung zu zeigen. */
|
||||
.loyalty-progress-dots { position: absolute; inset: 0; pointer-events: none; }
|
||||
.loyalty-progress-dot {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
background: rgba(10, 9, 16, 0.3);
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
.loyalty-progress-text { font-weight: 700; font-size: 1.05rem; margin: 0; }
|
||||
.loyalty-explainer { color: var(--c-text-muted); margin: 0.4rem 0 0; }
|
||||
.loyalty-last-redeemed { color: var(--c-text-muted); margin: -0.5rem 0 0; }
|
||||
|
||||
/* Stat-Kacheln (siehe .abo-stat-grid/.abo-stat-tile für die geteilte Basis) — die Treuebonus-
|
||||
eigene Hervorhebungsfarbe ist Grün statt Gold, passend zum restlichen Treuebonus-Auftritt
|
||||
(Fortschrittsbalken, Nav-Kachel, Stern). */
|
||||
.loyalty-stat-tile-highlight { background: color-mix(in srgb, #6ecb8f 12%, transparent); border-color: color-mix(in srgb, #6ecb8f 38%, transparent); }
|
||||
.loyalty-stat-tile-highlight .abo-stat-value { color: #6ecb8f; }
|
||||
|
||||
/* "Bonus bereit"-Karte — gleiches Prinzip wie .abo-reward-ready (leicht pulsierender Rahmen,
|
||||
soll als "hier wartet was Kostenloses" auffallen), nur in Grün statt Gold. */
|
||||
.loyalty-ready-block {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.9rem;
|
||||
padding: 0.8rem 1.1rem;
|
||||
border-radius: var(--radius-l);
|
||||
background: linear-gradient(155deg, color-mix(in srgb, #6ecb8f 16%, var(--c-anthracite-light)), var(--c-anthracite));
|
||||
border: 1.5px solid color-mix(in srgb, #6ecb8f 45%, transparent);
|
||||
animation: loyalty-ready-glow 2.6s ease-in-out infinite;
|
||||
}
|
||||
@keyframes loyalty-ready-glow {
|
||||
0%, 100% { box-shadow: 0 0 0 1px color-mix(in srgb, #6ecb8f 35%, transparent), 0 0 14px -6px color-mix(in srgb, #6ecb8f 55%, transparent); }
|
||||
50% { box-shadow: 0 0 0 1px color-mix(in srgb, #6ecb8f 55%, transparent), 0 0 22px -4px color-mix(in srgb, #6ecb8f 75%, transparent); }
|
||||
}
|
||||
.loyalty-ready-icon { font-size: 1.9rem; flex-shrink: 0; }
|
||||
.loyalty-ready-body { display: flex; flex-direction: column; gap: 0.35rem; }
|
||||
.loyalty-ready-body strong { font-size: 0.95rem; }
|
||||
.loyalty-ready-body p { margin: 0; }
|
||||
@media (prefers-reduced-motion: reduce) { .loyalty-ready-block { animation: none; } }
|
||||
|
||||
/* Kurze Sammel-Regeln als Icon-Liste statt Fließtext — schneller erfassbar. */
|
||||
.loyalty-rules { list-style: none; margin: 0.2rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.4rem; font-size: 0.86rem; color: var(--c-text-muted); }
|
||||
.loyalty-rules li { display: flex; align-items: flex-start; gap: 0.5em; }
|
||||
.loyalty-card-sparkle { position: absolute; font-size: 0.85rem; pointer-events: none; z-index: 1; }
|
||||
.loyalty-card-sparkle-1 { top: 1.1rem; right: 1.3rem; animation: account-nav-sparkle-twinkle 2.7s ease-in-out infinite; }
|
||||
.loyalty-card-sparkle-2 { bottom: 1.2rem; left: 1.3rem; font-size: 0.7rem; animation: account-nav-sparkle-twinkle 3.5s ease-in-out infinite 0.5s; }
|
||||
|
||||
Reference in New Issue
Block a user