Abo-Angebote: Gültigkeit überall sichtbar
Das "Angebot gültig bis" stand bisher nur in der Angebotsliste, auf der Bestellseite und in der Mail. Jetzt auch: - Admin, Formular "Neues Angebot": eigene Zeile "Angebot gültig bis TT.MM.JJJJ" in der Vorschau, in der Bestätigungsabfrage und in der Erfolgsmeldung (bei laufendem Abo nicht, dort gilt das Angebot sofort) - Kundenbereich, Abo-Karte (Seitenleiste bzw. Tab "Wartung"): Zeile "Angebot gültig bis TT.MM.JJJJ" unter dem Preis Das Datum aus dem Datumsfeld wird ohne Date-Umweg formatiert, im Portal nach Berliner Kalender (wie bisher). Nebenbei: "danach 50 €" mit geschütztem Leerzeichen, damit das € auf dem Handy nicht allein umbricht. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d5eb908cb6
commit
c600118374
3 files changed
+13
-5
No files matched your search
+8
-3
@@ -802,6 +802,9 @@ function offerPreviewText(price, months, regular, running) {
|
||||
return `${lead} ${amount}, danach ${eur(regular)} pro Monat`
|
||||
}
|
||||
|
||||
// Datumsfeld "2026-10-27" -> "27.10.2026", ohne Umweg über Date (keine Zeitzonen-Verschiebung)
|
||||
const fmtYmd = (ymd) => String(ymd || '').split('-').reverse().join('.')
|
||||
|
||||
function showOfferMessage(text) {
|
||||
const el = document.getElementById('offer-message')
|
||||
el.textContent = text
|
||||
@@ -899,6 +902,7 @@ function renderOfferPreview(form) {
|
||||
}
|
||||
if (ok) {
|
||||
lines.push(`${offerPreviewText(v.monthlyPriceCents, v.months, regular, running)} · Ersparnis ${euroFromCents((regular - v.monthlyPriceCents) * v.months)}`)
|
||||
if (!running) lines.push(`Angebot gültig bis ${fmtYmd(v.validUntil)}`)
|
||||
}
|
||||
lines.push(running
|
||||
? `Laufendes Abo: gilt ab der nächsten Abbuchung${ctx.subscription.currentPeriodEnd ? ` (${fmtDate(ctx.subscription.currentPeriodEnd)})` : ''} und wird sofort in Stripe angewendet.`
|
||||
@@ -930,8 +934,9 @@ async function saveOffer(form) {
|
||||
errEl.classList.add('hidden')
|
||||
const v = offerFormValues(form)
|
||||
const ctx = offerState.context
|
||||
const text = offerPreviewText(v.monthlyPriceCents, v.months, ctx.regularPriceCents, Boolean(ctx.subscription?.active))
|
||||
if (!window.confirm(`Angebot für ${ctx.customer.name || 'den Kunden'} anlegen?\n\n${text}\n\nDer Coupon wird sofort in Stripe angelegt${ctx.subscription?.active ? ' und auf das laufende Abo angewendet' : ''}.`)) return
|
||||
const running = Boolean(ctx.subscription?.active)
|
||||
const text = offerPreviewText(v.monthlyPriceCents, v.months, ctx.regularPriceCents, running)
|
||||
if (!window.confirm(`Angebot für ${ctx.customer.name || 'den Kunden'} anlegen?\n\n${text}${running ? '' : `\nGültig bis ${fmtYmd(v.validUntil)}`}\n\nDer Coupon wird sofort in Stripe angelegt${running ? ' und auf das laufende Abo angewendet' : ''}.`)) return
|
||||
const save = form.querySelector('#offer-save')
|
||||
save.disabled = true
|
||||
try {
|
||||
@@ -939,7 +944,7 @@ async function saveOffer(form) {
|
||||
const o = result.offer
|
||||
form.classList.add('hidden')
|
||||
showOfferMessage([
|
||||
`Angebot ${o.ref} für ${o.customerName} angelegt: ${o.text}.`,
|
||||
`Angebot ${o.ref} für ${o.customerName} angelegt: ${o.text}${o.scope === 'laufend' ? '' : `, gültig bis ${fmtDate(o.validUntil)}`}.`,
|
||||
`Stripe-Coupon ${o.stripeCouponId} ist eingerichtet${o.status === 'aktiv' ? ' und am laufenden Abo aktiv' : ''}.`,
|
||||
v.notifyCustomer ? (result.mailSent ? 'Die E-Mail an den Kunden ist raus.' : `E-Mail an den Kunden fehlgeschlagen (${result.mailError}).`) : '',
|
||||
].filter(Boolean).join(' '))
|
||||
|
||||
+2
-1
@@ -803,8 +803,9 @@ function renderAboSideOffer(card, data, { ended }) {
|
||||
<h3>Wartungs-Abo</h3>
|
||||
${offer ? '<span class="kb-offer-tag">Ihr persönliches Angebot</span>' : ''}
|
||||
<p class="kb-price">${offer
|
||||
? `<strong>${euroShort(offer.monthlyPriceCents)}</strong><span>${offer.months === 1 ? 'im ersten Monat' : `pro Monat in den ersten ${offer.months} Monaten`}, danach 50 €</span>`
|
||||
? `<strong>${euroShort(offer.monthlyPriceCents)}</strong><span>${offer.months === 1 ? 'im ersten Monat' : `pro Monat in den ersten ${offer.months} Monaten`}, danach 50 €</span>`
|
||||
: '<strong>50 €</strong><span>pro Monat</span>'}</p>
|
||||
${offer?.validUntil ? `<p class="kb-offer-until">Angebot gültig bis <strong>${fmtDateShort(offer.validUntil)}</strong></p>` : ''}
|
||||
<ul class="kb-checks">${ABO_SIDE_FEATURES.map((f) => `<li>${escapeHtml(f)}</li>`).join('')}</ul>
|
||||
<button type="button" class="kb-btn-primary kb-btn-block" data-view="abo"${data.available ? '' : ' disabled'}>Abo abschließen</button>
|
||||
<p class="kb-fine">${data.available
|
||||
|
||||
@@ -122,6 +122,8 @@
|
||||
display: inline-block; margin: 0 0 10px; padding: 3px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 700;
|
||||
color: #86efac; background: rgba(74, 222, 128, 0.1); border: 1px solid rgba(74, 222, 128, 0.4);
|
||||
}
|
||||
.kb-offer-until { margin: -6px 0 14px; font-size: 12.5px; color: var(--kb-muted); }
|
||||
.kb-offer-until strong { color: #fff; }
|
||||
.kb-fine { margin: 10px 0 0; font-size: 11.5px; line-height: 1.5; color: var(--kb-faint); }
|
||||
.kb-fine a { color: #fff; font-weight: 700; }
|
||||
.kb-status { display: inline-block; padding: 3px 11px; border-radius: 999px; font-size: 12px; font-weight: 700; }
|
||||
@@ -713,7 +715,7 @@ button.kb-avatar:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }
|
||||
#view-wartung .kb-abo { padding: 20px 18px 18px; border-radius: 20px; }
|
||||
#view-wartung .kb-abo h3 { font-size: 22px; }
|
||||
#view-wartung .kb-price strong, #view-wartung .kb-hours strong { font-size: 32px; }
|
||||
#view-wartung .kb-price span, #view-wartung .kb-hours span, #view-wartung .kb-sub { font-size: 14px; }
|
||||
#view-wartung .kb-price span, #view-wartung .kb-hours span, #view-wartung .kb-sub, #view-wartung .kb-offer-until { font-size: 14px; }
|
||||
#view-wartung .kb-checks { gap: 9px; font-size: 14.5px; }
|
||||
#view-wartung .kb-fine { font-size: 12.5px; }
|
||||
#view-wartung .kb-side-alert { font-size: 13.5px; }
|
||||
|
||||
Reference in new issue
Block a user