Kundenbereich: Verträge und Abmelden nur noch im Profilbild-Menü

- "Abo verwalten & Rechnungen" (Stripe-Kundenportal) steht nur noch im Konto-Menü
  (Gruppe "Verträge", nur mit Abo). In der Wartungskarte bleibt nur bei fehlgeschlagener
  Abbuchung "Zahlungsart aktualisieren".
- "Verträge hier kündigen" und "Vertrag widerrufen": raus aus Seitenfuß, Wartungskarte und
  Abo-Seite; im Kundenbereich nur noch im Konto-Menü. Ausnahme: während der Widerrufsfrist
  zeigt die Wartungskarte weiter "Vertrag widerrufen · bis TT.MM." (§ 356a BGB: hervorgehoben
  platziert). Öffentlich bleiben beide auf der Login-Seite und im Footer von webklar.com.
- "Abmelden" nicht mehr in der Kopfzeile, nur noch im Konto-Menü.
- Hinweis unter den Rechnungen verweist aufs Profilbild und öffnet das Menü.
- Browser ohne <dialog>: Menü klappt als Box auf (statt Abmelden in der Kopfzeile).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-09-26 16:43:21 +00:00
1 parent d72d30dd2a
commit 93381a81c5
3 files changed
+79 -48

No files matched your search

