Abosystem mit dauerhaften Rabatten und Teddy-Prämien

Drei Mitgliedschaftsstufen (Klein 4,99€/5%, Mittel 9,99€/10%, Groß 19,99€/20%)
mit automatischem Bestellrabatt und Teddy-Prämien nach Meilensteinen (12-Monats-
Rhythmus bzw. 6+12 bei Groß). Preise/Rabatte und alle Admin-Regeln (Kombinier-
barkeit mit anderen Rabatten, Fortsetzung bei Wiederanmeldung, Monate über
Stufen zusammenzählen, kostenloser Versand bei Prämie) sind über Decap CMS
einstellbar (src/content/abo-einstellungen.json).

Neuer "Mein Abo"-Bereich im Kundenkonto (6. Nav-Kachel) mit Plan-Vergleich,
Live-Status (Laufzeit, nächste Zahlung, Rabatt, Fortschritt bis zur nächsten
Prämie), Einlösen-Buttons für freigeschaltete Teddys, Kündigen/Wechseln sowie
einer übersetzten Benachrichtigungsliste. Dazu ein extra hervorgehobener
"Mein Abo verwalten"-Button neben Abmelden in der Kopfkarte.

Warenkorb/Checkout rechnen den Abo-Rabatt korrekt in die Summe ein und stellen
eingelöste Teddys als 0€-Gratisposition dar. Alles in 4 Sprachen (DE/EN/CH/FR).

Unterwegs zwei echte Bugs gefunden und behoben: addMonateISO() vermischte
lokale Zeit mit toISOString() (UTC) und verlor dadurch bei jeder Monats-
verlängerung reproduzierbar einen Tag; die Checkout-Positionsliste zeigte für
Gratis-Prämien fälschlich den vollen Preis statt 0€.

