import { config } from '../config.js' import { termineFuerKunde } from './termine/schalter.js' // Helfer fuer das Funktionen-Register config.features (News-Pinnwand, Terminplaner; Daten in server/config.js). // Diese Datei wird von siteFeatures.js importiert und importiert selbst keine Dienste statisch (einzige Ausnahme: // termine/schalter.js, das nur config liest): Standard-Haken laden ihre Dienste per dynamischem import() // innerhalb der Funktion (sonst entstuenden Importzyklen). /** Namen der Haken, die ein Registereintrag hat (optional): Bestell-, Freischalt- und Unterlagenpruefungen. */ const HAKEN_NAMEN = ['verfuegbar', 'bestellpruefung', 'unterlagenBereit', 'belehrung', 'freischaltbar'] /** * Bestellpruefung des Terminplaners im Checkout: die offenen Punkte des Entwurfs (Einstellung und Fragebogen aus * termineConfig; ohne Dokument beides null) nach termine/fragebogen.js. Ohne abo und mit mitAbo: false: den Abo-Punkt (h) * wendet der Checkout nicht an, die Abo-Stufe (7) der Route liefert kein_abo | gekuendigt | nicht_aktiv. */ async function termineBestellpruefung({ customerId, projectId, feature }) { const [{ getFeatureDoc }, { ladeEntwurf }, { bestellpruefung }] = await Promise.all([ import('./siteFeatures.js'), import('./featureOrder.js'), import('./termine/fragebogen.js'), ]) const doc = await getFeatureDoc(customerId, projectId, 'termine') const { einstellung, fragebogen } = doc ? await ladeEntwurf(doc.$id) : { einstellung: null, fragebogen: null } return bestellpruefung({ einstellung, fragebogen, feature, mitAbo: false }) } /** * Standard-Haken je Funktion (Funktionen, nicht Daten). B6 fuellt verfuegbar, bestellpruefung und unterlagenBereit * (Standard immer bereit; F8 haengt die Rechtsunterlagen ein), B7 belehrung und B11 freischaltbar. News hat keine * Standard-Haken. Feste Funktionen, damit featureHaken bei jedem Aufruf dieselben liefert. */ const STANDARD_HAKEN = { news: {}, termine: { verfuegbar: (email) => termineFuerKunde(email), bestellpruefung: termineBestellpruefung, unterlagenBereit: async () => true, }, } /** Registereintrag oder null; nur eigene Schluessel des Registers ('analyse', '__proto__', 'toString' => null). */ export function featureConfig(key) { if (typeof key !== 'string' || !Object.hasOwn(config.features, key)) return null return config.features[key] } /** Schluessel der Funktionen im Register, in Registerreihenfolge. */ export function featureKeys() { return Object.keys(config.features) } /** * Haken einer Funktion: { verfuegbar?, bestellpruefung?, unterlagenBereit?, belehrung?, freischaltbar? }. * Wer config.features.. als Funktion setzt (Tests), ueberschreibt den Standard. */ export function featureHaken(key) { const eintrag = featureConfig(key) if (!eintrag) return {} const standard = Object.hasOwn(STANDARD_HAKEN, key) ? STANDARD_HAKEN[key] : {} const haken = {} for (const name of HAKEN_NAMEN) { const fn = typeof eintrag[name] === 'function' ? eintrag[name] : standard[name] if (typeof fn === 'function') haken[name] = fn } return haken } const NICHT_VERFUEGBAR = 'Diese Funktion ist noch nicht verfügbar.' /** * Pruefstufe 5 des Checkouts: Text, warum die Funktion diesem Kunden (noch) nicht angeboten wird, sonst null. * Gefragt wird der Haken verfuegbar(email) (Terminplaner: Schalter TERMINE_ENABLED und Pilotliste); Funktionen ohne * Haken (News) sind immer verfuegbar. Ein unbekannter Schluessel ist nicht verfuegbar (fail-closed). */ export function featureSperre(key, email) { const eintrag = featureConfig(key) if (!eintrag) return NICHT_VERFUEGBAR const { verfuegbar } = featureHaken(key) if (typeof verfuegbar !== 'function' || verfuegbar(email)) return null return eintrag.unavailableMessage || NICHT_VERFUEGBAR } const fassung = (f) => (f ? { version: f.version, url: f.url ?? '' } : null) /** * Sicht fuer den Browser (Bestellseite): JSON-sicher, ohne Funktionen und ohne ticket/mails, eine Kopie * (Aenderungen daran aendern das Register nicht). Unbekannter Schluessel => null. */ export function oeffentlicheAnsicht(key) { const e = featureConfig(key) if (!e) return null return { key: e.key, name: e.label.name, kurz: e.label.kurz, priceLabel: e.priceLabel, setupEstimate: e.setupEstimate, consent: { version: e.consent.version, knopf: e.consent.knopf, items: e.consent.items.map((item) => ({ id: item.id, text: item.text, haken: Boolean(item.haken) })), }, leistungsbeschreibung: fassung(e.leistungsbeschreibung), avv: fassung(e.avv), widerruf: fassung(e.widerruf), returnHash: e.returnHash, requiresWartungsAbo: e.requiresWartungsAbo === true, } }