+46 -25
View File
@@ -706,26 +706,30 @@ function renderAboSideActive(card, data) {
<strong>${fmtHours(l.balanceMinutes)}</strong><span>Arbeitszeit verfügbar</span><em aria-hidden="true">›</em>
</button>
<p class="kb-sub">${dateLine}</p>
${sub.paymentProblem ? '<p class="kb-side-alert">Die letzte Abbuchung ist fehlgeschlagen. Bitte aktualisieren Sie Ihre Zahlungsart, damit Ihre Website online bleibt.</p>' : ''}
<button type="button" class="kb-side-btn" id="abo-manage">${sub.paymentProblem ? 'Zahlungsart aktualisieren ↗' : 'Abo verwalten &amp; Rechnungen ↗'}</button>
${sub.paymentProblem ? `
<p class="kb-side-alert">Die letzte Abbuchung ist fehlgeschlagen. Bitte aktualisieren Sie Ihre Zahlungsart, damit Ihre Website online bleibt.</p>
<button type="button" class="kb-side-btn" id="abo-manage">Zahlungsart aktualisieren ↗</button>` : ''}
<button type="button" class="kb-side-btn" data-view="takeover">Übernahmegutschrift beantragen <span class="kb-badge hidden" id="takeover-badge" title="Rückfrage offen"></span></button>
${sub.withdrawalOpen ? `<a class="kb-side-btn" href="/widerrufen.html">Vertrag widerrufen <small>· bis ${fmtDayMonth(sub.withdrawalUntil)}</small></a>` : ''}
<div class="kb-divider"></div>
<p class="kb-fine" style="margin-top:0">${aboTermText(sub)}</p>
${sub.cancelAtPeriodEnd ? '' : '<p class="kb-fine"><a href="/kuendigen.html">Vertrag hier kündigen</a></p>'}
<p class="error hidden" id="abo-error"></p>`
card.querySelector('#abo-manage')?.addEventListener('click', async (ev) => {
const btn = ev.currentTarget
btn.disabled = true
try {
const { url } = await api('/api/abo/portal', { method: 'POST' })
window.location.href = url
} catch (err) {
showError(card.querySelector('#abo-error'), err.message)
btn.disabled = false
}
})
card.querySelector('#abo-manage')?.addEventListener('click', (ev) => openBillingPortal(ev.currentTarget, card.querySelector('#abo-error')))
}
/* Stripe-Kundenportal (Zahlungsart, Rechnungen, Abo): "Abo verwalten & Rechnungen" im Konto-Menü,
in der Wartungskarte nur bei fehlgeschlagener Abbuchung ("Zahlungsart aktualisieren") */
async function openBillingPortal(btn, errorEl) {
btn.disabled = true
hideError(errorEl)
try {
const { url } = await api('/api/abo/portal', { method: 'POST' })
window.location.href = url
} catch (err) {
showError(errorEl, err.message)
btn.disabled = false
}
}
/* Ansicht "Wartungs-Abo": Bestellung (Bestellübersicht direkt über dem Bestellbutton, § 312j BGB) */
@@ -842,7 +846,7 @@ function renderAboAccountView(view, data) {
<div class="kb-panel">
<p class="abo-note" style="margin-top:0">Jeden bezahlten Monat kommt 1 Stunde dazu. Angerechnet wird in 15-Minuten-Schritten, die ältesten Stunden zuerst. Aufträge schicken Sie uns einfach über den <a href="#chat" data-view="chat">Chat</a>.</p>
${sub.offerText ? `<p class="abo-note">Ihr Angebot: ${escapeHtml(sub.offerText)}.</p>` : ''}
<p class="abo-note">${aboTermText(sub)}${sub.cancelAtPeriodEnd ? '' : ' <a href="/kuendigen.html">Vertrag hier kündigen</a>'}</p>
<p class="abo-note">${aboTermText(sub)}</p>
<p class="abo-note" style="margin-bottom:0"><a href="/abo-bedingungen.html" target="_blank" rel="noopener">Leistungen &amp; Bedingungen</a></p>
</div>
<div class="kb-panel">
@@ -880,6 +884,7 @@ async function loadAboCard({ afterCheckout = false } = {}) {
}
card.classList.remove('hidden')
document.getElementById('invoices-abo-note')?.classList.toggle('hidden', !data.subscription)
document.getElementById('kb-sheet-abo')?.classList.toggle('hidden', !data.subscription)
loadTakeoverCard({ abo: data })
document.dispatchEvent(new CustomEvent('kb:abo', { detail: data }))
} catch (err) {
@@ -1185,14 +1190,16 @@ async function initDashboardPage() {
const loading = document.getElementById('loading')
const empty = document.getElementById('empty')
const loadError = document.getElementById('load-error')
const logoutBtn = document.getElementById('logout-btn')
initAppShell()
logoutBtn?.addEventListener('click', async () => {
// Konto-Menü (Profilbild): Abmelden und "Abo verwalten & Rechnungen" gibt es nur dort
document.getElementById('kb-sheet-logout')?.addEventListener('click', async () => {
await api('/api/auth/logout', { method: 'POST' })
window.location.href = '/login.html'
})
document.getElementById('kb-sheet-abo')?.addEventListener('click', (ev) =>
openBillingPortal(ev.currentTarget, document.getElementById('kb-sheet-error')))
// Seitenleiste, Chips in den Projektkarten und Links im Inhalt schalten die Ansicht um
document.addEventListener('click', (ev) => {
@@ -1438,15 +1445,30 @@ function renderInstallOffers() {
})
}
/* Konto: Bottom-Sheet (Handy) bzw. Popover (Desktop) mit Ansprechpartner, Rechtlichem und Abmelden */
/* Konto: Bottom-Sheet (Handy) bzw. Popover (Desktop) mit Ansprechpartner, Verträgen, Rechtlichem und Abmelden.
Geöffnet über das Profilbild oder Verweise mit data-open-account (z. B. Hinweis unter den Rechnungen). */
function initAccountSheet() {
const sheet = document.getElementById('kb-sheet')
const avatar = document.getElementById('customer-avatar')
if (!sheet || !avatar) return
const fill = () => {
document.getElementById('kb-sheet-name').textContent = document.getElementById('customer-name')?.textContent || 'Mein Konto'
document.getElementById('kb-sheet-meta').textContent = document.getElementById('customer-meta')?.textContent || ''
document.getElementById('kb-sheet-avatar').textContent = avatar.textContent
renderInstallOffers()
}
if (typeof sheet.showModal !== 'function') {
// Sehr alte Browser ohne <dialog>: Abmelden bleibt in der Kopfzeile
// Sehr alte Browser ohne <dialog>: das Menü klappt als einfache Box unter der Kopfzeile auf (kb-no-sheet)
document.body.classList.add('kb-no-sheet')
avatar.disabled = true
const show = (on) => {
if (on) fill()
sheet.toggleAttribute('open', on)
}
avatar.addEventListener('click', () => show(!sheet.hasAttribute('open')))
document.addEventListener('click', (ev) => {
if (ev.target.closest('[data-open-account]')) show(true)
else if (ev.target.closest('#kb-sheet [data-sheet-close], #kb-sheet [data-view]')) show(false)
})
return
}
const panel = sheet.querySelector('.kb-sheet-panel')
@@ -1457,10 +1479,7 @@ function initAccountSheet() {
const open = () => {
if (sheet.open) return
closeToken += 1
document.getElementById('kb-sheet-name').textContent = document.getElementById('customer-name')?.textContent || 'Mein Konto'
document.getElementById('kb-sheet-meta').textContent = document.getElementById('customer-meta')?.textContent || ''
document.getElementById('kb-sheet-avatar').textContent = avatar.textContent
renderInstallOffers()
fill()
sheet.classList.remove('is-closing')
panel.style.transform = ''
sheet.showModal()
@@ -1485,6 +1504,9 @@ function initAccountSheet() {
}
avatar.addEventListener('click', open)
document.addEventListener('click', (ev) => {
if (ev.target.closest('[data-open-account]')) open()
})
// Escape bzw. Android-Zurück: mit Animation schließen
sheet.addEventListener('cancel', (ev) => {
ev.preventDefault()
@@ -1499,7 +1521,6 @@ function initAccountSheet() {
sheet.addEventListener('click', (ev) => {
if (ev.target === sheet || ev.target.closest('[data-sheet-close], [data-view]')) close()
})
document.getElementById('kb-sheet-logout')?.addEventListener('click', () => document.getElementById('logout-btn')?.click())
// Nach unten wischen schließt das Sheet (Griff und Kopf des Sheets)
let drag = null