221 lines
7.2 KiB
JavaScript
221 lines
7.2 KiB
JavaScript
/* 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");
|
|
});
|
|
})();
|