Files
Webklar-Kundenbereich/public/neu/ansicht-stunden.js
T
ClaudeandClaude Opus 5.5 83ac903ab3 Navigation leichter: Handy-Leiste, Schwarz nur für die Hauptaktion, Verträge mit zwei Reitern, kürzeres Konto (V3, V5, V7, V8)
V3: In der Fahrstuhl-Zeile läuft unter dem aktiven Wort eine dünne Linie statt des schwarzen Blocks; das Wort wird dunkel und fett. Die weiße Overlay-Kopie bleibt im Baum, wird aber nicht mehr gezeigt.
V5: Terminplaner: Tag/Woche/Monat als hellgrauer Umschalter mit weißer Auswahl, Filter als leise Chips. „Einschalten“ in der Liste Entdecken ist ein heller Knopf; schwarz bleibt der Knopf der Bestätigungsseite.
V7: Verträge nur noch mit Abo und Rechnungen. Das Stundenkonto steht beim Wartungs-Abo als Abschnitt unter dem Abo (ohne doppelte Warnungen), #stunden springt dorthin. Der E-Mail-Speicher ist die Unterseite #speicher (Link „ändern“ in der Abo-Zeile), ohne Widerrufsstreifen wie die anderen Bestellseiten. Alte Links bleiben gültig.
V8: Konto mit vier Zeilen (Passwort, Verträge kündigen, Vertrag widerrufen, Abmelden), Datenschutz und Impressum klein darunter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 17:27:30 +00:00

76 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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, ersetze, 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)}`}`)))
}
/**
* Stundenkonto. daten (GET /api/abo) ist optional; ohne sie lädt die Funktion selbst.
* eingebettet: Abschnitt unter dem Abo (Navigation leichter V7). Dann ohne die Warnungen und den Bedingungen-Link, die das Abo darüber schon zeigt,
* und ohne Hinweis, wenn es kein Wartungs-Abo gibt (der Abschnitt steht dann gar nicht da).
*/
export async function zeigeStunden(wurzel, { route, me, nav, daten, eingebettet = false } = {}) {
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)
if (!eingebettet) 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)]
ersetze(wurzel,
...(eingebettet ? [] : 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.'),
eingebettet ? null : 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(eingebettet ? 'h3' : '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.')))
}