Treuebonus nur noch mit aktivem Abo nutzbar
Der Treuebonus (10 Bestellungen → 20% Rabatt) sammelt jetzt nur noch, solange
ein aktives, bezahltes Abo besteht — ohne Abo bleibt der Fortschritt einfach
stehen, statt weiterzuzählen (account.ts: registriereAbgeschlosseneBestellung()).
Ebenso greift ein bereits freigeschalteter Rabatt im Warenkorb/Checkout nur,
solange das Abo aktiv ist (cart.ts: treuebonusDiscount()).
Im Kundenkonto wird die Treuebonus-Karte ohne aktives Abo nicht einfach
ausgeblendet, sondern zeigt bewusst einen einladenden gesperrten Zustand
("🔒 Nur mit aktivem Abo verfügbar" + Button "Zum Abo →") statt der üblichen
Fortschrittsanzeige — soll als klarer Anreiz fürs Abo wirken, nicht als totes
Feature. Dieselbe Sperre greift auch im Warenkorb: das Auswahl-Radio/Badge für
den Treuebonus-Artikel erscheint nur noch bei tatsächlich aktivem Abo, damit
nie ein Rabatt suggeriert wird, der beim Checkout dann doch nicht greift.
Alles in 4 Sprachen (DE/EN/CH/FR).
Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
64110484de
commit
902516753d
@@ -241,6 +241,9 @@ export const ui = {
|
|||||||
loyaltyUnlockedText: "Du hast zehn Bestellungen abgeschlossen und erhältst bei deiner nächsten Bestellung 20 % Rabatt auf einen Artikel deiner Wahl.",
|
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 →",
|
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.",
|
loyaltyExplainer: "Es zählen nur vollständig abgeschlossene und bezahlte Bestellungen. Nach der Einlösung beginnt dein Fortschritt wieder bei null.",
|
||||||
|
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 →",
|
||||||
statAbo: "Mein Abo",
|
statAbo: "Mein Abo",
|
||||||
aboHeaderBtn: "🎫 Mein Abo verwalten",
|
aboHeaderBtn: "🎫 Mein Abo verwalten",
|
||||||
aboTitle: "🎫 Mein Abo",
|
aboTitle: "🎫 Mein Abo",
|
||||||
@@ -539,6 +542,9 @@ export const ui = {
|
|||||||
loyaltyUnlockedText: "You've completed ten orders and get 20% off an item of your choice on your next order.",
|
loyaltyUnlockedText: "You've completed ten orders and get 20% off an item of your choice on your next order.",
|
||||||
loyaltyGoToCart: "Choose item in cart →",
|
loyaltyGoToCart: "Choose item in cart →",
|
||||||
loyaltyExplainer: "Only fully completed and paid orders count. After redemption your progress starts again from zero.",
|
loyaltyExplainer: "Only fully completed and paid orders count. After redemption your progress starts again from zero.",
|
||||||
|
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 →",
|
||||||
statAbo: "My Subscription",
|
statAbo: "My Subscription",
|
||||||
aboHeaderBtn: "🎫 Manage my subscription",
|
aboHeaderBtn: "🎫 Manage my subscription",
|
||||||
aboTitle: "🎫 My Subscription",
|
aboTitle: "🎫 My Subscription",
|
||||||
@@ -837,6 +843,9 @@ export const ui = {
|
|||||||
loyaltyUnlockedText: "Du hesch zäh Bstellige abgschlosse und übercho bi dinere nächschte Bstellig 20 % Rabatt uf en Artikel dinere Wahl.",
|
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 →",
|
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.",
|
loyaltyExplainer: "Es zelle nume vollständig abgschlosseni und bezahlti Bstellige. Nach dr Ilösig fangt din Fortschritt wieder bi null 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 →",
|
||||||
statAbo: "Mis Abo",
|
statAbo: "Mis Abo",
|
||||||
aboHeaderBtn: "🎫 Mis Abo verwalte",
|
aboHeaderBtn: "🎫 Mis Abo verwalte",
|
||||||
aboTitle: "🎫 Mis Abo",
|
aboTitle: "🎫 Mis Abo",
|
||||||
@@ -1135,6 +1144,9 @@ 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.",
|
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 →",
|
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.",
|
loyaltyExplainer: "Seules les commandes entièrement finalisées et payées comptent. Après utilisation, votre progression repart de zéro.",
|
||||||
|
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 →",
|
||||||
statAbo: "Mon abonnement",
|
statAbo: "Mon abonnement",
|
||||||
aboHeaderBtn: "🎫 Gérer mon abonnement",
|
aboHeaderBtn: "🎫 Gérer mon abonnement",
|
||||||
aboTitle: "🎫 Mon abonnement",
|
aboTitle: "🎫 Mon abonnement",
|
||||||
|
|||||||
@@ -324,6 +324,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<div class="card loyalty-card">
|
<div class="card loyalty-card">
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
||||||
|
<div class="loyalty-inner" id="loyalty-locked-content" style="display:none;">
|
||||||
|
<p class="loyalty-locked-title">{t.accountDash.loyaltyLockedTitle}</p>
|
||||||
|
<p class="small">{t.accountDash.loyaltyLockedText}</p>
|
||||||
|
<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-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||||
<p id="loyalty-status-text"></p>
|
<p id="loyalty-status-text"></p>
|
||||||
@@ -331,6 +337,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
||||||
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
function renderLoyalty() {
|
function renderLoyalty() {
|
||||||
const f = treueFortschritt(getAccount());
|
const f = treueFortschritt(getAccount());
|
||||||
const navValue = document.getElementById("loyalty-nav-value");
|
const navValue = document.getElementById("loyalty-nav-value");
|
||||||
if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`;
|
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||||
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv);
|
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv && !f.gesperrt);
|
||||||
|
|
||||||
|
const lockedContent = document.getElementById("loyalty-locked-content");
|
||||||
|
const unlockedContent = document.getElementById("loyalty-unlocked-content");
|
||||||
|
if (lockedContent) lockedContent.style.display = f.gesperrt ? "flex" : "none";
|
||||||
|
if (unlockedContent) unlockedContent.style.display = f.gesperrt ? "none" : "flex";
|
||||||
|
if (f.gesperrt) return;
|
||||||
|
|
||||||
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
||||||
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
||||||
@@ -573,6 +586,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
renderLoyalty();
|
renderLoyalty();
|
||||||
window.addEventListener("account:changed", renderLoyalty);
|
window.addEventListener("account:changed", renderLoyalty);
|
||||||
|
|
||||||
|
// Im gesperrten Treuebonus-Zustand direkt zum Abo-Bereich springen.
|
||||||
|
document.getElementById("loyalty-locked-abo-btn")?.addEventListener("click", () => {
|
||||||
|
setActiveTab("abo");
|
||||||
|
document.getElementById("abo")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
|
||||||
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
||||||
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
||||||
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr
|
|||||||
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
||||||
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
||||||
const konto = getAccount();
|
const konto = getAccount();
|
||||||
const treueAktiv = treueFortschritt(konto).aktiv;
|
const treueFortschrittKonto = treueFortschritt(konto);
|
||||||
|
const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt;
|
||||||
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
||||||
const loyaltyBanner = document.getElementById("loyalty-banner");
|
const loyaltyBanner = document.getElementById("loyalty-banner");
|
||||||
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
||||||
|
|||||||
@@ -324,6 +324,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<div class="card loyalty-card">
|
<div class="card loyalty-card">
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
||||||
|
<div class="loyalty-inner" id="loyalty-locked-content" style="display:none;">
|
||||||
|
<p class="loyalty-locked-title">{t.accountDash.loyaltyLockedTitle}</p>
|
||||||
|
<p class="small">{t.accountDash.loyaltyLockedText}</p>
|
||||||
|
<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-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||||
<p id="loyalty-status-text"></p>
|
<p id="loyalty-status-text"></p>
|
||||||
@@ -331,6 +337,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
||||||
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
function renderLoyalty() {
|
function renderLoyalty() {
|
||||||
const f = treueFortschritt(getAccount());
|
const f = treueFortschritt(getAccount());
|
||||||
const navValue = document.getElementById("loyalty-nav-value");
|
const navValue = document.getElementById("loyalty-nav-value");
|
||||||
if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`;
|
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||||
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv);
|
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv && !f.gesperrt);
|
||||||
|
|
||||||
|
const lockedContent = document.getElementById("loyalty-locked-content");
|
||||||
|
const unlockedContent = document.getElementById("loyalty-unlocked-content");
|
||||||
|
if (lockedContent) lockedContent.style.display = f.gesperrt ? "flex" : "none";
|
||||||
|
if (unlockedContent) unlockedContent.style.display = f.gesperrt ? "none" : "flex";
|
||||||
|
if (f.gesperrt) return;
|
||||||
|
|
||||||
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
||||||
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
||||||
@@ -573,6 +586,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
renderLoyalty();
|
renderLoyalty();
|
||||||
window.addEventListener("account:changed", renderLoyalty);
|
window.addEventListener("account:changed", renderLoyalty);
|
||||||
|
|
||||||
|
// Im gesperrten Treuebonus-Zustand direkt zum Abo-Bereich springen.
|
||||||
|
document.getElementById("loyalty-locked-abo-btn")?.addEventListener("click", () => {
|
||||||
|
setActiveTab("abo");
|
||||||
|
document.getElementById("abo")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
|
||||||
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
||||||
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
||||||
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr
|
|||||||
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
||||||
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
||||||
const konto = getAccount();
|
const konto = getAccount();
|
||||||
const treueAktiv = treueFortschritt(konto).aktiv;
|
const treueFortschrittKonto = treueFortschritt(konto);
|
||||||
|
const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt;
|
||||||
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
||||||
const loyaltyBanner = document.getElementById("loyalty-banner");
|
const loyaltyBanner = document.getElementById("loyalty-banner");
|
||||||
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
||||||
|
|||||||
@@ -324,6 +324,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<div class="card loyalty-card">
|
<div class="card loyalty-card">
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
||||||
|
<div class="loyalty-inner" id="loyalty-locked-content" style="display:none;">
|
||||||
|
<p class="loyalty-locked-title">{t.accountDash.loyaltyLockedTitle}</p>
|
||||||
|
<p class="small">{t.accountDash.loyaltyLockedText}</p>
|
||||||
|
<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-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||||
<p id="loyalty-status-text"></p>
|
<p id="loyalty-status-text"></p>
|
||||||
@@ -331,6 +337,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
||||||
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
function renderLoyalty() {
|
function renderLoyalty() {
|
||||||
const f = treueFortschritt(getAccount());
|
const f = treueFortschritt(getAccount());
|
||||||
const navValue = document.getElementById("loyalty-nav-value");
|
const navValue = document.getElementById("loyalty-nav-value");
|
||||||
if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`;
|
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||||
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv);
|
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv && !f.gesperrt);
|
||||||
|
|
||||||
|
const lockedContent = document.getElementById("loyalty-locked-content");
|
||||||
|
const unlockedContent = document.getElementById("loyalty-unlocked-content");
|
||||||
|
if (lockedContent) lockedContent.style.display = f.gesperrt ? "flex" : "none";
|
||||||
|
if (unlockedContent) unlockedContent.style.display = f.gesperrt ? "none" : "flex";
|
||||||
|
if (f.gesperrt) return;
|
||||||
|
|
||||||
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
||||||
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
||||||
@@ -573,6 +586,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
renderLoyalty();
|
renderLoyalty();
|
||||||
window.addEventListener("account:changed", renderLoyalty);
|
window.addEventListener("account:changed", renderLoyalty);
|
||||||
|
|
||||||
|
// Im gesperrten Treuebonus-Zustand direkt zum Abo-Bereich springen.
|
||||||
|
document.getElementById("loyalty-locked-abo-btn")?.addEventListener("click", () => {
|
||||||
|
setActiveTab("abo");
|
||||||
|
document.getElementById("abo")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
|
||||||
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
||||||
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
||||||
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
||||||
|
|||||||
@@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr
|
|||||||
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
||||||
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
||||||
const konto = getAccount();
|
const konto = getAccount();
|
||||||
const treueAktiv = treueFortschritt(konto).aktiv;
|
const treueFortschrittKonto = treueFortschritt(konto);
|
||||||
|
const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt;
|
||||||
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
||||||
const loyaltyBanner = document.getElementById("loyalty-banner");
|
const loyaltyBanner = document.getElementById("loyalty-banner");
|
||||||
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
||||||
|
|||||||
@@ -325,6 +325,15 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<div class="card loyalty-card">
|
<div class="card loyalty-card">
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-1" aria-hidden="true">✨</span>
|
||||||
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
<span class="loyalty-card-sparkle loyalty-card-sparkle-2" aria-hidden="true">✨</span>
|
||||||
|
{/* Treuebonus ist an ein aktives Abo gekoppelt (soll als Zusatzanreiz fürs Abo wirken) —
|
||||||
|
ohne aktives Abo wird dieser gesperrte Hinweis statt des Fortschritts gezeigt (siehe
|
||||||
|
renderLoyalty() im Skript unten, treueFortschritt().gesperrt). */}
|
||||||
|
<div class="loyalty-inner" id="loyalty-locked-content" style="display:none;">
|
||||||
|
<p class="loyalty-locked-title">{t.accountDash.loyaltyLockedTitle}</p>
|
||||||
|
<p class="small">{t.accountDash.loyaltyLockedText}</p>
|
||||||
|
<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-bar"><div class="loyalty-progress-fill" id="loyalty-bar-fill"></div></div>
|
||||||
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
<p class="loyalty-progress-text" id="loyalty-progress-text"></p>
|
||||||
<p id="loyalty-status-text"></p>
|
<p id="loyalty-status-text"></p>
|
||||||
@@ -332,6 +341,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
<p class="small loyalty-explainer">{t.accountDash.loyaltyExplainer}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
{/* Abosystem: drei Mitgliedschaftsstufen mit dauerhaftem Rabatt + Teddy-Prämien. Preise/Rabatte
|
||||||
@@ -543,11 +553,19 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
window.addEventListener("account:changed", renderProfil);
|
window.addEventListener("account:changed", renderProfil);
|
||||||
|
|
||||||
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
// Treuebonus: Fortschritt/Status live aus dem Konto rendern (Nav-Kachel + Inhaltsbereich).
|
||||||
|
// 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() {
|
function renderLoyalty() {
|
||||||
const f = treueFortschritt(getAccount());
|
const f = treueFortschritt(getAccount());
|
||||||
const navValue = document.getElementById("loyalty-nav-value");
|
const navValue = document.getElementById("loyalty-nav-value");
|
||||||
if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`;
|
if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`;
|
||||||
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv);
|
document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv && !f.gesperrt);
|
||||||
|
|
||||||
|
const lockedContent = document.getElementById("loyalty-locked-content");
|
||||||
|
const unlockedContent = document.getElementById("loyalty-unlocked-content");
|
||||||
|
if (lockedContent) lockedContent.style.display = f.gesperrt ? "flex" : "none";
|
||||||
|
if (unlockedContent) unlockedContent.style.display = f.gesperrt ? "none" : "flex";
|
||||||
|
if (f.gesperrt) return;
|
||||||
|
|
||||||
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
const fill = document.getElementById("loyalty-bar-fill") as HTMLElement | null;
|
||||||
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
if (fill) fill.style.width = `${Math.min(100, (f.aktuelle / f.ziel) * 100)}%`;
|
||||||
@@ -574,6 +592,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
renderLoyalty();
|
renderLoyalty();
|
||||||
window.addEventListener("account:changed", renderLoyalty);
|
window.addEventListener("account:changed", renderLoyalty);
|
||||||
|
|
||||||
|
// Im gesperrten Treuebonus-Zustand direkt zum Abo-Bereich springen.
|
||||||
|
document.getElementById("loyalty-locked-abo-btn")?.addEventListener("click", () => {
|
||||||
|
setActiveTab("abo");
|
||||||
|
document.getElementById("abo")?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||||
|
});
|
||||||
|
|
||||||
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
// Abosystem: Pläne, Status, Fortschritt, Prämien und Benachrichtigungen live aus dem Konto
|
||||||
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
// rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste).
|
||||||
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
const aboNameMap: Record<string, string> = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross };
|
||||||
|
|||||||
@@ -117,7 +117,10 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../script
|
|||||||
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
// Treuebonus: nur relevant, wenn im Konto gerade eine Belohnung auf Einlösung wartet — dann
|
||||||
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
// darf hier EIN Artikel frei ausgewählt werden, auf den die -20% angewendet werden.
|
||||||
const konto = getAccount();
|
const konto = getAccount();
|
||||||
const treueAktiv = treueFortschritt(konto).aktiv;
|
// Treuebonus ist an ein aktives Abo gekoppelt (siehe account.ts) — ohne aktives Abo bleibt
|
||||||
|
// die Auswahl/Anzeige hier ausgeblendet, auch wenn intern noch eine Belohnung "wartet".
|
||||||
|
const treueFortschrittKonto = treueFortschritt(konto);
|
||||||
|
const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt;
|
||||||
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
const gewaehlterSlug = konto.treuebonusArtikelSlug;
|
||||||
const loyaltyBanner = document.getElementById("loyalty-banner");
|
const loyaltyBanner = document.getElementById("loyalty-banner");
|
||||||
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none";
|
||||||
|
|||||||
+11
-1
@@ -226,10 +226,16 @@ export function markiereBestellungBewertet(bestellnummer: string, produktSlug: s
|
|||||||
/** Zählt eine gültige, abgeschlossene Bestellung für den Treuebonus. Zählt NICHT weiter, wenn
|
/** Zählt eine gültige, abgeschlossene Bestellung für den Treuebonus. Zählt NICHT weiter, wenn
|
||||||
* gerade schon eine Belohnung auf Einlösung wartet (erst einlösen, dann läuft der nächste
|
* gerade schon eine Belohnung auf Einlösung wartet (erst einlösen, dann läuft der nächste
|
||||||
* Zähler wieder bei 0 los). Schaltet die Belohnung automatisch frei, sobald TREUE_ZIEL erreicht
|
* Zähler wieder bei 0 los). Schaltet die Belohnung automatisch frei, sobald TREUE_ZIEL erreicht
|
||||||
* ist. */
|
* ist.
|
||||||
|
*
|
||||||
|
* Der Treuebonus ist bewusst an ein AKTIVES Abo gekoppelt (siehe Kundenwunsch: soll als
|
||||||
|
* zusätzlicher Anreiz fürs Abo wirken, nicht unabhängig davon nutzbar sein) — ohne aktives,
|
||||||
|
* bezahltes Abo bleibt der Fortschritt einfach stehen, statt weiterzuzählen. Sobald wieder ein
|
||||||
|
* Abo aktiv ist, geht es von genau diesem Stand aus weiter (kein Reset auf 0). */
|
||||||
export function registriereAbgeschlosseneBestellung(): Account {
|
export function registriereAbgeschlosseneBestellung(): Account {
|
||||||
const a = getAccount();
|
const a = getAccount();
|
||||||
if (a.treuebonusAktiv) return a;
|
if (a.treuebonusAktiv) return a;
|
||||||
|
if (!aboStatus(a).aktiv) return a;
|
||||||
const naechsterStand = Math.min(TREUE_ZIEL, a.treueBestellungen + 1);
|
const naechsterStand = Math.min(TREUE_ZIEL, a.treueBestellungen + 1);
|
||||||
const updated: Account = {
|
const updated: Account = {
|
||||||
...a,
|
...a,
|
||||||
@@ -265,6 +271,9 @@ export interface TreueFortschritt {
|
|||||||
ziel: number;
|
ziel: number;
|
||||||
fehlende: number;
|
fehlende: number;
|
||||||
aktiv: boolean;
|
aktiv: boolean;
|
||||||
|
/** 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Aufbereiteter Fortschritt fürs Anzeigen im Kundenkonto ("7 von 10", "Dir fehlen noch 3 …"). */
|
/** Aufbereiteter Fortschritt fürs Anzeigen im Kundenkonto ("7 von 10", "Dir fehlen noch 3 …"). */
|
||||||
@@ -274,6 +283,7 @@ export function treueFortschritt(a: Account): TreueFortschritt {
|
|||||||
ziel: TREUE_ZIEL,
|
ziel: TREUE_ZIEL,
|
||||||
fehlende: Math.max(0, TREUE_ZIEL - a.treueBestellungen),
|
fehlende: Math.max(0, TREUE_ZIEL - a.treueBestellungen),
|
||||||
aktiv: a.treuebonusAktiv,
|
aktiv: a.treuebonusAktiv,
|
||||||
|
gesperrt: !aboStatus(a).aktiv,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+6
-1
@@ -223,10 +223,15 @@ export function partnerCodeDiscount(_subtotal?: number): number {
|
|||||||
/** Rabatt in Euro, den der aktive Treuebonus auf den gewählten Artikel gibt — 0, wenn kein
|
/** Rabatt in Euro, den der aktive Treuebonus auf den gewählten Artikel gibt — 0, wenn kein
|
||||||
* Bonus aktiv ist, kein Artikel gewählt wurde, oder der gewählte Artikel gerade nicht (mehr)
|
* Bonus aktiv ist, kein Artikel gewählt wurde, oder der gewählte Artikel gerade nicht (mehr)
|
||||||
* zum Kauf ausgewählt im Warenkorb liegt. Gilt bewusst nur für EIN Stück der Position, nicht
|
* zum Kauf ausgewählt im Warenkorb liegt. Gilt bewusst nur für EIN Stück der Position, nicht
|
||||||
* mehrfach bei größerer Menge — "ein frei wählbarer Artikel", kein Mengenrabatt-Ersatz. */
|
* mehrfach bei größerer Menge — "ein frei wählbarer Artikel", kein Mengenrabatt-Ersatz.
|
||||||
|
*
|
||||||
|
* Der Treuebonus ist an ein AKTIVES Abo gekoppelt (siehe account.ts) — falls das Abo zwischen
|
||||||
|
* Freischaltung und Einlösung ausläuft, bleibt die Belohnung zwar erhalten, greift aber erst
|
||||||
|
* wieder, sobald erneut ein Abo aktiv ist. */
|
||||||
export function treuebonusDiscount(): number {
|
export function treuebonusDiscount(): number {
|
||||||
const a = getAccount();
|
const a = getAccount();
|
||||||
if (!a.treuebonusAktiv || !a.treuebonusArtikelSlug) return 0;
|
if (!a.treuebonusAktiv || !a.treuebonusArtikelSlug) return 0;
|
||||||
|
if (!aboStatus(a).aktiv) return 0;
|
||||||
const item = getCart().find((i) => i.slug === a.treuebonusArtikelSlug);
|
const item = getCart().find((i) => i.slug === a.treuebonusArtikelSlug);
|
||||||
if (!item || !istAusgewaehlt(item)) return 0;
|
if (!item || !istAusgewaehlt(item)) return 0;
|
||||||
const produkt = getProduct(item.slug);
|
const produkt = getProduct(item.slug);
|
||||||
|
|||||||
@@ -1492,6 +1492,12 @@ a:focus-visible, button:focus-visible {
|
|||||||
/* Treuebonus-Fortschrittskarte im Inhaltsbereich — eigener grüner Farbton, sonst dieselbe
|
/* Treuebonus-Fortschrittskarte im Inhaltsbereich — eigener grüner Farbton, sonst dieselbe
|
||||||
"besondere" Formsprache wie die anderen Konto-Karten (siehe .profile-card). */
|
"besondere" Formsprache wie die anderen Konto-Karten (siehe .profile-card). */
|
||||||
.loyalty-card { position: relative; overflow: hidden; display: flex; flex-direction: column; gap: 0.9rem; }
|
.loyalty-card { position: relative; overflow: hidden; display: flex; flex-direction: column; gap: 0.9rem; }
|
||||||
|
.loyalty-inner { display: flex; flex-direction: column; gap: 0.9rem; }
|
||||||
|
/* Gesperrter Zustand ohne aktives Abo: soll bewusst NICHT ausgegraut/unattraktiv wirken (der
|
||||||
|
Treuebonus soll als Anreiz fürs Abo dienen), sondern klar als "gibt's schon, nur noch
|
||||||
|
freischalten" — daher derselbe Kartenrahmen wie im freigeschalteten Zustand, nur mit
|
||||||
|
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 {
|
.loyalty-progress-bar {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 12px;
|
height: 12px;
|
||||||
|
|||||||
Reference in New Issue
Block a user