diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index f6772da..66ee289 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -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.", 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.", + 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", aboHeaderBtn: "🎫 Mein Abo verwalten", 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.", loyaltyGoToCart: "Choose item in cart →", 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", aboHeaderBtn: "🎫 Manage 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.", 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.", + 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", aboHeaderBtn: "🎫 Mis Abo verwalte", 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.", 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.", + 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", aboHeaderBtn: "🎫 Gérer mon abonnement", aboTitle: "🎫 Mon abonnement", diff --git a/src/pages/ch/konto/angemeldet.astro b/src/pages/ch/konto/angemeldet.astro index 5e307b0..a100dba 100644 --- a/src/pages/ch/konto/angemeldet.astro +++ b/src/pages/ch/konto/angemeldet.astro @@ -324,11 +324,18 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
-
-

-

- -

{t.accountDash.loyaltyExplainer}

+ +
+
+

+

+ +

{t.accountDash.loyaltyExplainer}

+
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status === function renderLoyalty() { const f = treueFortschritt(getAccount()); const navValue = document.getElementById("loyalty-nav-value"); - if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`; - document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv); + if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`; + 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; 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(); 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 // rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste). const aboNameMap: Record = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross }; diff --git a/src/pages/ch/warenkorb/index.astro b/src/pages/ch/warenkorb/index.astro index f64c449..3917db9 100644 --- a/src/pages/ch/warenkorb/index.astro +++ b/src/pages/ch/warenkorb/index.astro @@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr // 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. const konto = getAccount(); - const treueAktiv = treueFortschritt(konto).aktiv; + const treueFortschrittKonto = treueFortschritt(konto); + const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt; const gewaehlterSlug = konto.treuebonusArtikelSlug; const loyaltyBanner = document.getElementById("loyalty-banner"); if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none"; diff --git a/src/pages/en/konto/angemeldet.astro b/src/pages/en/konto/angemeldet.astro index 91d6cc2..0a85401 100644 --- a/src/pages/en/konto/angemeldet.astro +++ b/src/pages/en/konto/angemeldet.astro @@ -324,11 +324,18 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
-
-

-

- -

{t.accountDash.loyaltyExplainer}

+ +
+
+

+

+ +

{t.accountDash.loyaltyExplainer}

+
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status === function renderLoyalty() { const f = treueFortschritt(getAccount()); const navValue = document.getElementById("loyalty-nav-value"); - if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`; - document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv); + if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`; + 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; 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(); 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 // rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste). const aboNameMap: Record = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross }; diff --git a/src/pages/en/warenkorb/index.astro b/src/pages/en/warenkorb/index.astro index a01a5ca..1d43213 100644 --- a/src/pages/en/warenkorb/index.astro +++ b/src/pages/en/warenkorb/index.astro @@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr // 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. const konto = getAccount(); - const treueAktiv = treueFortschritt(konto).aktiv; + const treueFortschrittKonto = treueFortschritt(konto); + const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt; const gewaehlterSlug = konto.treuebonusArtikelSlug; const loyaltyBanner = document.getElementById("loyalty-banner"); if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none"; diff --git a/src/pages/fr/konto/angemeldet.astro b/src/pages/fr/konto/angemeldet.astro index 6ebe9ca..dfaf06e 100644 --- a/src/pages/fr/konto/angemeldet.astro +++ b/src/pages/fr/konto/angemeldet.astro @@ -324,11 +324,18 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
-
-

-

- -

{t.accountDash.loyaltyExplainer}

+ +
+
+

+

+ +

{t.accountDash.loyaltyExplainer}

+
@@ -545,8 +552,14 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status === function renderLoyalty() { const f = treueFortschritt(getAccount()); const navValue = document.getElementById("loyalty-nav-value"); - if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`; - document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv); + if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`; + 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; 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(); 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 // rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste). const aboNameMap: Record = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross }; diff --git a/src/pages/fr/warenkorb/index.astro b/src/pages/fr/warenkorb/index.astro index bb7741a..c1c3f7b 100644 --- a/src/pages/fr/warenkorb/index.astro +++ b/src/pages/fr/warenkorb/index.astro @@ -117,7 +117,8 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../../scr // 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. const konto = getAccount(); - const treueAktiv = treueFortschritt(konto).aktiv; + const treueFortschrittKonto = treueFortschritt(konto); + const treueAktiv = treueFortschrittKonto.aktiv && !treueFortschrittKonto.gesperrt; const gewaehlterSlug = konto.treuebonusArtikelSlug; const loyaltyBanner = document.getElementById("loyalty-banner"); if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none"; diff --git a/src/pages/konto/angemeldet.astro b/src/pages/konto/angemeldet.astro index 1455c4d..8888904 100644 --- a/src/pages/konto/angemeldet.astro +++ b/src/pages/konto/angemeldet.astro @@ -325,11 +325,21 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
-
-

-

- -

{t.accountDash.loyaltyExplainer}

+ {/* 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). */} + +
+
+

+

+ +

{t.accountDash.loyaltyExplainer}

+
@@ -543,11 +553,19 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status === window.addEventListener("account:changed", renderProfil); // 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() { const f = treueFortschritt(getAccount()); const navValue = document.getElementById("loyalty-nav-value"); - if (navValue) navValue.textContent = `${f.aktuelle}/${f.ziel}`; - document.querySelector(".account-nav-loyalty")?.classList.toggle("is-ready", f.aktiv); + if (navValue) navValue.textContent = f.gesperrt ? "🔒" : `${f.aktuelle}/${f.ziel}`; + 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; 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(); 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 // rendern (Nav-Kachel + Planvergleich + Statuskarte + Benachrichtigungsliste). const aboNameMap: Record = { klein: aboLabels.nameKlein, mittel: aboLabels.nameMittel, gross: aboLabels.nameGross }; diff --git a/src/pages/warenkorb/index.astro b/src/pages/warenkorb/index.astro index 7f88092..7bc2238 100644 --- a/src/pages/warenkorb/index.astro +++ b/src/pages/warenkorb/index.astro @@ -117,7 +117,10 @@ import { getAccount, treueFortschritt, setTreuebonusArtikel } from "../../script // 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. 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 loyaltyBanner = document.getElementById("loyalty-banner"); if (loyaltyBanner) loyaltyBanner.style.display = treueAktiv ? "flex" : "none"; diff --git a/src/scripts/account.ts b/src/scripts/account.ts index b5aa150..56dfd1a 100644 --- a/src/scripts/account.ts +++ b/src/scripts/account.ts @@ -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 * 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 - * 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 { const a = getAccount(); if (a.treuebonusAktiv) return a; + if (!aboStatus(a).aktiv) return a; const naechsterStand = Math.min(TREUE_ZIEL, a.treueBestellungen + 1); const updated: Account = { ...a, @@ -265,6 +271,9 @@ export interface TreueFortschritt { ziel: number; fehlende: number; 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 …"). */ @@ -274,6 +283,7 @@ export function treueFortschritt(a: Account): TreueFortschritt { ziel: TREUE_ZIEL, fehlende: Math.max(0, TREUE_ZIEL - a.treueBestellungen), aktiv: a.treuebonusAktiv, + gesperrt: !aboStatus(a).aktiv, }; } diff --git a/src/scripts/cart.ts b/src/scripts/cart.ts index 9ab3e82..759a5f2 100644 --- a/src/scripts/cart.ts +++ b/src/scripts/cart.ts @@ -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 * 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 - * 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 { const a = getAccount(); if (!a.treuebonusAktiv || !a.treuebonusArtikelSlug) return 0; + if (!aboStatus(a).aktiv) return 0; const item = getCart().find((i) => i.slug === a.treuebonusArtikelSlug); if (!item || !istAusgewaehlt(item)) return 0; const produkt = getProduct(item.slug); diff --git a/src/styles/global.css b/src/styles/global.css index a93f00f..7354ff2 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -1492,6 +1492,12 @@ a:focus-visible, button:focus-visible { /* Treuebonus-Fortschrittskarte im Inhaltsbereich — eigener grüner Farbton, sonst dieselbe "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-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 { width: 100%; height: 12px;