diff --git a/public/app.js b/public/app.js index 236b8ac..e740cab 100644 --- a/public/app.js +++ b/public/app.js @@ -706,26 +706,30 @@ function renderAboSideActive(card, data) { ${fmtHours(l.balanceMinutes)}Arbeitszeit verfügbar

${dateLine}

- ${sub.paymentProblem ? '

Die letzte Abbuchung ist fehlgeschlagen. Bitte aktualisieren Sie Ihre Zahlungsart, damit Ihre Website online bleibt.

' : ''} - + ${sub.paymentProblem ? ` +

Die letzte Abbuchung ist fehlgeschlagen. Bitte aktualisieren Sie Ihre Zahlungsart, damit Ihre Website online bleibt.

+ ` : ''} ${sub.withdrawalOpen ? `Vertrag widerrufen · bis ${fmtDayMonth(sub.withdrawalUntil)}` : ''}

${aboTermText(sub)}

- ${sub.cancelAtPeriodEnd ? '' : '

Vertrag hier kündigen

'} ` - 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) {

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 Chat.

${sub.offerText ? `

Ihr Angebot: ${escapeHtml(sub.offerText)}.

` : ''} -

${aboTermText(sub)}${sub.cancelAtPeriodEnd ? '' : ' Vertrag hier kündigen'}

+

${aboTermText(sub)}

Leistungen & Bedingungen

@@ -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 : Abmelden bleibt in der Kopfzeile + // Sehr alte Browser ohne : 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 diff --git a/public/dashboard.html b/public/dashboard.html index 050bee1..3a311c6 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -44,12 +44,14 @@
- - + - +
@@ -66,10 +68,16 @@
-