Kundenansicht neu P2: Stundenkonto mit Vorschuss-Anzeige
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
989e7be640
commit
8a23652e07
3 files changed
+114
-3
No files matched your search
@@ -0,0 +1,70 @@
|
||||
/* Kundenansicht neu (P2): Reiter Stunden in Verträge. Stundenkonto des Wartungs-Abos mit Verlauf; ein Vorschuss für die Einrichtung
|
||||
(ab P6 in ledger.vorschussMinutes) wird nie als Mehraufwand gezeigt. */
|
||||
|
||||
import { h, leeren } from './dom.js'
|
||||
import { K, fmtStd, fmtDatum, ladeAbo, ABO_NICHT_GELADEN } from './ansicht-hilfen.js'
|
||||
import { aboWarnungen } from './vertraege-abo.js'
|
||||
|
||||
const laeufe = new WeakMap() // Reiter-Inhalt → Nummer des jüngsten Aufrufs
|
||||
const stat = (wert, beschriftung) => h('div', { class: 'kn-stat' }, h('strong', { class: 'kn-num' }, wert), h('span', null, beschriftung))
|
||||
|
||||
/** Zeile zum Vorschuss für die Einrichtung; bei 0 oder weniger leer. gekuendigt: der Rest verfällt. */
|
||||
export function vorschussZeile(minuten, gekuendigt) {
|
||||
if (!(Number(minuten) > 0)) return ''
|
||||
return `Vorschuss für Einrichtung: ${fmtStd(minuten)}, ${gekuendigt ? 'verfällt, wird nicht berechnet' : 'wird mit den nächsten Monaten verrechnet'}`
|
||||
}
|
||||
|
||||
function verlaufZeile(u) {
|
||||
const vorschuss = Number(u.vorschussMinutes) || 0
|
||||
const mehr = Number(u.overageMinutes) || 0
|
||||
const pille = vorschuss > 0
|
||||
? h('span', { class: K.pille.violett }, `Vorschuss ${fmtStd(vorschuss)}`)
|
||||
: mehr > 0 ? h('span', { class: K.pille.orange, title: 'Über das Kontingent hinaus' }, `+${fmtStd(mehr)} extra`) : null
|
||||
return h('tr', null,
|
||||
h('td', { 'data-label': 'Datum' }, fmtDatum(u.date)),
|
||||
h('td', { 'data-label': 'Vorgang' }, u.topic || u.details || 'Arbeitszeit', u.woid ? h('span', { class: 'kn-zeile-sub' }, ` #${u.woid}`) : null),
|
||||
h('td', { 'data-label': 'Zeit', class: 'kn-num' }, `−${fmtStd(u.minutes)}`, u.supportMinutes ? h('span', { class: 'kn-zeile-sub' }, ` inkl. ${fmtStd(u.supportMinutes)} Unterstützung`) : null, pille ? ' ' : null, pille))
|
||||
}
|
||||
|
||||
function gutschriftZeile(c) {
|
||||
return h('tr', null,
|
||||
h('td', { 'data-label': 'Datum' }, fmtDatum(c.grantedAt)),
|
||||
h('td', { 'data-label': 'Vorgang' }, 'Monatskontingent'),
|
||||
h('td', { 'data-label': 'Zeit', class: 'kn-num' }, `+${fmtStd(c.minutes)}`, h('span', { class: 'kn-zeile-sub' }, ` ${c.expired ? 'abgelaufen' : `nutzbar bis ${fmtDatum(c.expiresAt)}`}`)))
|
||||
}
|
||||
|
||||
/** Reiter-Inhalt Stunden. daten (GET /api/abo) ist optional; ohne sie lädt die Funktion selbst. */
|
||||
export async function zeigeStunden(wurzel, { route, me, nav, daten } = {}) {
|
||||
const nr = (laeufe.get(wurzel) ?? 0) + 1
|
||||
laeufe.set(wurzel, nr)
|
||||
const aktuell = () => laeufe.get(wurzel) === nr
|
||||
leeren(wurzel).append(h('p', { class: K.leer }, 'Wird geladen …'))
|
||||
|
||||
const abo = daten ?? await ladeAbo()
|
||||
if (!aktuell()) return
|
||||
if (!abo) { leeren(wurzel).append(h('p', { class: K.fehler }, ABO_NICHT_GELADEN)); return }
|
||||
const sub = abo.subscription
|
||||
const l = abo.ledger
|
||||
if (!sub?.active || sub.productKey !== 'wartung' || !l) {
|
||||
leeren(wurzel).append(h('p', { class: K.leer }, 'Ein Stundenkonto gibt es im Wartungs-Abo.'))
|
||||
return
|
||||
}
|
||||
const gekuendigt = Boolean(sub.cancelAtPeriodEnd || sub.cancelAt)
|
||||
const vorschuss = vorschussZeile(l.vorschussMinutes, gekuendigt)
|
||||
const zeilen = [...(l.usage || []).map(verlaufZeile), ...(l.credits || []).slice().reverse().map(gutschriftZeile)]
|
||||
|
||||
leeren(wurzel).append(
|
||||
...aboWarnungen(sub),
|
||||
h('div', { class: 'kn-statistik' },
|
||||
stat(fmtStd(l.balanceMinutes), 'verfügbare Zeit'),
|
||||
stat(fmtStd(l.usedMinutes), 'bisher genutzt'),
|
||||
l.nextExpiry ? stat(fmtStd(l.nextExpiry.minutes), `verfallen am ${fmtDatum(l.nextExpiry.at)}`) : null),
|
||||
vorschuss ? h('p', { class: 'kn-hinweis kn-vorschuss' }, vorschuss) : null,
|
||||
h('p', { class: 'kn-hinweis' }, '1 Stunde pro bezahltem Monat. Angerechnet wird in 15-Minuten-Schritten, die ältesten Stunden zuerst.'),
|
||||
h('p', { class: 'kn-links' }, h('a', { href: sub.termsUrl || '/abo-bedingungen.html', target: '_blank', rel: 'noopener noreferrer' }, 'Leistungen & Bedingungen')),
|
||||
h('section', { class: 'kn-sektion' },
|
||||
h('h2', { class: K.abschnitt }, 'Verlauf'),
|
||||
zeilen.length
|
||||
? h('div', { class: 'kn-tabelle' }, h('table', null, h('thead', null, h('tr', null, h('th', null, 'Datum'), h('th', null, 'Vorgang'), h('th', null, 'Zeit'))), h('tbody', null, zeilen)))
|
||||
: h('p', { class: 'kn-hinweis' }, 'Noch keine Buchungen.')))
|
||||
}
|
||||
@@ -11,6 +11,14 @@ const TEXT_ZAHLUNG = 'Die letzte Abbuchung ist fehlgeschlagen. Bitte aktualisier
|
||||
const TEXT_ANSICHTSMODUS = 'Im Ansichtsmodus nicht möglich.'
|
||||
const TEXT_NICHT_VERFUEGBAR = 'Die Online-Bestellung wird gerade eingerichtet – schreib uns gern im Chat.'
|
||||
|
||||
/** Die zwei Warnungen über dem Vertrag (auch im Stundenkonto): fehlgeschlagene Abbuchung, späterer Beginn nach der Widerrufsfrist. */
|
||||
export function aboWarnungen(sub) {
|
||||
const aus = []
|
||||
if (sub?.active && sub.paymentProblem) aus.push(h('p', { class: `${K.meldung} kn-meldung-warn` }, TEXT_ZAHLUNG))
|
||||
if (sub?.active && sub.serviceStartsAt) aus.push(h('p', { class: K.meldung }, `Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist am ${fmtDatum(sub.serviceStartsAt)}. Dann wird auch zum ersten Mal abgebucht.`))
|
||||
return aus
|
||||
}
|
||||
|
||||
const gekuendigt = (sub) => Boolean(sub?.cancelAtPeriodEnd || sub?.cancelAt)
|
||||
const produktName = (daten, key) => {
|
||||
const p = (daten?.products || []).find((x) => x.key === key)
|
||||
@@ -153,9 +161,7 @@ export async function zeigeAbo(box, { daten, me } = {}) {
|
||||
const zustand = aboZustand(daten)
|
||||
const gutschrift = sub ? gutschriftLink() : null
|
||||
|
||||
const teile = []
|
||||
if (sub?.active && sub.paymentProblem) teile.push(h('p', { class: `${K.meldung} kn-meldung-warn` }, TEXT_ZAHLUNG))
|
||||
if (sub?.active && sub.serviceStartsAt) teile.push(h('p', { class: K.meldung }, `Wie gewählt beginnen wir nach Ablauf der Widerrufsfrist am ${fmtDatum(sub.serviceStartsAt)}. Dann wird auch zum ersten Mal abgebucht.`))
|
||||
const teile = aboWarnungen(sub)
|
||||
if (sub?.active) {
|
||||
teile.push(vertragsBlock(daten, sub, viewAs), wechselKarte(daten, sub))
|
||||
} else {
|
||||
|
||||
Reference in new issue
Block a user