Teddy-Praemien: zeigt jetzt WELCHE Teddys schon erhalten wurden, nicht nur wie viele
Statt "Bereits erhalten: 1x kleiner Teddy, 1x grosser Teddy" steht im Abo-Bereich jetzt eine Liste mit dem tatsaechlichen Teddy-Namen, dem Einloesedatum und einem Link zum Produkt — damit Kundinnen sehen, welchen Teddy sie schon besitzen, und keinen doppelt waehlen. Dafuer haelt das Konto beim Einloesen fest, WELCHER Teddy vergeben wurde (neues Feld abo.erhalteneTeddys mit Slug, Name und Datum). Bewusst als Verlauf gespeichert statt aus der aktuellen Abo-Einstellung abgeleitet: VanVan kann die hinterlegten Teddy-Produkte jederzeit im Adminbereich austauschen — eine nachtraeglich berechnete Liste wuerde dann einen Teddy anzeigen, den die Kundin nie bekommen hat. Die Anzeige nimmt den aktuellen, uebersetzten Produktnamen und faellt auf den gespeicherten Namen zurueck, falls das Produkt spaeter umbenannt oder entfernt wird. Nebenbei behoben: Beim Einloesen wanderte der Teddy in den Warenkorb, BEVOR geprueft wurde, ob ueberhaupt eine Praemie offen ist. Bei einem zweiten Klick landete er dadurch erneut gratis im Warenkorb, ohne im Konto gezaehlt zu werden. Jetzt wird zuerst geprueft. Ausserdem sichert getAccount() nun auch die Listenfelder gegen kaputte Speicherwerte ab. Getestet (Browser): Leerzustand, Einloesen beider Arten, Speicherung im Konto, zweiter Klick ohne offene Praemie, Altkonto ohne Namensliste (ehrlicher Ersatztext) und entferntes Produkt (gespeicherter Name greift). Beschriftungen in allen 4 Sprachen geprueft. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
+28
-4
@@ -269,7 +269,13 @@ export const ui = {
|
|||||||
aboMonateSuffix: "Monate",
|
aboMonateSuffix: "Monate",
|
||||||
aboFortschrittKleinTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten kleinen Teddy",
|
aboFortschrittKleinTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten kleinen Teddy",
|
||||||
aboFortschrittGrossTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten großen Teddy",
|
aboFortschrittGrossTemplate: "🧸 Noch {n} Monat(e) bis zum nächsten großen Teddy",
|
||||||
aboPraemienErhaltenTemplate: "Bereits erhalten: {klein}× kleiner Teddy, {gross}× großer Teddy",
|
aboPraemienErhaltenTitel: "Diese Teddys hast du schon bekommen:",
|
||||||
|
aboPraemienKeine: "Du hast noch keinen Teddy eingelöst.",
|
||||||
|
aboPraemienAmTemplate: "erhalten am {datum}",
|
||||||
|
aboPraemienAnsehen: "ansehen",
|
||||||
|
aboPraemienUnbekannt: "Teddy aus einer früheren Einlösung",
|
||||||
|
aboTeddyArtKlein: "Kleiner Teddy",
|
||||||
|
aboTeddyArtGross: "Großer Teddy",
|
||||||
aboPraemienVerfuegbarTitle: "🎉 Verfügbare Prämien",
|
aboPraemienVerfuegbarTitle: "🎉 Verfügbare Prämien",
|
||||||
aboKeinePraemienVerfuegbar: "Aktuell keine Prämie zum Einlösen verfügbar.",
|
aboKeinePraemienVerfuegbar: "Aktuell keine Prämie zum Einlösen verfügbar.",
|
||||||
aboEinloesenBtn: "Jetzt einlösen",
|
aboEinloesenBtn: "Jetzt einlösen",
|
||||||
@@ -580,7 +586,13 @@ export const ui = {
|
|||||||
aboMonateSuffix: "months",
|
aboMonateSuffix: "months",
|
||||||
aboFortschrittKleinTemplate: "🧸 {n} more month(s) until your next small teddy",
|
aboFortschrittKleinTemplate: "🧸 {n} more month(s) until your next small teddy",
|
||||||
aboFortschrittGrossTemplate: "🧸 {n} more month(s) until your next large teddy",
|
aboFortschrittGrossTemplate: "🧸 {n} more month(s) until your next large teddy",
|
||||||
aboPraemienErhaltenTemplate: "Already received: {klein}× small teddy, {gross}× large teddy",
|
aboPraemienErhaltenTitel: "Teddies you've already received:",
|
||||||
|
aboPraemienKeine: "You haven't redeemed a teddy yet.",
|
||||||
|
aboPraemienAmTemplate: "received on {datum}",
|
||||||
|
aboPraemienAnsehen: "view",
|
||||||
|
aboPraemienUnbekannt: "Teddy from an earlier redemption",
|
||||||
|
aboTeddyArtKlein: "Small teddy",
|
||||||
|
aboTeddyArtGross: "Large teddy",
|
||||||
aboPraemienVerfuegbarTitle: "🎉 Available rewards",
|
aboPraemienVerfuegbarTitle: "🎉 Available rewards",
|
||||||
aboKeinePraemienVerfuegbar: "No reward available to redeem right now.",
|
aboKeinePraemienVerfuegbar: "No reward available to redeem right now.",
|
||||||
aboEinloesenBtn: "Redeem now",
|
aboEinloesenBtn: "Redeem now",
|
||||||
@@ -891,7 +903,13 @@ export const ui = {
|
|||||||
aboMonateSuffix: "Monet",
|
aboMonateSuffix: "Monet",
|
||||||
aboFortschrittKleinTemplate: "🧸 No {n} Monet bis zum nächschte chliine Teddy",
|
aboFortschrittKleinTemplate: "🧸 No {n} Monet bis zum nächschte chliine Teddy",
|
||||||
aboFortschrittGrossTemplate: "🧸 No {n} Monet bis zum nächschte grosse Teddy",
|
aboFortschrittGrossTemplate: "🧸 No {n} Monet bis zum nächschte grosse Teddy",
|
||||||
aboPraemienErhaltenTemplate: "Scho erhalte: {klein}× chliine Teddy, {gross}× grosse Teddy",
|
aboPraemienErhaltenTitel: "Die Teddys hesch scho ueberchoo:",
|
||||||
|
aboPraemienKeine: "Du hesch no kein Teddy iiglöst.",
|
||||||
|
aboPraemienAmTemplate: "erhalte am {datum}",
|
||||||
|
aboPraemienAnsehen: "aaluege",
|
||||||
|
aboPraemienUnbekannt: "Teddy us ere frueehere Iiloesig",
|
||||||
|
aboTeddyArtKlein: "Chliine Teddy",
|
||||||
|
aboTeddyArtGross: "Grosse Teddy",
|
||||||
aboPraemienVerfuegbarTitle: "🎉 Verfüegbari Prämie",
|
aboPraemienVerfuegbarTitle: "🎉 Verfüegbari Prämie",
|
||||||
aboKeinePraemienVerfuegbar: "Momentan kei Prämie zum Ilöse verfüegbar.",
|
aboKeinePraemienVerfuegbar: "Momentan kei Prämie zum Ilöse verfüegbar.",
|
||||||
aboEinloesenBtn: "Jetzt ilöse",
|
aboEinloesenBtn: "Jetzt ilöse",
|
||||||
@@ -1202,7 +1220,13 @@ export const ui = {
|
|||||||
aboMonateSuffix: "mois",
|
aboMonateSuffix: "mois",
|
||||||
aboFortschrittKleinTemplate: "🧸 Encore {n} mois avant votre prochain petit ours",
|
aboFortschrittKleinTemplate: "🧸 Encore {n} mois avant votre prochain petit ours",
|
||||||
aboFortschrittGrossTemplate: "🧸 Encore {n} mois avant votre prochain grand ours",
|
aboFortschrittGrossTemplate: "🧸 Encore {n} mois avant votre prochain grand ours",
|
||||||
aboPraemienErhaltenTemplate: "Déjà reçus : {klein}× petit ours, {gross}× grand ours",
|
aboPraemienErhaltenTitel: "Les ours que vous avez déjà reçus :",
|
||||||
|
aboPraemienKeine: "Vous n'avez pas encore utilisé de récompense ours.",
|
||||||
|
aboPraemienAmTemplate: "reçu le {datum}",
|
||||||
|
aboPraemienAnsehen: "voir",
|
||||||
|
aboPraemienUnbekannt: "Ours d'une récompense antérieure",
|
||||||
|
aboTeddyArtKlein: "Petit ours",
|
||||||
|
aboTeddyArtGross: "Grand ours",
|
||||||
aboPraemienVerfuegbarTitle: "🎉 Récompenses disponibles",
|
aboPraemienVerfuegbarTitle: "🎉 Récompenses disponibles",
|
||||||
aboKeinePraemienVerfuegbar: "Aucune récompense à échanger pour le moment.",
|
aboKeinePraemienVerfuegbar: "Aucune récompense à échanger pour le moment.",
|
||||||
aboEinloesenBtn: "Échanger maintenant",
|
aboEinloesenBtn: "Échanger maintenant",
|
||||||
|
|||||||
@@ -560,9 +560,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="abo-erhalten-chip">
|
{/* Zeigt nicht nur WIE VIELE Teddys schon eingeloest wurden, sondern WELCHE genau —
|
||||||
<span aria-hidden="true">🧸</span>
|
mit Name und Datum. So sieht die Kundin auf einen Blick, welchen Teddy sie schon
|
||||||
<span id="abo-praemien-erhalten"></span>
|
besitzt, und waehlt keinen doppelt. Wird per JS aus abo.erhalteneTeddys befuellt. */}
|
||||||
|
<div class="abo-erhalten-box" id="abo-erhalten-box">
|
||||||
|
<p class="abo-erhalten-titel"><span aria-hidden="true">🧸</span> <span id="abo-erhalten-titel-text"></span></p>
|
||||||
|
<ul class="abo-erhalten-liste" id="abo-erhalten-liste"></ul>
|
||||||
</div>
|
</div>
|
||||||
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
||||||
|
|
||||||
@@ -607,7 +610,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</section>
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ loginPath: "/ch/konto/", fallbackName: "Kundin", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
<script define:vars={{ loginPath: "/ch/konto/", fallbackName: "Kundin", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTitel: t.accountDash.aboPraemienErhaltenTitel, praemienKeine: t.accountDash.aboPraemienKeine, praemienAmTemplate: t.accountDash.aboPraemienAmTemplate, praemienAnsehen: t.accountDash.aboPraemienAnsehen, praemienUnbekannt: t.accountDash.aboPraemienUnbekannt, teddyArtKlein: t.accountDash.aboTeddyArtKlein, teddyArtGross: t.accountDash.aboTeddyArtGross, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
||||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
@@ -790,6 +793,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
||||||
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
||||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||||
|
import { getProduct } from "../../../data/products";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
import { aboStufenKonfig } from "../../../data/abo";
|
import { aboStufenKonfig } from "../../../data/abo";
|
||||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
||||||
@@ -1010,9 +1014,57 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
} else fortschrittGrossWrap.style.display = "none";
|
} else fortschrittGrossWrap.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
setText("abo-praemien-erhalten", aboLabels.praemienErhaltenTemplate
|
// Bereits erhaltene Teddys namentlich auflisten (neueste zuerst).
|
||||||
.replace("{klein}", String(a.abo.kleinTeddyEingeloest))
|
const erhaltenBox = document.getElementById("abo-erhalten-box");
|
||||||
.replace("{gross}", String(a.abo.grossTeddyEingeloest)));
|
const erhaltenListe = document.getElementById("abo-erhalten-liste");
|
||||||
|
const gesamtEingeloest = a.abo.kleinTeddyEingeloest + a.abo.grossTeddyEingeloest;
|
||||||
|
setText("abo-erhalten-titel-text", gesamtEingeloest === 0 ? aboLabels.praemienKeine : aboLabels.praemienErhaltenTitel);
|
||||||
|
if (erhaltenBox) erhaltenBox.classList.toggle("is-leer", gesamtEingeloest === 0);
|
||||||
|
if (erhaltenListe) {
|
||||||
|
erhaltenListe.innerHTML = "";
|
||||||
|
const eintraege = [...a.abo.erhalteneTeddys].reverse();
|
||||||
|
eintraege.forEach((teddy) => {
|
||||||
|
// Aktueller, uebersetzter Produktname bevorzugt; der beim Einloesen gespeicherte Name
|
||||||
|
// ist nur die Rueckfallebene, falls das Produkt inzwischen umbenannt/entfernt wurde.
|
||||||
|
const produkt = getProduct(teddy.slug);
|
||||||
|
const name = produkt?.name[aboLang] ?? teddy.name;
|
||||||
|
const artLabel = teddy.art === "klein" ? aboLabels.teddyArtKlein : aboLabels.teddyArtGross;
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag";
|
||||||
|
const bez = document.createElement("span");
|
||||||
|
bez.className = "abo-erhalten-name";
|
||||||
|
// Heisst das Produkt selbst schon "Kleiner Teddy", nicht "Kleiner Teddy: Kleiner Teddy"
|
||||||
|
// schreiben. Erst wenn VanVan echte Teddy-Namen hinterlegt, steht beides nebeneinander.
|
||||||
|
const nameGleichArt = name.trim().toLowerCase() === artLabel.trim().toLowerCase();
|
||||||
|
bez.textContent = name && !nameGleichArt ? `${artLabel}: ${name}` : (name || artLabel);
|
||||||
|
li.appendChild(bez);
|
||||||
|
if (teddy.datum) {
|
||||||
|
const dat = document.createElement("span");
|
||||||
|
dat.className = "abo-erhalten-datum";
|
||||||
|
const [j, m, tg] = teddy.datum.split("-");
|
||||||
|
dat.textContent = aboLabels.praemienAmTemplate.replace("{datum}", `${tg}.${m}.${j}`);
|
||||||
|
li.appendChild(dat);
|
||||||
|
}
|
||||||
|
// Direkt zum Produkt springen, damit man sofort nachsehen kann, welcher Teddy das war.
|
||||||
|
if (produkt) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.className = "abo-erhalten-link";
|
||||||
|
link.href = `${aboLang === "de" ? "" : "/" + aboLang}/produkt/${teddy.slug}/`;
|
||||||
|
link.textContent = aboLabels.praemienAnsehen;
|
||||||
|
li.appendChild(link);
|
||||||
|
}
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
});
|
||||||
|
// Aeltere Konten kennen die namentliche Liste noch nicht: fehlende Eintraege werden
|
||||||
|
// ehrlich ohne Namen ergaenzt, statt sie einfach zu verschweigen.
|
||||||
|
const fehlend = gesamtEingeloest - eintraege.length;
|
||||||
|
for (let i = 0; i < fehlend; i++) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag abo-erhalten-eintrag-unbekannt";
|
||||||
|
li.textContent = aboLabels.praemienUnbekannt;
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
||||||
if (gekuendigtHinweis) {
|
if (gekuendigtHinweis) {
|
||||||
@@ -1080,8 +1132,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
||||||
if (!art) return;
|
if (!art) return;
|
||||||
teddyEinloesen(art);
|
// Erst pruefen, ob ueberhaupt eine Praemie offen ist — sonst landete der Teddy im
|
||||||
fuegeTeddyGratisHinzu(art);
|
// Warenkorb, ohne dass er im Konto als eingeloest gezaehlt wurde (waere gratis doppelt).
|
||||||
|
const offen = aboStatus(getAccount());
|
||||||
|
if ((art === "klein" ? offen.kleinVerfuegbar : offen.grossVerfuegbar) <= 0) return;
|
||||||
|
const teddy = fuegeTeddyGratisHinzu(art);
|
||||||
|
teddyEinloesen(art, teddy);
|
||||||
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
||||||
if (hinweis) {
|
if (hinweis) {
|
||||||
hinweis.style.display = "block";
|
hinweis.style.display = "block";
|
||||||
|
|||||||
@@ -560,9 +560,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="abo-erhalten-chip">
|
{/* Zeigt nicht nur WIE VIELE Teddys schon eingeloest wurden, sondern WELCHE genau —
|
||||||
<span aria-hidden="true">🧸</span>
|
mit Name und Datum. So sieht die Kundin auf einen Blick, welchen Teddy sie schon
|
||||||
<span id="abo-praemien-erhalten"></span>
|
besitzt, und waehlt keinen doppelt. Wird per JS aus abo.erhalteneTeddys befuellt. */}
|
||||||
|
<div class="abo-erhalten-box" id="abo-erhalten-box">
|
||||||
|
<p class="abo-erhalten-titel"><span aria-hidden="true">🧸</span> <span id="abo-erhalten-titel-text"></span></p>
|
||||||
|
<ul class="abo-erhalten-liste" id="abo-erhalten-liste"></ul>
|
||||||
</div>
|
</div>
|
||||||
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
||||||
|
|
||||||
@@ -607,7 +610,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</section>
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ loginPath: "/en/konto/", fallbackName: "Customer", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
<script define:vars={{ loginPath: "/en/konto/", fallbackName: "Customer", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTitel: t.accountDash.aboPraemienErhaltenTitel, praemienKeine: t.accountDash.aboPraemienKeine, praemienAmTemplate: t.accountDash.aboPraemienAmTemplate, praemienAnsehen: t.accountDash.aboPraemienAnsehen, praemienUnbekannt: t.accountDash.aboPraemienUnbekannt, teddyArtKlein: t.accountDash.aboTeddyArtKlein, teddyArtGross: t.accountDash.aboTeddyArtGross, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
||||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
@@ -790,6 +793,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
||||||
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
||||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||||
|
import { getProduct } from "../../../data/products";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
import { aboStufenKonfig } from "../../../data/abo";
|
import { aboStufenKonfig } from "../../../data/abo";
|
||||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
||||||
@@ -1010,9 +1014,57 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
} else fortschrittGrossWrap.style.display = "none";
|
} else fortschrittGrossWrap.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
setText("abo-praemien-erhalten", aboLabels.praemienErhaltenTemplate
|
// Bereits erhaltene Teddys namentlich auflisten (neueste zuerst).
|
||||||
.replace("{klein}", String(a.abo.kleinTeddyEingeloest))
|
const erhaltenBox = document.getElementById("abo-erhalten-box");
|
||||||
.replace("{gross}", String(a.abo.grossTeddyEingeloest)));
|
const erhaltenListe = document.getElementById("abo-erhalten-liste");
|
||||||
|
const gesamtEingeloest = a.abo.kleinTeddyEingeloest + a.abo.grossTeddyEingeloest;
|
||||||
|
setText("abo-erhalten-titel-text", gesamtEingeloest === 0 ? aboLabels.praemienKeine : aboLabels.praemienErhaltenTitel);
|
||||||
|
if (erhaltenBox) erhaltenBox.classList.toggle("is-leer", gesamtEingeloest === 0);
|
||||||
|
if (erhaltenListe) {
|
||||||
|
erhaltenListe.innerHTML = "";
|
||||||
|
const eintraege = [...a.abo.erhalteneTeddys].reverse();
|
||||||
|
eintraege.forEach((teddy) => {
|
||||||
|
// Aktueller, uebersetzter Produktname bevorzugt; der beim Einloesen gespeicherte Name
|
||||||
|
// ist nur die Rueckfallebene, falls das Produkt inzwischen umbenannt/entfernt wurde.
|
||||||
|
const produkt = getProduct(teddy.slug);
|
||||||
|
const name = produkt?.name[aboLang] ?? teddy.name;
|
||||||
|
const artLabel = teddy.art === "klein" ? aboLabels.teddyArtKlein : aboLabels.teddyArtGross;
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag";
|
||||||
|
const bez = document.createElement("span");
|
||||||
|
bez.className = "abo-erhalten-name";
|
||||||
|
// Heisst das Produkt selbst schon "Kleiner Teddy", nicht "Kleiner Teddy: Kleiner Teddy"
|
||||||
|
// schreiben. Erst wenn VanVan echte Teddy-Namen hinterlegt, steht beides nebeneinander.
|
||||||
|
const nameGleichArt = name.trim().toLowerCase() === artLabel.trim().toLowerCase();
|
||||||
|
bez.textContent = name && !nameGleichArt ? `${artLabel}: ${name}` : (name || artLabel);
|
||||||
|
li.appendChild(bez);
|
||||||
|
if (teddy.datum) {
|
||||||
|
const dat = document.createElement("span");
|
||||||
|
dat.className = "abo-erhalten-datum";
|
||||||
|
const [j, m, tg] = teddy.datum.split("-");
|
||||||
|
dat.textContent = aboLabels.praemienAmTemplate.replace("{datum}", `${tg}.${m}.${j}`);
|
||||||
|
li.appendChild(dat);
|
||||||
|
}
|
||||||
|
// Direkt zum Produkt springen, damit man sofort nachsehen kann, welcher Teddy das war.
|
||||||
|
if (produkt) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.className = "abo-erhalten-link";
|
||||||
|
link.href = `${aboLang === "de" ? "" : "/" + aboLang}/produkt/${teddy.slug}/`;
|
||||||
|
link.textContent = aboLabels.praemienAnsehen;
|
||||||
|
li.appendChild(link);
|
||||||
|
}
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
});
|
||||||
|
// Aeltere Konten kennen die namentliche Liste noch nicht: fehlende Eintraege werden
|
||||||
|
// ehrlich ohne Namen ergaenzt, statt sie einfach zu verschweigen.
|
||||||
|
const fehlend = gesamtEingeloest - eintraege.length;
|
||||||
|
for (let i = 0; i < fehlend; i++) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag abo-erhalten-eintrag-unbekannt";
|
||||||
|
li.textContent = aboLabels.praemienUnbekannt;
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
||||||
if (gekuendigtHinweis) {
|
if (gekuendigtHinweis) {
|
||||||
@@ -1080,8 +1132,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
||||||
if (!art) return;
|
if (!art) return;
|
||||||
teddyEinloesen(art);
|
// Erst pruefen, ob ueberhaupt eine Praemie offen ist — sonst landete der Teddy im
|
||||||
fuegeTeddyGratisHinzu(art);
|
// Warenkorb, ohne dass er im Konto als eingeloest gezaehlt wurde (waere gratis doppelt).
|
||||||
|
const offen = aboStatus(getAccount());
|
||||||
|
if ((art === "klein" ? offen.kleinVerfuegbar : offen.grossVerfuegbar) <= 0) return;
|
||||||
|
const teddy = fuegeTeddyGratisHinzu(art);
|
||||||
|
teddyEinloesen(art, teddy);
|
||||||
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
||||||
if (hinweis) {
|
if (hinweis) {
|
||||||
hinweis.style.display = "block";
|
hinweis.style.display = "block";
|
||||||
|
|||||||
@@ -560,9 +560,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="abo-erhalten-chip">
|
{/* Zeigt nicht nur WIE VIELE Teddys schon eingeloest wurden, sondern WELCHE genau —
|
||||||
<span aria-hidden="true">🧸</span>
|
mit Name und Datum. So sieht die Kundin auf einen Blick, welchen Teddy sie schon
|
||||||
<span id="abo-praemien-erhalten"></span>
|
besitzt, und waehlt keinen doppelt. Wird per JS aus abo.erhalteneTeddys befuellt. */}
|
||||||
|
<div class="abo-erhalten-box" id="abo-erhalten-box">
|
||||||
|
<p class="abo-erhalten-titel"><span aria-hidden="true">🧸</span> <span id="abo-erhalten-titel-text"></span></p>
|
||||||
|
<ul class="abo-erhalten-liste" id="abo-erhalten-liste"></ul>
|
||||||
</div>
|
</div>
|
||||||
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
||||||
|
|
||||||
@@ -607,7 +610,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</section>
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ loginPath: "/fr/konto/", fallbackName: "Cliente", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
<script define:vars={{ loginPath: "/fr/konto/", fallbackName: "Cliente", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTitel: t.accountDash.aboPraemienErhaltenTitel, praemienKeine: t.accountDash.aboPraemienKeine, praemienAmTemplate: t.accountDash.aboPraemienAmTemplate, praemienAnsehen: t.accountDash.aboPraemienAnsehen, praemienUnbekannt: t.accountDash.aboPraemienUnbekannt, teddyArtKlein: t.accountDash.aboTeddyArtKlein, teddyArtGross: t.accountDash.aboTeddyArtGross, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
||||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
@@ -790,6 +793,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../../scripts/account";
|
||||||
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
import { rechnungAlsPdfHerunterladen } from "../../../scripts/invoice-pdf";
|
||||||
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
import { fuegeTeddyGratisHinzu } from "../../../scripts/cart";
|
||||||
|
import { getProduct } from "../../../data/products";
|
||||||
import { formatPrice } from "../../../i18n/format";
|
import { formatPrice } from "../../../i18n/format";
|
||||||
import { aboStufenKonfig } from "../../../data/abo";
|
import { aboStufenKonfig } from "../../../data/abo";
|
||||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
||||||
@@ -1010,9 +1014,57 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
} else fortschrittGrossWrap.style.display = "none";
|
} else fortschrittGrossWrap.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
setText("abo-praemien-erhalten", aboLabels.praemienErhaltenTemplate
|
// Bereits erhaltene Teddys namentlich auflisten (neueste zuerst).
|
||||||
.replace("{klein}", String(a.abo.kleinTeddyEingeloest))
|
const erhaltenBox = document.getElementById("abo-erhalten-box");
|
||||||
.replace("{gross}", String(a.abo.grossTeddyEingeloest)));
|
const erhaltenListe = document.getElementById("abo-erhalten-liste");
|
||||||
|
const gesamtEingeloest = a.abo.kleinTeddyEingeloest + a.abo.grossTeddyEingeloest;
|
||||||
|
setText("abo-erhalten-titel-text", gesamtEingeloest === 0 ? aboLabels.praemienKeine : aboLabels.praemienErhaltenTitel);
|
||||||
|
if (erhaltenBox) erhaltenBox.classList.toggle("is-leer", gesamtEingeloest === 0);
|
||||||
|
if (erhaltenListe) {
|
||||||
|
erhaltenListe.innerHTML = "";
|
||||||
|
const eintraege = [...a.abo.erhalteneTeddys].reverse();
|
||||||
|
eintraege.forEach((teddy) => {
|
||||||
|
// Aktueller, uebersetzter Produktname bevorzugt; der beim Einloesen gespeicherte Name
|
||||||
|
// ist nur die Rueckfallebene, falls das Produkt inzwischen umbenannt/entfernt wurde.
|
||||||
|
const produkt = getProduct(teddy.slug);
|
||||||
|
const name = produkt?.name[aboLang] ?? teddy.name;
|
||||||
|
const artLabel = teddy.art === "klein" ? aboLabels.teddyArtKlein : aboLabels.teddyArtGross;
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag";
|
||||||
|
const bez = document.createElement("span");
|
||||||
|
bez.className = "abo-erhalten-name";
|
||||||
|
// Heisst das Produkt selbst schon "Kleiner Teddy", nicht "Kleiner Teddy: Kleiner Teddy"
|
||||||
|
// schreiben. Erst wenn VanVan echte Teddy-Namen hinterlegt, steht beides nebeneinander.
|
||||||
|
const nameGleichArt = name.trim().toLowerCase() === artLabel.trim().toLowerCase();
|
||||||
|
bez.textContent = name && !nameGleichArt ? `${artLabel}: ${name}` : (name || artLabel);
|
||||||
|
li.appendChild(bez);
|
||||||
|
if (teddy.datum) {
|
||||||
|
const dat = document.createElement("span");
|
||||||
|
dat.className = "abo-erhalten-datum";
|
||||||
|
const [j, m, tg] = teddy.datum.split("-");
|
||||||
|
dat.textContent = aboLabels.praemienAmTemplate.replace("{datum}", `${tg}.${m}.${j}`);
|
||||||
|
li.appendChild(dat);
|
||||||
|
}
|
||||||
|
// Direkt zum Produkt springen, damit man sofort nachsehen kann, welcher Teddy das war.
|
||||||
|
if (produkt) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.className = "abo-erhalten-link";
|
||||||
|
link.href = `${aboLang === "de" ? "" : "/" + aboLang}/produkt/${teddy.slug}/`;
|
||||||
|
link.textContent = aboLabels.praemienAnsehen;
|
||||||
|
li.appendChild(link);
|
||||||
|
}
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
});
|
||||||
|
// Aeltere Konten kennen die namentliche Liste noch nicht: fehlende Eintraege werden
|
||||||
|
// ehrlich ohne Namen ergaenzt, statt sie einfach zu verschweigen.
|
||||||
|
const fehlend = gesamtEingeloest - eintraege.length;
|
||||||
|
for (let i = 0; i < fehlend; i++) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag abo-erhalten-eintrag-unbekannt";
|
||||||
|
li.textContent = aboLabels.praemienUnbekannt;
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
||||||
if (gekuendigtHinweis) {
|
if (gekuendigtHinweis) {
|
||||||
@@ -1080,8 +1132,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
||||||
if (!art) return;
|
if (!art) return;
|
||||||
teddyEinloesen(art);
|
// Erst pruefen, ob ueberhaupt eine Praemie offen ist — sonst landete der Teddy im
|
||||||
fuegeTeddyGratisHinzu(art);
|
// Warenkorb, ohne dass er im Konto als eingeloest gezaehlt wurde (waere gratis doppelt).
|
||||||
|
const offen = aboStatus(getAccount());
|
||||||
|
if ((art === "klein" ? offen.kleinVerfuegbar : offen.grossVerfuegbar) <= 0) return;
|
||||||
|
const teddy = fuegeTeddyGratisHinzu(art);
|
||||||
|
teddyEinloesen(art, teddy);
|
||||||
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
||||||
if (hinweis) {
|
if (hinweis) {
|
||||||
hinweis.style.display = "block";
|
hinweis.style.display = "block";
|
||||||
|
|||||||
@@ -569,9 +569,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="abo-erhalten-chip">
|
{/* Zeigt nicht nur WIE VIELE Teddys schon eingeloest wurden, sondern WELCHE genau —
|
||||||
<span aria-hidden="true">🧸</span>
|
mit Name und Datum. So sieht die Kundin auf einen Blick, welchen Teddy sie schon
|
||||||
<span id="abo-praemien-erhalten"></span>
|
besitzt, und waehlt keinen doppelt. Wird per JS aus abo.erhalteneTeddys befuellt. */}
|
||||||
|
<div class="abo-erhalten-box" id="abo-erhalten-box">
|
||||||
|
<p class="abo-erhalten-titel"><span aria-hidden="true">🧸</span> <span id="abo-erhalten-titel-text"></span></p>
|
||||||
|
<ul class="abo-erhalten-liste" id="abo-erhalten-liste"></ul>
|
||||||
</div>
|
</div>
|
||||||
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
<p id="abo-gekuendigt-hinweis" class="small" style="display:none;"></p>
|
||||||
|
|
||||||
@@ -616,7 +619,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
</section>
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
||||||
<script define:vars={{ loginPath: "/konto/", fallbackName: "Kundin", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTemplate: t.accountDash.aboPraemienErhaltenTemplate, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
<script define:vars={{ loginPath: "/konto/", fallbackName: "Kundin", greetingTemplate: t.accountDash.greeting("{name}"), reviewedBadgeText: t.accountDash.reviewedBadge , invoiceLang: lang, invoiceLabels: { title: t.accountDash.invoiceTitle, numberLabel: t.accountDash.invoiceNumberLabel, dateLabel: t.accountDash.invoiceDateLabel, sellerLabel: t.accountDash.invoiceSellerLabel, billToLabel: t.accountDash.invoiceBillToLabel, itemLabel: t.accountDash.invoiceItemLabel, qtyLabel: t.accountDash.invoiceQtyLabel, unitPriceLabel: t.accountDash.invoiceUnitPriceLabel, sumLabel: t.accountDash.invoiceSumLabel, totalLabel: t.accountDash.orderTotal, vatNote: t.common.vatNote }, loyaltyProgressTemplate: t.accountDash.loyaltyProgressTemplate, loyaltyRemainingOne: t.accountDash.loyaltyRemainingOne, loyaltyRemainingOther: t.accountDash.loyaltyRemainingOther, loyaltyUnlockedTitle: t.accountDash.loyaltyUnlockedTitle, loyaltyUnlockedText: t.accountDash.loyaltyUnlockedText, loyaltyStatRedeemedNever: t.accountDash.loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix: t.accountDash.loyaltyStatRedeemedSuffix, loyaltyLastRedeemed: t.accountDash.loyaltyLastRedeemed, aboLang: lang, aboLabels: { nameKlein: t.accountDash.aboNameKlein, nameMittel: t.accountDash.aboNameMittel, nameGross: t.accountDash.aboNameGross, preisSuffix: t.accountDash.aboPreisSuffix, monateSuffix: t.accountDash.aboMonateSuffix, fortschrittKleinTemplate: t.accountDash.aboFortschrittKleinTemplate, fortschrittGrossTemplate: t.accountDash.aboFortschrittGrossTemplate, praemienErhaltenTitel: t.accountDash.aboPraemienErhaltenTitel, praemienKeine: t.accountDash.aboPraemienKeine, praemienAmTemplate: t.accountDash.aboPraemienAmTemplate, praemienAnsehen: t.accountDash.aboPraemienAnsehen, praemienUnbekannt: t.accountDash.aboPraemienUnbekannt, teddyArtKlein: t.accountDash.aboTeddyArtKlein, teddyArtGross: t.accountDash.aboTeddyArtGross, aktivBadge: t.accountDash.aboAktivesLabel, nichtAktivBadge: t.accountDash.aboNichtAktivBadge, gekuendigtHinweisTemplate: t.accountDash.aboGekuendigtHinweisTemplate, kuendigenConfirm: t.accountDash.aboKuendigenConfirm, abschliessenBtn: t.accountDash.aboAbschliessenBtn, wechselnBtn: t.accountDash.aboWechselnBtn, nichtBuchbar: t.accountDash.aboNichtBuchbar, keineBenachrichtigungen: t.accountDash.aboKeineBenachrichtigungen, notifKeyLabels: { abo_abgeschlossen: t.accountDash.notifAboAbgeschlossen, abo_gewechselt: t.accountDash.notifAboGewechselt, abo_zahlung_erfolgreich: t.accountDash.notifAboZahlungErfolgreich, abo_teddy_klein_frei: t.accountDash.notifAboTeddyKleinFrei, abo_teddy_gross_frei: t.accountDash.notifAboTeddyGrossFrei, abo_bald_praemie: t.accountDash.notifAboBaldPraemie, abo_gekuendigt: t.accountDash.notifAboGekuendigt, abo_teddy_klein_eingeloest: t.accountDash.notifAboTeddyKleinEingeloest, abo_teddy_gross_eingeloest: t.accountDash.notifAboTeddyGrossEingeloest } }, deleteAccountConfirm: t.accountDash.deleteAccountConfirm, deleteAccountError: t.accountDash.deleteAccountError }}>
|
||||||
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
window.__accountDashVars = { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError };
|
||||||
</script>
|
</script>
|
||||||
<script>
|
<script>
|
||||||
@@ -803,6 +806,7 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../scripts/account";
|
import { getAccount, isLoggedIn, login, setName, logout, setUsername, setPhoto, hatBestellungBewertet, anzeigeName, treueFortschritt, aboStatus, aboAbschliessen, aboZahlungSimulieren, aboKuendigen, teddyEinloesen } from "../../scripts/account";
|
||||||
import { rechnungAlsPdfHerunterladen } from "../../scripts/invoice-pdf";
|
import { rechnungAlsPdfHerunterladen } from "../../scripts/invoice-pdf";
|
||||||
import { fuegeTeddyGratisHinzu } from "../../scripts/cart";
|
import { fuegeTeddyGratisHinzu } from "../../scripts/cart";
|
||||||
|
import { getProduct } from "../../data/products";
|
||||||
import { formatPrice } from "../../i18n/format";
|
import { formatPrice } from "../../i18n/format";
|
||||||
import { aboStufenKonfig } from "../../data/abo";
|
import { aboStufenKonfig } from "../../data/abo";
|
||||||
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
const { loginPath, fallbackName, greetingTemplate, reviewedBadgeText, invoiceLang, invoiceLabels, loyaltyProgressTemplate, loyaltyRemainingOne, loyaltyRemainingOther, loyaltyUnlockedTitle, loyaltyUnlockedText, loyaltyStatRedeemedNever, loyaltyStatRedeemedSuffix, loyaltyLastRedeemed, aboLang, aboLabels, deleteAccountConfirm, deleteAccountError } = (window as any).__accountDashVars;
|
||||||
@@ -1026,9 +1030,57 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
} else fortschrittGrossWrap.style.display = "none";
|
} else fortschrittGrossWrap.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
setText("abo-praemien-erhalten", aboLabels.praemienErhaltenTemplate
|
// Bereits erhaltene Teddys namentlich auflisten (neueste zuerst).
|
||||||
.replace("{klein}", String(a.abo.kleinTeddyEingeloest))
|
const erhaltenBox = document.getElementById("abo-erhalten-box");
|
||||||
.replace("{gross}", String(a.abo.grossTeddyEingeloest)));
|
const erhaltenListe = document.getElementById("abo-erhalten-liste");
|
||||||
|
const gesamtEingeloest = a.abo.kleinTeddyEingeloest + a.abo.grossTeddyEingeloest;
|
||||||
|
setText("abo-erhalten-titel-text", gesamtEingeloest === 0 ? aboLabels.praemienKeine : aboLabels.praemienErhaltenTitel);
|
||||||
|
if (erhaltenBox) erhaltenBox.classList.toggle("is-leer", gesamtEingeloest === 0);
|
||||||
|
if (erhaltenListe) {
|
||||||
|
erhaltenListe.innerHTML = "";
|
||||||
|
const eintraege = [...a.abo.erhalteneTeddys].reverse();
|
||||||
|
eintraege.forEach((teddy) => {
|
||||||
|
// Aktueller, uebersetzter Produktname bevorzugt; der beim Einloesen gespeicherte Name
|
||||||
|
// ist nur die Rueckfallebene, falls das Produkt inzwischen umbenannt/entfernt wurde.
|
||||||
|
const produkt = getProduct(teddy.slug);
|
||||||
|
const name = produkt?.name[aboLang] ?? teddy.name;
|
||||||
|
const artLabel = teddy.art === "klein" ? aboLabels.teddyArtKlein : aboLabels.teddyArtGross;
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag";
|
||||||
|
const bez = document.createElement("span");
|
||||||
|
bez.className = "abo-erhalten-name";
|
||||||
|
// Heisst das Produkt selbst schon "Kleiner Teddy", nicht "Kleiner Teddy: Kleiner Teddy"
|
||||||
|
// schreiben. Erst wenn VanVan echte Teddy-Namen hinterlegt, steht beides nebeneinander.
|
||||||
|
const nameGleichArt = name.trim().toLowerCase() === artLabel.trim().toLowerCase();
|
||||||
|
bez.textContent = name && !nameGleichArt ? `${artLabel}: ${name}` : (name || artLabel);
|
||||||
|
li.appendChild(bez);
|
||||||
|
if (teddy.datum) {
|
||||||
|
const dat = document.createElement("span");
|
||||||
|
dat.className = "abo-erhalten-datum";
|
||||||
|
const [j, m, tg] = teddy.datum.split("-");
|
||||||
|
dat.textContent = aboLabels.praemienAmTemplate.replace("{datum}", `${tg}.${m}.${j}`);
|
||||||
|
li.appendChild(dat);
|
||||||
|
}
|
||||||
|
// Direkt zum Produkt springen, damit man sofort nachsehen kann, welcher Teddy das war.
|
||||||
|
if (produkt) {
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.className = "abo-erhalten-link";
|
||||||
|
link.href = `${aboLang === "de" ? "" : "/" + aboLang}/produkt/${teddy.slug}/`;
|
||||||
|
link.textContent = aboLabels.praemienAnsehen;
|
||||||
|
li.appendChild(link);
|
||||||
|
}
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
});
|
||||||
|
// Aeltere Konten kennen die namentliche Liste noch nicht: fehlende Eintraege werden
|
||||||
|
// ehrlich ohne Namen ergaenzt, statt sie einfach zu verschweigen.
|
||||||
|
const fehlend = gesamtEingeloest - eintraege.length;
|
||||||
|
for (let i = 0; i < fehlend; i++) {
|
||||||
|
const li = document.createElement("li");
|
||||||
|
li.className = "abo-erhalten-eintrag abo-erhalten-eintrag-unbekannt";
|
||||||
|
li.textContent = aboLabels.praemienUnbekannt;
|
||||||
|
erhaltenListe.appendChild(li);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
const gekuendigtHinweis = document.getElementById("abo-gekuendigt-hinweis");
|
||||||
if (gekuendigtHinweis) {
|
if (gekuendigtHinweis) {
|
||||||
@@ -1096,8 +1148,12 @@ const angekommeneBestellungen = beispielBestellungen.filter((b) => b.status ===
|
|||||||
btn.addEventListener("click", () => {
|
btn.addEventListener("click", () => {
|
||||||
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
const art = btn.dataset.einloesen as "klein" | "gross" | undefined;
|
||||||
if (!art) return;
|
if (!art) return;
|
||||||
teddyEinloesen(art);
|
// Erst pruefen, ob ueberhaupt eine Praemie offen ist — sonst landete der Teddy im
|
||||||
fuegeTeddyGratisHinzu(art);
|
// Warenkorb, ohne dass er im Konto als eingeloest gezaehlt wurde (waere gratis doppelt).
|
||||||
|
const offen = aboStatus(getAccount());
|
||||||
|
if ((art === "klein" ? offen.kleinVerfuegbar : offen.grossVerfuegbar) <= 0) return;
|
||||||
|
const teddy = fuegeTeddyGratisHinzu(art);
|
||||||
|
teddyEinloesen(art, teddy);
|
||||||
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
const hinweis = document.getElementById("abo-eingeloest-hinweis");
|
||||||
if (hinweis) {
|
if (hinweis) {
|
||||||
hinweis.style.display = "block";
|
hinweis.style.display = "block";
|
||||||
|
|||||||
+40
-4
@@ -80,9 +80,28 @@ export interface AboZustand {
|
|||||||
grossTeddyFreigeschaltetGesamt: number;
|
grossTeddyFreigeschaltetGesamt: number;
|
||||||
kleinTeddyEingeloest: number;
|
kleinTeddyEingeloest: number;
|
||||||
grossTeddyEingeloest: number;
|
grossTeddyEingeloest: number;
|
||||||
|
/** WELCHE Teddys bereits eingelöst wurden — nicht nur wie viele. Wird beim Einlösen
|
||||||
|
* festgehalten und nie wieder verändert, damit die Kundin jederzeit nachsehen kann, welchen
|
||||||
|
* Teddy sie schon besitzt, und keinen doppelt auswählt. Bewusst als Verlauf gespeichert und
|
||||||
|
* NICHT aus der aktuellen Abo-Einstellung abgeleitet: VanVan kann die hinterlegten
|
||||||
|
* Teddy-Produkte jederzeit im Adminbereich austauschen — dann wäre eine nachträglich
|
||||||
|
* berechnete Liste falsch und würde einen Teddy anzeigen, den die Kundin nie bekommen hat. */
|
||||||
|
erhalteneTeddys: ErhaltenerTeddy[];
|
||||||
historie: AboWechsel[];
|
historie: AboWechsel[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ErhaltenerTeddy {
|
||||||
|
art: "klein" | "gross";
|
||||||
|
/** Produkt-Slug zum Zeitpunkt der Einlösung. */
|
||||||
|
slug: string;
|
||||||
|
/** Produktname beim Einlösen — dient nur als Rückfallebene, falls das Produkt später
|
||||||
|
* umbenannt oder aus dem Shop genommen wird. Die Anzeige bevorzugt den aktuellen,
|
||||||
|
* übersetzten Produktnamen. */
|
||||||
|
name: string;
|
||||||
|
/** Einlösedatum als ISO-Datum (JJJJ-MM-TT). */
|
||||||
|
datum: string;
|
||||||
|
}
|
||||||
|
|
||||||
/** Feste Schlüssel statt fertigem Text — der eigentliche, sprachabhängige Satz wird erst beim
|
/** Feste Schlüssel statt fertigem Text — der eigentliche, sprachabhängige Satz wird erst beim
|
||||||
* Anzeigen im Kundenkonto übersetzt (siehe aboLabels.notifKeyLabels in konto/angemeldet.astro).
|
* Anzeigen im Kundenkonto übersetzt (siehe aboLabels.notifKeyLabels in konto/angemeldet.astro).
|
||||||
* So bleiben Benachrichtigungen auch auf EN/CH/FR-Seiten korrekt in der jeweiligen Sprache,
|
* So bleiben Benachrichtigungen auch auf EN/CH/FR-Seiten korrekt in der jeweiligen Sprache,
|
||||||
@@ -118,6 +137,7 @@ function leeresAbo(): AboZustand {
|
|||||||
grossTeddyFreigeschaltetGesamt: 0,
|
grossTeddyFreigeschaltetGesamt: 0,
|
||||||
kleinTeddyEingeloest: 0,
|
kleinTeddyEingeloest: 0,
|
||||||
grossTeddyEingeloest: 0,
|
grossTeddyEingeloest: 0,
|
||||||
|
erhalteneTeddys: [],
|
||||||
historie: [],
|
historie: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -158,12 +178,17 @@ export function getAccount(): Account {
|
|||||||
// wirft dann einen Fehler, der das GESAMTE Konto-Dashboard lahmlegt (keine Bestellungen,
|
// wirft dann einen Fehler, der das GESAMTE Konto-Dashboard lahmlegt (keine Bestellungen,
|
||||||
// keine Tabs mehr). Deshalb wird das verschachtelte abo-Objekt ausdrücklich mit den
|
// keine Tabs mehr). Deshalb wird das verschachtelte abo-Objekt ausdrücklich mit den
|
||||||
// Standardwerten aufgefüllt.
|
// Standardwerten aufgefüllt.
|
||||||
|
const abo = { ...basis.abo, ...(gespeichert.abo ?? {}) };
|
||||||
|
// Listen einzeln absichern: ein null/kaputter Wert aus dem Speicher darf hier nie
|
||||||
|
// durchrutschen, sonst wirft der erste .map()/.length-Zugriff und legt die Seite lahm.
|
||||||
|
if (!Array.isArray(abo.erhalteneTeddys)) abo.erhalteneTeddys = [];
|
||||||
|
if (!Array.isArray(abo.historie)) abo.historie = [];
|
||||||
return {
|
return {
|
||||||
...basis,
|
...basis,
|
||||||
...gespeichert,
|
...gespeichert,
|
||||||
abo: { ...basis.abo, ...(gespeichert.abo ?? {}) },
|
abo,
|
||||||
bewerteteBestellungen: gespeichert.bewerteteBestellungen ?? basis.bewerteteBestellungen,
|
bewerteteBestellungen: Array.isArray(gespeichert.bewerteteBestellungen) ? gespeichert.bewerteteBestellungen : basis.bewerteteBestellungen,
|
||||||
benachrichtigungen: gespeichert.benachrichtigungen ?? basis.benachrichtigungen,
|
benachrichtigungen: Array.isArray(gespeichert.benachrichtigungen) ? gespeichert.benachrichtigungen : basis.benachrichtigungen,
|
||||||
};
|
};
|
||||||
} catch {
|
} catch {
|
||||||
return leeresKonto();
|
return leeresKonto();
|
||||||
@@ -436,12 +461,23 @@ export function aboKuendigen(): Account {
|
|||||||
/** Löst eine freigeschaltete, noch nicht eingelöste Teddy-Prämie ein — schreibt sie fest auf
|
/** Löst eine freigeschaltete, noch nicht eingelöste Teddy-Prämie ein — schreibt sie fest auf
|
||||||
* dieses Konto (Lifetime-Zähler steigt), damit sie nicht doppelt eingelöst werden kann. Das
|
* dieses Konto (Lifetime-Zähler steigt), damit sie nicht doppelt eingelöst werden kann. Das
|
||||||
* eigentliche Hinzufügen zum Warenkorb übernimmt die aufrufende Seite (siehe konto/angemeldet). */
|
* eigentliche Hinzufügen zum Warenkorb übernimmt die aufrufende Seite (siehe konto/angemeldet). */
|
||||||
export function teddyEinloesen(art: "klein" | "gross"): Account {
|
/** Löst eine verdiente Teddy-Prämie ein. `produkt` sagt, WELCHER Teddy tatsächlich vergeben
|
||||||
|
* wurde (Slug + Name) — das wird dauerhaft im Konto festgehalten, damit die Kundin später
|
||||||
|
* sieht, welchen Teddy sie schon hat, und keinen doppelt nimmt. Ohne Angabe wird auf den
|
||||||
|
* aktuell hinterlegten Teddy der Abo-Einstellungen zurückgegriffen. */
|
||||||
|
export function teddyEinloesen(art: "klein" | "gross", produkt?: { slug: string; name: string }): Account {
|
||||||
let a = getAccount();
|
let a = getAccount();
|
||||||
const verfuegbar = art === "klein"
|
const verfuegbar = art === "klein"
|
||||||
? a.abo.kleinTeddyFreigeschaltetGesamt - a.abo.kleinTeddyEingeloest
|
? a.abo.kleinTeddyFreigeschaltetGesamt - a.abo.kleinTeddyEingeloest
|
||||||
: a.abo.grossTeddyFreigeschaltetGesamt - a.abo.grossTeddyEingeloest;
|
: a.abo.grossTeddyFreigeschaltetGesamt - a.abo.grossTeddyEingeloest;
|
||||||
if (verfuegbar <= 0) return a;
|
if (verfuegbar <= 0) return a;
|
||||||
|
const eintrag: ErhaltenerTeddy = {
|
||||||
|
art,
|
||||||
|
slug: produkt?.slug ?? (art === "klein" ? aboEinstellungen.kleinTeddySlug : aboEinstellungen.grossTeddySlug),
|
||||||
|
name: produkt?.name ?? "",
|
||||||
|
datum: heutigesDatumISO(),
|
||||||
|
};
|
||||||
|
a = { ...a, abo: { ...a.abo, erhalteneTeddys: [...a.abo.erhalteneTeddys, eintrag] } };
|
||||||
a = art === "klein"
|
a = art === "klein"
|
||||||
? { ...a, abo: { ...a.abo, kleinTeddyEingeloest: a.abo.kleinTeddyEingeloest + 1 } }
|
? { ...a, abo: { ...a.abo, kleinTeddyEingeloest: a.abo.kleinTeddyEingeloest + 1 } }
|
||||||
: { ...a, abo: { ...a.abo, grossTeddyEingeloest: a.abo.grossTeddyEingeloest + 1 } };
|
: { ...a, abo: { ...a.abo, grossTeddyEingeloest: a.abo.grossTeddyEingeloest + 1 } };
|
||||||
|
|||||||
+5
-2
@@ -116,8 +116,10 @@ export function cartTotal(): number {
|
|||||||
|
|
||||||
/** Fügt eine eingelöste Teddy-Prämie (siehe scripts/account.ts → teddyEinloesen()) als echten,
|
/** Fügt eine eingelöste Teddy-Prämie (siehe scripts/account.ts → teddyEinloesen()) als echten,
|
||||||
* aber kostenlosen Artikel zum Warenkorb hinzu — taucht ganz normal in der Liste auf, zählt
|
* aber kostenlosen Artikel zum Warenkorb hinzu — taucht ganz normal in der Liste auf, zählt
|
||||||
* aber mit 0 € in die Summe (siehe zeilenpreisFuer()). */
|
* aber mit 0 € in die Summe (siehe zeilenpreisFuer()).
|
||||||
export function fuegeTeddyGratisHinzu(art: "klein" | "gross") {
|
* Gibt zurück, WELCHER Teddy es war — der Aufrufer hält das im Konto fest, damit die Kundin
|
||||||
|
* später sieht, welchen Teddy sie schon besitzt (siehe ErhaltenerTeddy in account.ts). */
|
||||||
|
export function fuegeTeddyGratisHinzu(art: "klein" | "gross"): { slug: string; name: string } {
|
||||||
const slug = art === "klein" ? aboEinstellungen.kleinTeddySlug : aboEinstellungen.grossTeddySlug;
|
const slug = art === "klein" ? aboEinstellungen.kleinTeddySlug : aboEinstellungen.grossTeddySlug;
|
||||||
const produkt = getProduct(slug);
|
const produkt = getProduct(slug);
|
||||||
const name = produkt?.name.de ?? (art === "klein" ? "Kleiner Teddy" : "Großer Teddy");
|
const name = produkt?.name.de ?? (art === "klein" ? "Kleiner Teddy" : "Großer Teddy");
|
||||||
@@ -129,6 +131,7 @@ export function fuegeTeddyGratisHinzu(art: "klein" | "gross") {
|
|||||||
cart.push({ slug, name, preis: produkt?.preis ?? 0, menge: 1, ausgewaehlt: true, gratis: true });
|
cart.push({ slug, name, preis: produkt?.preis ?? 0, menge: 1, ausgewaehlt: true, gratis: true });
|
||||||
}
|
}
|
||||||
saveCart(cart);
|
saveCart(cart);
|
||||||
|
return { slug, name };
|
||||||
}
|
}
|
||||||
|
|
||||||
const COUPON_KEY = "vandiy_coupon_v1";
|
const COUPON_KEY = "vandiy_coupon_v1";
|
||||||
|
|||||||
+26
-9
@@ -2752,17 +2752,34 @@ a:focus-visible, button:focus-visible {
|
|||||||
.abo-reward-progress-head { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.88rem; font-weight: 600; }
|
.abo-reward-progress-head { display: flex; justify-content: space-between; gap: 1rem; font-size: 0.88rem; font-weight: 600; }
|
||||||
.abo-reward-bar-gross { background: linear-gradient(90deg, #e3b23c, #f6dda0); }
|
.abo-reward-bar-gross { background: linear-gradient(90deg, #e3b23c, #f6dda0); }
|
||||||
|
|
||||||
.abo-erhalten-chip {
|
/* "Diese Teddys hast du schon bekommen" — nennt jeden eingelösten Teddy namentlich mit Datum,
|
||||||
align-self: flex-start;
|
damit die Kundin keinen doppelt auswählt (siehe erhalteneTeddys in scripts/account.ts).
|
||||||
display: inline-flex;
|
Früher war das nur ein schmaler Chip mit reinen Stückzahlen. */
|
||||||
align-items: center;
|
.abo-erhalten-box {
|
||||||
gap: 0.5rem;
|
align-self: stretch;
|
||||||
padding: 0.4rem 0.9rem;
|
padding: 0.7rem 1rem 0.8rem;
|
||||||
border-radius: 999px;
|
border-radius: var(--radius-m);
|
||||||
background: rgba(126, 200, 160, 0.14);
|
background: rgba(126, 200, 160, 0.12);
|
||||||
border: 1px solid rgba(126, 200, 160, 0.35);
|
border: 1px solid rgba(126, 200, 160, 0.32);
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
}
|
}
|
||||||
|
.abo-erhalten-box.is-leer { background: rgba(244, 241, 247, 0.05); border-color: rgba(244, 241, 247, 0.14); }
|
||||||
|
.abo-erhalten-titel { margin: 0; display: flex; align-items: center; gap: 0.5rem; font-weight: 600; }
|
||||||
|
.abo-erhalten-liste { list-style: none; margin: 0.55rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.35rem; }
|
||||||
|
.abo-erhalten-liste:empty { display: none; }
|
||||||
|
.abo-erhalten-eintrag {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 0.15rem 0.5rem;
|
||||||
|
padding-left: 1.4rem;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
.abo-erhalten-eintrag::before { content: "🧸"; position: absolute; left: 0; font-size: 0.9em; }
|
||||||
|
.abo-erhalten-name { font-weight: 600; }
|
||||||
|
.abo-erhalten-datum { color: var(--c-text-muted); font-size: 0.85em; }
|
||||||
|
.abo-erhalten-link { font-size: 0.85em; text-decoration: underline; }
|
||||||
|
.abo-erhalten-eintrag-unbekannt { color: var(--c-text-muted); font-style: italic; }
|
||||||
|
|
||||||
.abo-praemien-block { border-top: 1px dashed rgba(244, 241, 247, 0.14); padding-top: 0.9rem; margin-top: 0.3rem; }
|
.abo-praemien-block { border-top: 1px dashed rgba(244, 241, 247, 0.14); padding-top: 0.9rem; margin-top: 0.3rem; }
|
||||||
.abo-praemien-block h4 { margin: 0 0 0.4rem; }
|
.abo-praemien-block h4 { margin: 0 0 0.4rem; }
|
||||||
|
|||||||
Reference in New Issue
Block a user