/* global SkriftConfigurator */ import { createInitialState, deriveContextFromUrl, reducer, STEPS, } from "./configurator-state.js?ver=0.3.1"; import { render, showValidationOverlay, hideValidationOverlay, showOverflowWarning, showValidationError, flushAllTables, } from "./configurator-ui.js?ver=0.3.1"; import "./configurator-api.js"; // Backend API initialisieren import PreviewManager from "./configurator-preview-manager.js"; // Preview Management (function boot() { const root = document.querySelector('[data-skrift-konfigurator="1"]'); if (!root) return; // Guard: Verhindere doppelte Initialisierung if (root.dataset.skriftInitialized === "1") { console.warn("[Konfigurator] Bereits initialisiert, überspringe."); return; } root.dataset.skriftInitialized = "1"; // Cleanup bei Navigation (SPA) oder Seiten-Unload const cleanupHandlers = []; const cleanup = () => { flushAllTables(); // Tabellen-Daten speichern vor Cleanup/Seiten-Unload cleanupHandlers.forEach((handler) => handler()); cleanupHandlers.length = 0; if (window.envelopePreviewManager) { window.envelopePreviewManager.destroy(); } if (window.contentPreviewManager) { window.contentPreviewManager.destroy(); } }; // Cleanup bei Seiten-Unload window.addEventListener("beforeunload", cleanup); cleanupHandlers.push(() => window.removeEventListener("beforeunload", cleanup), ); const ctx = deriveContextFromUrl(window.location.search); let state = createInitialState(ctx); const dom = { topbar: document.getElementById("sk-topbar"), stepper: document.getElementById("sk-stepper"), form: document.getElementById("sk-form"), prev: document.getElementById("sk-prev"), next: document.getElementById("sk-next"), preview: document.getElementById("sk-preview"), previewMobile: document.getElementById("sk-preview-mobile"), contactMobile: document.getElementById("sk-contact-mobile"), }; // Preview Manager initialisieren const api = window.SkriftBackendAPI; if (api) { window.envelopePreviewManager = new PreviewManager(api); window.contentPreviewManager = new PreviewManager(api); } const dispatch = (action) => { // Scroll-Position VOR dem State-Update speichern const scrollY = window.scrollY; const scrollX = window.scrollX; state = reducer(state, action); render({ state, dom, dispatch }); // Scroll-Position NACH dem Render wiederherstellen // Nur bei Actions die NICHT den Step wechseln (Navigation) const navigationActions = ["NAV_NEXT", "NAV_PREV", "SET_STEP"]; if (!navigationActions.includes(action.type)) { // requestAnimationFrame stellt sicher, dass das DOM komplett gerendert ist requestAnimationFrame(() => { window.scrollTo(scrollX, scrollY); }); } }; // Event-Handler mit Cleanup-Tracking const prevClickHandler = () => { flushAllTables(); // Tabellen-Daten speichern vor Navigation dispatch({ type: "NAV_PREV" }); }; // Next-Handler mit Validierung bei Content-Step const nextClickHandler = async () => { flushAllTables(); // Tabellen-Daten speichern vor Navigation // WICHTIG: Globalen State in den Store synchronisieren // Nötig weil Paste direkt in window.currentGlobalState schreibt (Performance) const globalState = window.currentGlobalState; if (globalState) { // Alle Tabellen-Daten in einem einzigen Dispatch synchronisieren dispatch({ type: "SYNC_GLOBAL_STATE", payload: { recipientRows: globalState.recipientRows, freeAddressRows: globalState.freeAddressRows, placeholderValues: globalState.placeholderValues, }, }); } // WICHTIG: Aktuellen State verwenden, nicht den gecachten aus dem Closure const currentState = window.currentGlobalState || state; // Bei Content-Step: Textlänge validieren und alle Previews generieren if (currentState.step === STEPS.CONTENT) { const previewManager = window.contentPreviewManager; if (previewManager) { showValidationOverlay(); try { const validation = await previewManager.validateTextLength(currentState); if (!validation.valid) { hideValidationOverlay(); // Bei Overflow: Warnung anzeigen if ( validation.overflowFiles && validation.overflowFiles.length > 0 ) { showOverflowWarning(validation.overflowFiles, dom.form); } else if (validation.error) { // Bei Fehler (z.B. keine Anfragen mehr): Fehlermeldung anzeigen showValidationError(validation.error, dom.form); } return; // Nicht weiter navigieren } // Nach erfolgreicher Validierung: Umschlag-Previews generieren (gleiche Session) // So sind alle Dokumente im Cache wenn die Bestellung finalisiert wird const envelopeManager = window.envelopePreviewManager; if (envelopeManager && currentState.answers?.envelope === true) { try { envelopeManager.previewCount = parseInt(currentState.answers?.quantity) || 1; await envelopeManager.loadAllPreviews(currentState, true, true); console.log("[App] Envelope previews generated for cache"); } catch (envError) { console.error( "[App] Envelope preview generation failed:", envError, ); // Nicht blockieren - Umschläge sind nicht kritisch für Navigation } } hideValidationOverlay(); } catch (error) { hideValidationOverlay(); console.error("[App] Validation error:", error); showValidationError( error.message || "Validierung fehlgeschlagen", dom.form, ); return; // Nicht weiter navigieren } } } dispatch({ type: "NAV_NEXT" }); }; dom.prev.addEventListener("click", prevClickHandler); dom.next.addEventListener("click", nextClickHandler); cleanupHandlers.push(() => { dom.prev.removeEventListener("click", prevClickHandler); dom.next.removeEventListener("click", nextClickHandler); }); // Keyboard Navigation für Previews (nur innerhalb des aktuellen Batches) const keydownHandler = (e) => { if ( window.envelopePreviewManager && window.envelopePreviewManager.currentBatchPreviews.length > 0 ) { window.envelopePreviewManager.handleKeyboardNavigation( e, state, dom.preview, true, ); } if ( window.contentPreviewManager && window.contentPreviewManager.currentBatchPreviews.length > 0 ) { window.contentPreviewManager.handleKeyboardNavigation( e, state, dom.preview, false, ); } }; document.addEventListener("keydown", keydownHandler); cleanupHandlers.push(() => document.removeEventListener("keydown", keydownHandler), ); render({ state, dom, dispatch }); // Konfigurator sichtbar machen nachdem erster Render abgeschlossen ist requestAnimationFrame(() => { root.classList.add("sk-ready"); }); })();