Bestellseiten für Hosting- und Wartungs-Abo

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-09-29 15:13:22 +00:00
1 parent 19af93338b
commit 90a3c57aa3
1 file changed
+35 -15
+35 -15
View File
@@ -876,14 +876,24 @@ async function openBillingPortal(btn, errorEl) {
}
}
/* Ansicht "Wartungs-Abo": Bestellung (Bestellübersicht direkt über dem Bestellbutton, § 312j BGB) */
function renderAboOrderView(view, data) {
const available = data.available
const offer = data.offer
/* Bestellseite je Produkt (#bestellen-hosting, #bestellen-wartung): Bestellübersicht direkt über dem Bestellbutton (§ 312j BGB) */
function renderOrderView(view, data, productKey) {
const product = productOf(data, productKey)
const offer = data.offers?.[productKey] || null
const label = productLabelOf(data, productKey)
if (!product || !data.available) {
view.innerHTML = `
<header class="kb-head"><h1>${escapeHtml(label)}</h1></header>
<div class="kb-panel"><p class="abo-note" style="margin:0">Die Online-Bestellung wird gerade eingerichtet – schreiben Sie uns gern im Chat.</p></div>`
return
}
const price = fmtEuro(product.amountCents / 100)
const lateStart = data.lateStartAt ? fmtDateShort(data.lateStartAt) : ''
const isWartung = productKey === 'wartung'
view.innerHTML = `
<header class="kb-head">
<h1>Wartungs-Abo</h1>
<p>Betrieb, Pflege und Support für Ihre Website – ${offer ? 'mit Ihrem persönlichen Angebot' : 'für 50 € im Monat'}.</p>
<h1>${escapeHtml(label)}</h1>
<p>${isWartung ? 'Betrieb, Pflege und Support für Ihre Website' : 'Ihre Website sicher und schnell online'} – ${offer ? 'mit Ihrem persönlichen Angebot' : `für ${escapeHtml(price)} im Monat`}.</p>
</header>
${offer ? `
<div class="abo-offer-box">
@@ -893,10 +903,9 @@ function renderAboOrderView(view, data) {
<p class="abo-note">Gültig bis ${fmtDateShort(offer.validUntil)} · wird beim Abschluss automatisch berücksichtigt</p>
</div>` : ''}
<div class="kb-panel">
${aboFeaturesHtml(productOf(data, 'wartung')?.features)}
${aboFeaturesHtml(product.features)}
<p class="abo-note" style="margin:0">Schon bei einem anderen Anbieter für Domain, Hosting oder E-Mail bezahlt? Nach dem Abschluss können Sie eine Übernahmegutschrift beantragen – wir rechnen die doppelt bezahlte Zeit anteilig an.</p>
</div>
${available ? `
<div class="kb-panel" id="abo-order">
<h3>Ihre Bestellung</h3>
${data.testAccount ? `<div class="abo-alert">Testkonto: Bei Stripe wird ein Test-Rabatt verrechnet, abgebucht werden nur 0,50 € pro Monat.${offer ? ' Das Angebot wird nach dem Abschluss zusätzlich ans Abo gehängt.' : ''}</div>` : ''}
@@ -908,18 +917,19 @@ function renderAboOrderView(view, data) {
<label class="abo-check"><input type="radio" name="abo-start-choice" value="sofort">
<span><strong>Sofort.</strong> Ich verlange ausdrücklich, dass WEBklar vor Ablauf der Widerrufsfrist mit der Leistung beginnt. Mir ist bekannt, dass ich bei einem Widerruf einen angemessenen Betrag für die bis dahin erbrachten Leistungen zahle.</span></label>
<label class="abo-check"><input type="radio" name="abo-start-choice" value="nach_frist">
<span><strong>Nach Ablauf der 14-tägigen Widerrufsfrist</strong>${data.lateStartAt ? ` (am ${fmtDateShort(data.lateStartAt)})` : ''}. Bis dahin erbringt WEBklar keine Leistungen und bucht nichts ab.</span></label>
<span><strong>Nach Ablauf der 14-tägigen Widerrufsfrist</strong>${lateStart ? ` (am ${lateStart})` : ''}. Bis dahin erbringt WEBklar keine Leistungen und bucht nichts ab.</span></label>
<p class="abo-note">Firmenkunden haben kein Widerrufsrecht und wählen am besten „Sofort“.</p>
</fieldset>
<div class="abo-summary-box">
<table class="abo-summary">
<tr><td>Leistung</td><td>WEBklar Wartungs-Abo: Betrieb &amp; Pflege Ihrer Website bzw. Web-Anwendung, 1 Domain, 1 E-Mail-Postfach (5 GB), 1 Stunde Arbeitszeit je bezahltem Monat (12 Monate nutzbar)</td></tr>
<tr><td>Leistung</td><td>${escapeHtml(product.name)}: ${(product.features || []).map(([t]) => escapeHtml(t)).join(', ')}</td></tr>
<tr><td>Preis</td><td>${offer
? `<strong>${escapeHtml(offer.text)}</strong> (individuelles Angebot; Gesamtpreis, gemäß § 19 UStG ohne Umsatzsteuer), keine weiteren Kosten`
: '<strong>50,00 € pro Monat</strong> (Gesamtpreis, gemäß § 19 UStG ohne Umsatzsteuer), keine weiteren Kosten'}</td></tr>
: `<strong>${escapeHtml(price)} pro Monat</strong> (Gesamtpreis, gemäß § 19 UStG ohne Umsatzsteuer), keine weiteren Kosten`}</td></tr>
<tr><td>Abrechnung</td><td>monatlich im Voraus, erstmals zum Leistungsbeginn</td></tr>
<tr><td>Laufzeit</td><td><strong>Mindestlaufzeit 12 Monate</strong> ab Leistungsbeginn, kündbar zum Ende der Mindestlaufzeit bis einen Monat vorher; danach unbefristet und jederzeit zum Ende des Abrechnungsmonats kündbar</td></tr>
<tr><td>Leistungsbeginn</td><td id="abo-start-summary">Bitte oben wählen: sofort oder nach Ablauf der 14-tägigen Widerrufsfrist${lateStart ? ` (am ${lateStart})` : ''}</td></tr>
<tr><td>Laufzeit</td><td><strong>Mindestlaufzeit ${escapeHtml(String(product.minTermMonths))} Monate</strong> ab Leistungsbeginn, kündbar zum Ende der Mindestlaufzeit bis einen Monat vorher; danach unbefristet und jederzeit zum Ende des Abrechnungsmonats kündbar</td></tr>
<tr><td>Zahlung</td><td>Kreditkarte oder SEPA-Lastschrift über Stripe</td></tr>
<tr><td>Anbieter</td><td>WEBklar GbR, Am Schwimmbad 10, 67722 Winnweiler · Telefon 0176 23726355 · support@webklar.com</td></tr>
</table>
@@ -930,8 +940,14 @@ function renderAboOrderView(view, data) {
<button type="button" class="kb-btn-primary" id="abo-buy">Zahlungspflichtig bestellen</button>
<span class="abo-note">Danach hinterlegen Sie bei unserem Zahlungsdienstleister Stripe Ihre Zahlungsart. Bis zum Abschluss dort können Sie jederzeit abbrechen.</span>
</div>
</div>` : `
<div class="kb-panel"><p class="abo-note" style="margin:0">Die Online-Bestellung wird gerade eingerichtet – schreiben Sie uns gern im Chat.</p></div>`}`
</div>`
view.querySelectorAll('input[name="abo-start-choice"]').forEach((radio) => radio.addEventListener('change', () => {
const el = view.querySelector('#abo-start-summary')
if (el) el.textContent = radio.value === 'sofort'
? 'sofort, auf Ihren ausdrücklichen Wunsch vor Ablauf der Widerrufsfrist'
: `nach Ablauf der 14-tägigen Widerrufsfrist${lateStart ? ` (am ${lateStart})` : ''}; bis dahin keine Leistung und keine Abbuchung`
}))
view.querySelector('#abo-buy')?.addEventListener('click', async (ev) => {
const btn = ev.currentTarget
@@ -952,7 +968,7 @@ function renderAboOrderView(view, data) {
try {
const { url } = await api('/api/abo/checkout', {
method: 'POST',
body: JSON.stringify({ acceptTerms, startChoice, termsVersion: data.termsVersion, offerId: offer?.id || '', lateStartAt: data.lateStartAt || '' }),
body: JSON.stringify({ productKey, acceptTerms, startChoice, termsVersion: data.termsVersion, offerId: offer?.id || '', lateStartAt: data.lateStartAt || '' }),
})
window.location.href = url
} catch (err) {
@@ -1149,6 +1165,10 @@ async function loadContracts({ afterCheckout = false } = {}) {
aboState.data = data
renderSideCard(card, data)
renderContractsView(contracts, data)
for (const key of ['hosting', 'wartung']) {
const orderView = document.getElementById(`view-bestellen-${key}`)
if (orderView) renderOrderView(orderView, data, key)
}
if (data.subscription?.active && data.subscription.productKey === 'wartung') renderHoursView(view, data)
else view.innerHTML = '<header class="kb-head"><h1>Stundenkonto</h1></header><div class="kb-panel"><p class="abo-note" style="margin:0">Ein Stundenkonto gibt es im Wartungs-Abo. Ihre Verträge finden Sie unter <a href="#vertraege" data-view="vertraege">Verträge</a>.</p></div>'
card.classList.remove('hidden')