Wie beim Treuebonus gilt: Speicherung aktuell im localStorage des Geräts
(Phase 1) statt einer echten geräteübergreifenden Datenbank — folgt mit der
Zahlungsanbindung in Phase 2.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
qcigano
2026-08-03 16:54:25 +02:00
co-authored by Claude Sonnet 5
parent 0cb7d3ca65
commit 208d995941
21 changed files with 1902 additions and 91 deletions
+180
View File
@@ -180,6 +180,8 @@ export const ui = {
loyaltyBannerText: "Wähle unten den Artikel aus, auf den du 20 % Rabatt erhältst.",
loyaltySelectLabel: "Für 20 % Treuebonus auswählen",
loyaltySelectedLabel: "🎁 -20 % Treuebonus angewendet",
gratisPraemie: "Gratis-Prämie aus deinem Abo",
aboDiscount: "🎫 Abo-Rabatt",
},
checkout: {
eyebrow: "Checkout", title: "Bestellung abschließen",
@@ -238,6 +240,49 @@ 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.",
statAbo: "Mein Abo",
aboHeaderBtn: "🎫 Mein Abo verwalten",
aboTitle: "🎫 Mein Abo",
aboIntro: "Sichere dir dauerhaften Rabatt auf jede Bestellung und hol dir kostenlose Teddys als Dankeschön — automatisch, jeden Monat.",
aboNameKlein: "Kleines Abo", aboNameMittel: "Mittleres Abo", aboNameGross: "Großes Abo",
aboPreisSuffix: "/ Monat",
aboVorteilKlein: "5 % Rabatt auf jede Bestellung. Nach jedem vollen Abo-Jahr: 1 kleiner Teddy gratis.",
aboVorteilMittel: "10 % Rabatt auf jede Bestellung. Nach jedem vollen Abo-Jahr: 1 großer Teddy gratis.",
aboVorteilGross: "20 % Rabatt auf jede Bestellung. Alle 6 Monate ein kleiner Teddy, nach jedem vollen Jahr zusätzlich ein großer Teddy.",
aboAbschliessenBtn: "Abo abschließen",
aboAktivesLabel: "✓ Dein aktuelles Abo",
aboWechselnBtn: "Zu diesem Abo wechseln",
aboStatusTitle: "Dein Abo-Status",
aboFieldStufe: "Abo-Stufe", aboFieldPreis: "Monatlicher Preis", aboFieldRabatt: "Enthaltener Rabatt",
aboFieldStart: "Startdatum", aboFieldNaechsteZahlung: "Nächste Zahlung", aboFieldLaufzeit: "Bisherige Laufzeit",
aboMonateSuffix: "Monate",
aboFortschrittKleinTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten kleinen Teddy",
aboFortschrittGrossTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten großen Teddy",
aboPraemienErhaltenTemplate: "Bereits erhalten: {klein}× kleiner Teddy, {gross}× großer Teddy",
aboPraemienVerfuegbarTitle: "🎉 Verfügbare Prämien",
aboKeinePraemienVerfuegbar: "Aktuell keine Prämie zum Einlösen verfügbar.",
aboEinloesenBtn: "Jetzt einlösen",
aboEinloesenKleinLabel: "Kleiner Teddy", aboEinloesenGrossLabel: "Großer Teddy",
aboEingeloestHinweis: "🧸 In den Warenkorb gelegt!",
aboZahlungSimBtn: "Monatszahlung simulieren (Demo)",
aboZahlungSimHinweis: "Phase 1: Es gibt noch keine echte wiederkehrende Zahlung im Hintergrund. Mit diesem Knopf testest du, was bei der nächsten erfolgreichen Monatsabbuchung passiert.",
aboKuendigenBtn: "Abo kündigen",
aboKuendigenConfirm: "Abo wirklich kündigen? Deine Vorteile bleiben bis zum Ende der bezahlten Laufzeit aktiv.",
aboGekuendigtHinweisTemplate: "Gekündigt am {datum}. Deine Vorteile bleiben bis zum Ende der bezahlten Laufzeit aktiv, danach endet dein Abo automatisch.",
aboWechselTitle: "Zu einem anderen Abo wechseln",
aboBenachrichtigungenTitle: "🔔 Benachrichtigungen",
aboKeineBenachrichtigungen: "Noch keine Benachrichtigungen.",
aboNichtAktivBadge: "⏸ Rabatt pausiert",
aboExplainer: "Alle Angaben werden aktuell sicher auf diesem Gerät gespeichert (Phase 1). Eine geräteübergreifende Datenbank folgt mit der echten Zahlungsanbindung in Phase 2.",
notifAboAbgeschlossen: "Dein Abo wurde erfolgreich abgeschlossen — willkommen! 🎉",
notifAboGewechselt: "Du hast erfolgreich in ein anderes Abo gewechselt ({stufe}).",
notifAboZahlungErfolgreich: "Deine monatliche Abo-Zahlung war erfolgreich. ✅",
notifAboTeddyKleinFrei: "🧸 Neue Prämie freigeschaltet: ein kleiner Teddy wartet auf dich!",
notifAboTeddyGrossFrei: "🧸 Neue Prämie freigeschaltet: ein großer Teddy wartet auf dich!",
notifAboBaldPraemie: "🧸 Fast geschafft — nächsten Monat wartet deine nächste Teddy-Prämie!",
notifAboGekuendigt: "Du hast dein Abo gekündigt. Deine Vorteile bleiben bis zum Ende der bezahlten Laufzeit aktiv.",
notifAboTeddyKleinEingeloest: "🧸 Kleiner Teddy eingelöst — er liegt jetzt kostenlos in deinem Warenkorb.",
notifAboTeddyGrossEingeloest: "🧸 Großer Teddy eingelöst — er liegt jetzt kostenlos in deinem Warenkorb.",
noOpenOrders: "Aktuell keine offenen Sendungen — alle Bestellungen sind schon angekommen. 🎉",
noArrivedOrders: "Noch keine angekommenen Bestellungen — hier erscheinen deine Bestellungen, sobald sie geliefert wurden.",
ordersTitle: "📦 Meine Bestellungen",
@@ -431,6 +476,8 @@ export const ui = {
loyaltyBannerText: "Pick the item below that you'd like 20% off.",
loyaltySelectLabel: "Select for 20% loyalty reward",
loyaltySelectedLabel: "🎁 -20% loyalty reward applied",
gratisPraemie: "Free reward from your subscription",
aboDiscount: "🎫 Subscription discount",
},
checkout: {
eyebrow: "Checkout", title: "Complete your order",
@@ -489,6 +536,49 @@ 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.",
statAbo: "My Subscription",
aboHeaderBtn: "🎫 Manage my subscription",
aboTitle: "🎫 My Subscription",
aboIntro: "Get a permanent discount on every order and free teddies as a thank-you — automatically, every month.",
aboNameKlein: "Small plan", aboNameMittel: "Medium plan", aboNameGross: "Large plan",
aboPreisSuffix: "/ month",
aboVorteilKlein: "5% off every order. After each full subscription year: 1 small teddy for free.",
aboVorteilMittel: "10% off every order. After each full subscription year: 1 large teddy for free.",
aboVorteilGross: "20% off every order. A small teddy every 6 months, plus a large teddy after every full year.",
aboAbschliessenBtn: "Subscribe",
aboAktivesLabel: "✓ Your current plan",
aboWechselnBtn: "Switch to this plan",
aboStatusTitle: "Your subscription status",
aboFieldStufe: "Plan", aboFieldPreis: "Monthly price", aboFieldRabatt: "Included discount",
aboFieldStart: "Start date", aboFieldNaechsteZahlung: "Next payment", aboFieldLaufzeit: "Duration so far",
aboMonateSuffix: "months",
aboFortschrittKleinTemplate: "🧸 {n} more month(s) until your next small teddy",
aboFortschrittGrossTemplate: "🧸 {n} more month(s) until your next large teddy",
aboPraemienErhaltenTemplate: "Already received: {klein}× small teddy, {gross}× large teddy",
aboPraemienVerfuegbarTitle: "🎉 Available rewards",
aboKeinePraemienVerfuegbar: "No reward available to redeem right now.",
aboEinloesenBtn: "Redeem now",
aboEinloesenKleinLabel: "Small teddy", aboEinloesenGrossLabel: "Large teddy",
aboEingeloestHinweis: "🧸 Added to your cart!",
aboZahlungSimBtn: "Simulate monthly payment (demo)",
aboZahlungSimHinweis: "Phase 1: there is no real recurring payment running in the background yet. Use this button to test what happens on the next successful monthly payment.",
aboKuendigenBtn: "Cancel subscription",
aboKuendigenConfirm: "Really cancel your subscription? Your benefits stay active until the end of the paid period.",
aboGekuendigtHinweisTemplate: "Cancelled on {datum}. Your benefits stay active until the end of the paid period, then your subscription ends automatically.",
aboWechselTitle: "Switch to a different plan",
aboBenachrichtigungenTitle: "🔔 Notifications",
aboKeineBenachrichtigungen: "No notifications yet.",
aboNichtAktivBadge: "⏸ Discount paused",
aboExplainer: "All data is currently stored securely on this device (Phase 1). A cross-device database follows with real payment integration in Phase 2.",
notifAboAbgeschlossen: "Your subscription was successfully set up — welcome! 🎉",
notifAboGewechselt: "You successfully switched to a different plan ({stufe}).",
notifAboZahlungErfolgreich: "Your monthly subscription payment was successful. ✅",
notifAboTeddyKleinFrei: "🧸 New reward unlocked: a small teddy is waiting for you!",
notifAboTeddyGrossFrei: "🧸 New reward unlocked: a large teddy is waiting for you!",
notifAboBaldPraemie: "🧸 Almost there — your next teddy reward is waiting next month!",
notifAboGekuendigt: "You cancelled your subscription. Your benefits stay active until the end of the paid period.",
notifAboTeddyKleinEingeloest: "🧸 Small teddy redeemed — it's now in your cart for free.",
notifAboTeddyGrossEingeloest: "🧸 Large teddy redeemed — it's now in your cart for free.",
noOpenOrders: "No open shipments right now — every order has already arrived. 🎉",
noArrivedOrders: "No delivered orders yet — your orders will appear here once they've arrived.",
ordersTitle: "📦 My orders",
@@ -682,6 +772,8 @@ export const ui = {
loyaltyBannerText: "Wähl unde dr Artikel us, uf dä du 20 % Rabatt übercho.",
loyaltySelectLabel: "Für 20 % Treuebonus uswähle",
loyaltySelectedLabel: "🎁 -20 % Treuebonus aagwändet",
gratisPraemie: "Gratisprämie us dim Abo",
aboDiscount: "🎫 Abo-Rabatt",
},
checkout: {
eyebrow: "Checkout", title: "Bschtellig abschliesse",
@@ -740,6 +832,49 @@ 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.",
statAbo: "Mis Abo",
aboHeaderBtn: "🎫 Mis Abo verwalte",
aboTitle: "🎫 Mis Abo",
aboIntro: "Sicher dir en dauerhafte Rabatt uf jedi Bstellig und hol dir gratis Teddys als Dankeschön — automatisch, jede Monet.",
aboNameKlein: "Chliises Abo", aboNameMittel: "Mittleres Abo", aboNameGross: "Grosses Abo",
aboPreisSuffix: "/ Monet",
aboVorteilKlein: "5 % Rabatt uf jedi Bstellig. Nach jedem volle Abo-Jahr: 1 chliine Teddy gratis.",
aboVorteilMittel: "10 % Rabatt uf jedi Bstellig. Nach jedem volle Abo-Jahr: 1 grosse Teddy gratis.",
aboVorteilGross: "20 % Rabatt uf jedi Bstellig. Alli 6 Monet en chliine Teddy, nach jedem volle Jahr zuesätzlich en grosse Teddy.",
aboAbschliessenBtn: "Abo abschliesse",
aboAktivesLabel: "✓ Dis aktuells Abo",
aboWechselnBtn: "Zu dem Abo wechsle",
aboStatusTitle: "Din Abo-Status",
aboFieldStufe: "Abo-Stufe", aboFieldPreis: "Monetliche Priis", aboFieldRabatt: "Enthaltene Rabatt",
aboFieldStart: "Startdatum", aboFieldNaechsteZahlung: "Nächschti Zahlig", aboFieldLaufzeit: "Bisherigi Laufziit",
aboMonateSuffix: "Monet",
aboFortschrittKleinTemplate: "🧸 No {n} Monet bis zum nächschte chliine Teddy",
aboFortschrittGrossTemplate: "🧸 No {n} Monet bis zum nächschte grosse Teddy",
aboPraemienErhaltenTemplate: "Scho erhalte: {klein}× chliine Teddy, {gross}× grosse Teddy",
aboPraemienVerfuegbarTitle: "🎉 Verfüegbari Prämie",
aboKeinePraemienVerfuegbar: "Momentan kei Prämie zum Ilöse verfüegbar.",
aboEinloesenBtn: "Jetzt ilöse",
aboEinloesenKleinLabel: "Chliine Teddy", aboEinloesenGrossLabel: "Grosse Teddy",
aboEingeloestHinweis: "🧸 In Warechorb gleit!",
aboZahlungSimBtn: "Monetszahlig simuliere (Demo)",
aboZahlungSimHinweis: "Phase 1: Es git no kei echti wiederkehrendi Zahlig im Hintergrund. Mit dem Chnopf teschtisch, was bi dr nächschte erfolgriiche Monetsabbuechig passiert.",
aboKuendigenBtn: "Abo kündige",
aboKuendigenConfirm: "Abo würklich kündige? Dini Vorteil bliibed bis am Ändi vo dr bezahlte Laufziit aktiv.",
aboGekuendigtHinweisTemplate: "Kündet am {datum}. Dini Vorteil bliibed bis am Ändi vo dr bezahlte Laufziit aktiv, dernah ändet dis Abo automatisch.",
aboWechselTitle: "Zu emene andere Abo wechsle",
aboBenachrichtigungenTitle: "🔔 Benachrichtigunge",
aboKeineBenachrichtigungen: "No kei Benachrichtigunge.",
aboNichtAktivBadge: "⏸ Rabatt pausiert",
aboExplainer: "Alli Aagabe werded aktuell sicher uf dem Gerät gspeicheret (Phase 1). Ä geräteübergriifendi Datebank chunt mit dr echte Zahligs-Aabindig i Phase 2.",
notifAboAbgeschlossen: "Dis Abo isch erfolgriich abgschlosse worde — willkomme! 🎉",
notifAboGewechselt: "Du bisch erfolgriich in es anders Abo gwechslet ({stufe}).",
notifAboZahlungErfolgreich: "Dini monetlichi Abo-Zahlig isch erfolgriich gsi. ✅",
notifAboTeddyKleinFrei: "🧸 Nöii Prämie freigschaltet: en chliine Teddy wartet uf dich!",
notifAboTeddyGrossFrei: "🧸 Nöii Prämie freigschaltet: en grosse Teddy wartet uf dich!",
notifAboBaldPraemie: "🧸 Fasch gschafft — nächschte Monet wartet dini nächschti Teddy-Prämie!",
notifAboGekuendigt: "Du hesch dis Abo kündet. Dini Vorteil bliibed bis am Ändi vo dr bezahlte Laufziit aktiv.",
notifAboTeddyKleinEingeloest: "🧸 Chliine Teddy ilöst — er liit jetzt gratis in dim Warechorb.",
notifAboTeddyGrossEingeloest: "🧸 Grosse Teddy ilöst — er liit jetzt gratis in dim Warechorb.",
noOpenOrders: "Momentan kei offni Sändige — alli Bstellige sind scho aacho. 🎉",
noArrivedOrders: "No kei aacho Bstellige — da erschine dini Bstellige, sobald sie glifert worde sind.",
ordersTitle: "📦 Mini Bstellige",
@@ -933,6 +1068,8 @@ export const ui = {
loyaltyBannerText: "Choisissez ci-dessous l'article sur lequel vous voulez -20 %.",
loyaltySelectLabel: "Choisir pour la récompense fidélité de 20 %",
loyaltySelectedLabel: "🎁 Récompense fidélité de -20 % appliquée",
gratisPraemie: "Récompense gratuite de votre abonnement",
aboDiscount: "🎫 Remise d'abonnement",
},
checkout: {
eyebrow: "Paiement", title: "Finaliser la commande",
@@ -991,6 +1128,49 @@ 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.",
statAbo: "Mon abonnement",
aboHeaderBtn: "🎫 Gérer mon abonnement",
aboTitle: "🎫 Mon abonnement",
aboIntro: "Profitez d'une remise permanente sur chaque commande et d'oursons en peluche gratuits en cadeau — automatiquement, chaque mois.",
aboNameKlein: "Petit abonnement", aboNameMittel: "Abonnement moyen", aboNameGross: "Grand abonnement",
aboPreisSuffix: "/ mois",
aboVorteilKlein: "5 % de remise sur chaque commande. Après chaque année d'abonnement complète : 1 petit ours en peluche offert.",
aboVorteilMittel: "10 % de remise sur chaque commande. Après chaque année d'abonnement complète : 1 grand ours en peluche offert.",
aboVorteilGross: "20 % de remise sur chaque commande. Un petit ours tous les 6 mois, et un grand ours en plus après chaque année complète.",
aboAbschliessenBtn: "S'abonner",
aboAktivesLabel: "✓ Votre abonnement actuel",
aboWechselnBtn: "Passer à cet abonnement",
aboStatusTitle: "Statut de votre abonnement",
aboFieldStufe: "Formule", aboFieldPreis: "Prix mensuel", aboFieldRabatt: "Remise incluse",
aboFieldStart: "Date de début", aboFieldNaechsteZahlung: "Prochain paiement", aboFieldLaufzeit: "Durée jusqu'ici",
aboMonateSuffix: "mois",
aboFortschrittKleinTemplate: "🧸 Encore {n} mois avant votre prochain petit ours",
aboFortschrittGrossTemplate: "🧸 Encore {n} mois avant votre prochain grand ours",
aboPraemienErhaltenTemplate: "Déjà reçus : {klein}× petit ours, {gross}× grand ours",
aboPraemienVerfuegbarTitle: "🎉 Récompenses disponibles",
aboKeinePraemienVerfuegbar: "Aucune récompense à échanger pour le moment.",
aboEinloesenBtn: "Échanger maintenant",
aboEinloesenKleinLabel: "Petit ours", aboEinloesenGrossLabel: "Grand ours",
aboEingeloestHinweis: "🧸 Ajouté à votre panier !",
aboZahlungSimBtn: "Simuler le paiement mensuel (démo)",
aboZahlungSimHinweis: "Phase 1 : il n'y a pas encore de vrai paiement récurrent en arrière-plan. Ce bouton permet de tester ce qui se passe lors du prochain paiement mensuel réussi.",
aboKuendigenBtn: "Résilier l'abonnement",
aboKuendigenConfirm: "Résilier vraiment l'abonnement ? Vos avantages restent actifs jusqu'à la fin de la période payée.",
aboGekuendigtHinweisTemplate: "Résilié le {datum}. Vos avantages restent actifs jusqu'à la fin de la période payée, puis votre abonnement se termine automatiquement.",
aboWechselTitle: "Passer à une autre formule",
aboBenachrichtigungenTitle: "🔔 Notifications",
aboKeineBenachrichtigungen: "Pas encore de notifications.",
aboNichtAktivBadge: "⏸ Remise suspendue",
aboExplainer: "Toutes les données sont actuellement stockées en sécurité sur cet appareil (Phase 1). Une base de données multi-appareils suivra avec le vrai paiement en Phase 2.",
notifAboAbgeschlossen: "Votre abonnement a été souscrit avec succès — bienvenue ! 🎉",
notifAboGewechselt: "Vous êtes passé(e) avec succès à un autre abonnement ({stufe}).",
notifAboZahlungErfolgreich: "Votre paiement mensuel d'abonnement a été effectué avec succès. ✅",
notifAboTeddyKleinFrei: "🧸 Nouvelle récompense débloquée : un petit ours vous attend !",
notifAboTeddyGrossFrei: "🧸 Nouvelle récompense débloquée : un grand ours vous attend !",
notifAboBaldPraemie: "🧸 Presque au but — votre prochaine récompense en peluche arrive le mois prochain !",
notifAboGekuendigt: "Vous avez résilié votre abonnement. Vos avantages restent actifs jusqu'à la fin de la période payée.",
notifAboTeddyKleinEingeloest: "🧸 Petit ours échangé — il est maintenant gratuit dans votre panier.",
notifAboTeddyGrossEingeloest: "🧸 Grand ours échangé — il est maintenant gratuit dans votre panier.",
noOpenOrders: "Aucun envoi en cours pour le moment — toutes les commandes sont déjà arrivées. 🎉",
noArrivedOrders: "Aucune commande livrée pour le moment — vos commandes apparaîtront ici dès qu'elles seront arrivées.",
ordersTitle: "📦 Mes commandes",