From 208d9959411723a40fb8ca125414452359addfa1 Mon Sep 17 00:00:00 2001 From: qcigano Date: Mon, 3 Aug 2026 16:54:25 +0200 Subject: [PATCH] =?UTF-8?q?Abosystem=20mit=20dauerhaften=20Rabatten=20und?= =?UTF-8?q?=20Teddy-Pr=C3=A4mien?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- public/admin/config.yml | 54 +++++ src/content/abo-einstellungen.json | 9 + src/content/products/grosser-teddy.json | 26 +++ src/content/products/kleiner-teddy.json | 26 +++ src/data/abo.ts | 85 ++++++++ src/i18n/ui.ts | 180 ++++++++++++++++ src/pages/ch/checkout/index.astro | 16 +- src/pages/ch/konto/angemeldet.astro | 259 +++++++++++++++++++++++- src/pages/ch/warenkorb/index.astro | 28 ++- src/pages/checkout/index.astro | 16 +- src/pages/en/checkout/index.astro | 16 +- src/pages/en/konto/angemeldet.astro | 259 +++++++++++++++++++++++- src/pages/en/warenkorb/index.astro | 28 ++- src/pages/fr/checkout/index.astro | 16 +- src/pages/fr/konto/angemeldet.astro | 259 +++++++++++++++++++++++- src/pages/fr/warenkorb/index.astro | 28 ++- src/pages/konto/angemeldet.astro | 259 +++++++++++++++++++++++- src/pages/warenkorb/index.astro | 28 ++- src/scripts/account.ts | 246 ++++++++++++++++++++++ src/scripts/cart.ts | 64 +++++- src/styles/global.css | 91 ++++++++- 21 files changed, 1902 insertions(+), 91 deletions(-) create mode 100644 src/content/abo-einstellungen.json create mode 100644 src/content/products/grosser-teddy.json create mode 100644 src/content/products/kleiner-teddy.json create mode 100644 src/data/abo.ts diff --git a/public/admin/config.yml b/public/admin/config.yml index e634d86..0db4062 100644 --- a/public/admin/config.yml +++ b/public/admin/config.yml @@ -348,3 +348,57 @@ collections: - { label: IBAN, name: iban, widget: string, required: false, hint: "z.B. DE12 3456 7890 1234 5678 90" } - { label: BIC, name: bic, widget: string, required: false } - { label: Bank (Name), name: bank, widget: string, required: false } + + - name: abo-einstellungen + label: "🎁 Abo-System" + icon: card_membership + files: + - name: einstellungen + label: "Abo-Stufen, Rabatte & Regeln" + file: src/content/abo-einstellungen.json + description: > + Preise, Rabatthöhen und Verhaltensregeln für das monatliche Abo-System (siehe + scripts/account.ts + data/abo.ts). Änderungen gelten ab dem nächsten Deploy für alle + Kund:innen. Die eigentlichen Kundendaten (wer welches Abo hat, Zahlungsstand, + freigeschaltete Prämien) liegen NICHT hier, sondern pro Kundenkonto (Phase 1: im + Browser der jeweiligen Person, Phase 2: in einer echten Datenbank) — hier werden nur + die grundsätzlichen Konditionen festgelegt, zu denen JEDES Abo läuft. + fields: + - label: "Kleines Abo" + name: klein + widget: object + fields: + - { label: "Preis pro Monat (€)", name: preis, widget: number, value_type: float, step: 0.01 } + - { label: "Rabatt auf jede Bestellung (%)", name: rabattProzent, widget: number, value_type: int, min: 0, max: 100 } + - label: "Mittleres Abo" + name: mittel + widget: object + fields: + - { label: "Preis pro Monat (€)", name: preis, widget: number, value_type: float, step: 0.01 } + - { label: "Rabatt auf jede Bestellung (%)", name: rabattProzent, widget: number, value_type: int, min: 0, max: 100 } + - label: "Großes Abo" + name: gross + widget: object + fields: + - { label: "Preis pro Monat (€)", name: preis, widget: number, value_type: float, step: 0.01 } + - { label: "Rabatt auf jede Bestellung (%)", name: rabattProzent, widget: number, value_type: int, min: 0, max: 100 } + - label: "Abo-Rabatt mit anderen Rabatten kombinierbar (Gutschein/Partnercode/Treuebonus)?" + name: kombinierbarMitAnderenRabatten + widget: boolean + default: false + hint: "Aus: Bei aktivem Abo gilt NUR der Abo-Rabatt, andere Rabatte werden für diese Bestellung ignoriert. An: alle Rabatte werden zusammengezählt." + - label: "Versand bei Teddy-Prämie ebenfalls kostenlos?" + name: versandKostenlosBeiPraemie + widget: boolean + default: false + hint: "Aus: Für den kostenlosen Teddy gelten die normalen Versandregeln des Shops." + - label: "Bei Wiederanmeldung nach vollständigem Auslaufen: bisherige Laufzeit fortsetzen?" + name: fortsetzungBeiWiederanmeldung + widget: boolean + default: false + hint: "Aus: Der Monatszähler für die nächste Prämie beginnt bei einer Wiederanmeldung wieder bei 0. An: er läuft dort weiter, wo er beim Auslaufen stand. Bereits eingelöste/freigeschaltete Teddys gehen NIE verloren, unabhängig von dieser Einstellung." + - label: "Monate aus verschiedenen Abo-Stufen beim Wechsel zusammenzählen?" + name: monateUeberStufenZusammenzaehlen + widget: boolean + default: false + hint: "Aus (empfohlen): Beim Wechsel in eine andere Stufe beginnt der Monatszähler für die nächste Prämie bei 0. An: die bisher gesammelten Monate werden in die neue Stufe übernommen." diff --git a/src/content/abo-einstellungen.json b/src/content/abo-einstellungen.json new file mode 100644 index 0000000..56f668a --- /dev/null +++ b/src/content/abo-einstellungen.json @@ -0,0 +1,9 @@ +{ + "klein": { "preis": 4.99, "rabattProzent": 5 }, + "mittel": { "preis": 9.99, "rabattProzent": 10 }, + "gross": { "preis": 19.99, "rabattProzent": 20 }, + "kombinierbarMitAnderenRabatten": false, + "versandKostenlosBeiPraemie": false, + "fortsetzungBeiWiederanmeldung": false, + "monateUeberStufenZusammenzaehlen": false +} diff --git a/src/content/products/grosser-teddy.json b/src/content/products/grosser-teddy.json new file mode 100644 index 0000000..d2591c6 --- /dev/null +++ b/src/content/products/grosser-teddy.json @@ -0,0 +1,26 @@ +{ + "slug": "grosser-teddy", + "name": { + "de": "Großer Teddy", + "en": "Large Teddy", + "ch": "Grosse Teddy", + "fr": "Grand ours en peluche" + }, + "artikelnummer": "PRAEMIE-TEDDY-GROSS", + "kategorie": "diy-plushies", + "preis": 24.9, + "bestand": 999, + "beschreibung": { + "de": "Unser handgemachter großer Teddy — als Dankeschön-Prämie im Abo-System kostenlos einlösbar, sobald du den passenden Meilenstein erreicht hast.", + "en": "Our handmade large teddy — redeemable for free as a subscription reward once you reach the matching milestone.", + "ch": "Öise vo Hang gmachti grossi Teddy — als Dankeschön-Prämie im Abo-System gratis ilösbar, sobald du dr passendi Meilestei erreicht hesch.", + "fr": "Notre grand ours en peluche fait main — à échanger gratuitement comme récompense d'abonnement une fois le palier atteint." + }, + "material": "Baumwollstoff, Füllwatte OEKO-TEX", + "groesse": "ca. 30 cm", + "lieferzeit": "2–4 Werktage", + "badges": [ + "handgemacht" + ], + "bilder": [] +} diff --git a/src/content/products/kleiner-teddy.json b/src/content/products/kleiner-teddy.json new file mode 100644 index 0000000..0fac6dd --- /dev/null +++ b/src/content/products/kleiner-teddy.json @@ -0,0 +1,26 @@ +{ + "slug": "kleiner-teddy", + "name": { + "de": "Kleiner Teddy", + "en": "Small Teddy", + "ch": "Chline Teddy", + "fr": "Petit ours en peluche" + }, + "artikelnummer": "PRAEMIE-TEDDY-KLEIN", + "kategorie": "diy-plushies", + "preis": 12.9, + "bestand": 999, + "beschreibung": { + "de": "Unser handgemachter kleiner Teddy — als Dankeschön-Prämie im Abo-System kostenlos einlösbar, sobald du den passenden Meilenstein erreicht hast.", + "en": "Our handmade small teddy — redeemable for free as a subscription reward once you reach the matching milestone.", + "ch": "Öise vo Hang gmachti chlini Teddy — als Dankeschön-Prämie im Abo-System gratis ilösbar, sobald du dr passendi Meilestei erreicht hesch.", + "fr": "Notre petit ours en peluche fait main — à échanger gratuitement comme récompense d'abonnement une fois le palier atteint." + }, + "material": "Baumwollstoff, Füllwatte OEKO-TEX", + "groesse": "ca. 16 cm", + "lieferzeit": "2–4 Werktage", + "badges": [ + "handgemacht" + ], + "bilder": [] +} diff --git a/src/data/abo.ts b/src/data/abo.ts new file mode 100644 index 0000000..6d9501d --- /dev/null +++ b/src/data/abo.ts @@ -0,0 +1,85 @@ +// Abo-System: Preise/Rabatte/Verhaltensregeln kommen aus dem Adminbereich (Decap CMS, siehe +// public/admin/config.yml → "🎁 Abo-System" → src/content/abo-einstellungen.json). Reine, +// zustandslose Berechnungsfunktionen — der eigentliche Konto-Zustand (welches Abo, wie viele +// bezahlte Monate, freigeschaltete Prämien) lebt in scripts/account.ts. + +// @ts-ignore -- JSON-Import, zur Build-Zeit von Astro/Vite aufgelöst +import raw from "../content/abo-einstellungen.json"; + +export type AboStufe = "klein" | "mittel" | "gross"; + +export interface AboStufenKonfig { + preis: number; + rabattProzent: number; +} + +export interface AboEinstellungen { + klein: AboStufenKonfig; + mittel: AboStufenKonfig; + gross: AboStufenKonfig; + kombinierbarMitAnderenRabatten: boolean; + versandKostenlosBeiPraemie: boolean; + fortsetzungBeiWiederanmeldung: boolean; + monateUeberStufenZusammenzaehlen: boolean; +} + +export const aboEinstellungen = raw as AboEinstellungen; + +export function aboStufenKonfig(stufe: AboStufe): AboStufenKonfig { + return aboEinstellungen[stufe]; +} + +export const ABO_STUFEN: AboStufe[] = ["klein", "mittel", "gross"]; + +/** Wie viele Teddy-Prämien welcher Art bei X durchgehend bezahlten Monaten in dieser Stufe + * INSGESAMT freigeschaltet sein müssten (kumulativ, nicht nur "neu diesen Monat"): + * - Klein-Abo: 1 kleiner Teddy pro volles Jahr. + * - Mittel-Abo: 1 großer Teddy pro volles Jahr. + * - Groß-Abo: 1 kleiner Teddy alle 6 Monate UND zusätzlich 1 großer Teddy pro volles Jahr + * (nach 6 Monaten: 1 klein · nach 12: 2 klein + 1 groß · nach 18: 3 klein · nach 24: 4 klein + 2 groß). */ +export function teddyMeilensteine(stufe: AboStufe, monate: number): { klein: number; gross: number } { + if (monate <= 0) return { klein: 0, gross: 0 }; + if (stufe === "klein") return { klein: Math.floor(monate / 12), gross: 0 }; + if (stufe === "mittel") return { klein: 0, gross: Math.floor(monate / 12) }; + return { klein: Math.floor(monate / 6), gross: Math.floor(monate / 12) }; +} + +/** Wie viele Monate fehlen noch bis zum jeweils nächsten Meilenstein (für die + * Fortschrittsanzeige im Kundenkonto), null wenn diese Stufe die Prämienart gar nicht kennt. */ +export function naechsteMeilensteine(stufe: AboStufe, monate: number): { klein: number | null; gross: number | null } { + const kleinIntervall = stufe === "gross" ? 6 : stufe === "klein" ? 12 : null; + const grossIntervall = stufe === "klein" ? null : 12; + return { + klein: kleinIntervall ? kleinIntervall - (monate % kleinIntervall) : null, + gross: grossIntervall ? grossIntervall - (monate % grossIntervall) : null, + }; +} + +/** Ein Abo ist aktiv, solange der aktuell bezahlte Abrechnungszeitraum noch nicht abgelaufen + * ist — reiner Datumsvergleich zur echten, aktuellen Uhrzeit (läuft also automatisch ab, ganz + * ohne Cronjob/Backend), genau wie befristete Produkt-Rabatte (siehe data/rabatt.ts). Gilt + * bewusst UNABHÄNGIG davon, ob das Abo schon gekündigt wurde — gekündigte Abos behalten ihre + * Vorteile bis zum Ende der bezahlten Laufzeit. */ +export function aboAktuellAktiv(bezahltBisDatum: string | null): boolean { + if (!bezahltBisDatum) return false; + return new Date() <= new Date(`${bezahltBisDatum}T23:59:59`); +} + +/** Heutiges Datum als lokales Kalenderdatum (NICHT über toISOString(), das auf UTC umrechnet und + * in Zeitzonen östlich von UTC nachts kurzzeitig das falsche — gestrige — Datum liefern würde). */ +export function heutigesDatumISO(): string { + const heute = new Date(); + const jahr = heute.getFullYear(); + const monat = String(heute.getMonth() + 1).padStart(2, "0"); + const tag = String(heute.getDate()).padStart(2, "0"); + return `${jahr}-${monat}-${tag}`; +} + +/** Rechnet rein kalendarisch in UTC (bewusst NICHT über lokale Zeit + toISOString() gemischt — + * das würde je nach Zeitzone des Geräts bei jedem Aufruf einen Tag "verlieren" und sich über + * viele Monate hinweg zu einem spürbaren Datumsfehler aufsummieren; mit reinem UTC-Rechnen ist + * das Ergebnis unabhängig von der Zeitzone des Kunden immer exakt). */ +export function addMonateISO(datumISO: string, monate: number): string { + const [jahr, monat, tag] = datumISO.split("-").map(Number); + return new Date(Date.UTC(jahr, monat - 1 + monate, tag)).toISOString().slice(0, 10); +} diff --git a/src/i18n/ui.ts b/src/i18n/ui.ts index 3650954..ed0d281 100644 --- a/src/i18n/ui.ts +++ b/src/i18n/ui.ts @@ -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", diff --git a/src/pages/ch/checkout/index.astro b/src/pages/ch/checkout/index.astro index d57fcc9..856a614 100644 --- a/src/pages/ch/checkout/index.astro +++ b/src/pages/ch/checkout/index.astro @@ -173,20 +173,19 @@ const t = useTranslations(lang); -