fix, table cp, mengenfeld

This commit is contained in:
s4luorth
2026-02-19 10:22:44 +01:00
parent 5e0fceab15
commit a9c86c6dad
3 changed files with 109 additions and 67 deletions

View File

@@ -13,6 +13,13 @@ import PreviewManager from './configurator-preview-manager.js'; // Preview Manag
const root = document.querySelector('[data-skrift-konfigurator="1"]'); const root = document.querySelector('[data-skrift-konfigurator="1"]');
if (!root) return; 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 // Cleanup bei Navigation (SPA) oder Seiten-Unload
const cleanupHandlers = []; const cleanupHandlers = [];

View File

@@ -3512,6 +3512,7 @@ function downloadText(filename, text) {
function createTableModal(dispatch, config) { function createTableModal(dispatch, config) {
let tableWrapper; let tableWrapper;
let currentState = config.getStateFunc(); let currentState = config.getStateFunc();
let isPasting = false; // Flag um Paste-Vorgang zu markieren
// State-Referenz aktualisieren (wird nach jedem Dispatch aufgerufen) // State-Referenz aktualisieren (wird nach jedem Dispatch aufgerufen)
const updateState = () => { const updateState = () => {
@@ -3585,6 +3586,9 @@ function createTableModal(dispatch, config) {
oninput: col.readOnly oninput: col.readOnly
? null ? null
: (e) => { : (e) => {
// Während Paste-Vorgang ignorieren - Paste-Handler übernimmt
if (isPasting) return;
// Scroll-Position VOR dem dispatch speichern // Scroll-Position VOR dem dispatch speichern
const modalContent = e.target.closest(".sk-modal-content"); const modalContent = e.target.closest(".sk-modal-content");
const scrollTop = modalContent ? modalContent.scrollTop : 0; const scrollTop = modalContent ? modalContent.scrollTop : 0;
@@ -3633,15 +3637,26 @@ function createTableModal(dispatch, config) {
// Excel Paste Handler mit automatischer Tabellen-Aktualisierung // Excel Paste Handler mit automatischer Tabellen-Aktualisierung
table.addEventListener("paste", (e) => { table.addEventListener("paste", (e) => {
e.preventDefault(); e.preventDefault();
isPasting = true; // Flag setzen um oninput zu blockieren
const text = e.clipboardData.getData("text/plain"); const text = e.clipboardData.getData("text/plain");
if (!text || !text.trim()) return; if (!text || !text.trim()) {
isPasting = false;
return;
}
const rows = text.split(/\r?\n/).filter((r) => r.trim()); const rows = text.split(/\r?\n/).filter((r) => r.trim());
if (rows.length === 0) return; if (rows.length === 0) {
isPasting = false;
return;
}
const activeInput = document.activeElement; const activeInput = document.activeElement;
if (!activeInput || !activeInput.classList.contains("sk-input")) return; if (!activeInput || !activeInput.classList.contains("sk-input")) {
isPasting = false;
return;
}
const startRow = parseInt(activeInput.dataset.row || "0"); const startRow = parseInt(activeInput.dataset.row || "0");
const startCol = parseInt(activeInput.dataset.col || "0"); const startCol = parseInt(activeInput.dataset.col || "0");
@@ -3683,6 +3698,11 @@ function createTableModal(dispatch, config) {
// Tabelle im Modal aktualisieren // Tabelle im Modal aktualisieren
renderTable(); renderTable();
} }
// Flag nach kurzem Delay zurücksetzen (für evtl. verzögerte input-Events)
setTimeout(() => {
isPasting = false;
}, 50);
}); });
return table; return table;
@@ -4039,20 +4059,26 @@ function renderRecipientsTable(state, dispatch) {
} }
table.appendChild(tbody); table.appendChild(tbody);
// Focusout-Handler mit Debounce um Flackern zu vermeiden // Save-Funktion für Registry (wird bei Navigation und periodisch aufgerufen)
let focusoutTimer = null; let hasLocalChanges = false;
let hasLocalChanges = false; // Track ob lokale Änderungen gemacht wurden const saveLocalChanges = () => {
table.addEventListener("focusout", (e) => { if (hasLocalChanges) {
if (focusoutTimer) clearTimeout(focusoutTimer); dispatch({ type: "SET_RECIPIENT_ROWS", rows: [...table._rows] });
focusoutTimer = setTimeout(() => { hasLocalChanges = false;
const newFocus = document.activeElement; }
if (!table.contains(newFocus) && hasLocalChanges) { };
// Nur dispatchen wenn lokale Änderungen gemacht wurden
dispatch({ type: "SET_RECIPIENT_ROWS", rows: [...table._rows] }); // Bei globaler Registry registrieren
hasLocalChanges = false; const unregisterSave = registerTableSave(saveLocalChanges);
}
}, 150); // Auto-Save alle 60 Sekunden
}); const autoSaveInterval = setInterval(saveLocalChanges, 60000);
// Cleanup-Funktion
table._cleanup = () => {
if (autoSaveInterval) clearInterval(autoSaveInterval);
unregisterSave();
};
// Änderungen tracken bei Input // Änderungen tracken bei Input
table.addEventListener("input", () => { table.addEventListener("input", () => {
@@ -4129,6 +4155,7 @@ function renderRecipientsTable(state, dispatch) {
}); });
if (pastedCells > 0) { if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_RECIPIENT_ROWS", rows: newData }); dispatch({ type: "SET_RECIPIENT_ROWS", rows: newData });
} }
}); });
@@ -4200,20 +4227,26 @@ function renderRecipientsTable(state, dispatch) {
} }
table.appendChild(tbody); table.appendChild(tbody);
// Focusout-Handler mit Debounce um Flackern zu vermeiden // Save-Funktion für Registry (wird bei Navigation und periodisch aufgerufen)
let focusoutTimer = null; let hasLocalChanges = false;
let hasLocalChanges = false; // Track ob lokale Änderungen gemacht wurden const saveLocalChanges = () => {
table.addEventListener("focusout", (e) => { if (hasLocalChanges) {
if (focusoutTimer) clearTimeout(focusoutTimer); dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: [...rows] });
focusoutTimer = setTimeout(() => { hasLocalChanges = false;
const newFocus = document.activeElement; }
if (!table.contains(newFocus) && hasLocalChanges) { };
// Nur dispatchen wenn lokale Änderungen gemacht wurden
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: [...rows] }); // Bei globaler Registry registrieren
hasLocalChanges = false; const unregisterSave = registerTableSave(saveLocalChanges);
}
}, 150); // Auto-Save alle 60 Sekunden
}); const autoSaveInterval = setInterval(saveLocalChanges, 60000);
// Cleanup-Funktion
table._cleanup = () => {
if (autoSaveInterval) clearInterval(autoSaveInterval);
unregisterSave();
};
// Änderungen tracken bei Input // Änderungen tracken bei Input
table.addEventListener("input", () => { table.addEventListener("input", () => {
@@ -4272,6 +4305,7 @@ function renderRecipientsTable(state, dispatch) {
}); });
if (pastedCells > 0) { if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: newData }); dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: newData });
} }
}); });
@@ -4561,18 +4595,6 @@ function renderCombinedPlaceholderTable(
} }
table.appendChild(tbody); table.appendChild(tbody);
// Focusout-Handler: Speichern wenn Fokus die Tabelle verlässt
let focusoutTimer = null;
table.addEventListener("focusout", (e) => {
if (focusoutTimer) clearTimeout(focusoutTimer);
focusoutTimer = setTimeout(() => {
const newFocus = document.activeElement;
if (!table.contains(newFocus)) {
saveLocalChanges();
}
}, 150);
});
// Paste-Handler für Excel-Daten // Paste-Handler für Excel-Daten
table.addEventListener("paste", (e) => { table.addEventListener("paste", (e) => {
e.preventDefault(); e.preventDefault();
@@ -4814,20 +4836,26 @@ function renderPlaceholderTable(
} }
table.appendChild(tbody); table.appendChild(tbody);
// Focusout-Handler mit Debounce - analog zur Empfängertabelle // Save-Funktion für Registry (wird bei Navigation und periodisch aufgerufen)
let focusoutTimer = null; let hasLocalChanges = false;
let hasLocalChanges = false; // Track ob lokale Änderungen gemacht wurden const saveLocalChanges = () => {
table.addEventListener("focusout", (e) => { if (hasLocalChanges) {
if (focusoutTimer) clearTimeout(focusoutTimer); dispatch({ type: "SET_PLACEHOLDER_VALUES", values: localValues });
focusoutTimer = setTimeout(() => { hasLocalChanges = false;
const newFocus = document.activeElement; }
if (!table.contains(newFocus) && hasLocalChanges) { };
// Nur dispatchen wenn lokale Änderungen gemacht wurden
dispatch({ type: "SET_PLACEHOLDER_VALUES", values: localValues }); // Bei globaler Registry registrieren
hasLocalChanges = false; const unregisterSave = registerTableSave(saveLocalChanges);
}
}, 150); // Auto-Save alle 60 Sekunden
}); const autoSaveInterval = setInterval(saveLocalChanges, 60000);
// Cleanup-Funktion
table._cleanup = () => {
if (autoSaveInterval) clearInterval(autoSaveInterval);
unregisterSave();
};
// Änderungen tracken bei Input // Änderungen tracken bei Input
table.addEventListener("input", () => { table.addEventListener("input", () => {
@@ -4881,6 +4909,7 @@ function renderPlaceholderTable(
// Einmal dispatch für alle Änderungen // Einmal dispatch für alle Änderungen
if (pastedCells > 0) { if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_PLACEHOLDER_VALUES", values: localValues }); dispatch({ type: "SET_PLACEHOLDER_VALUES", values: localValues });
} }
}); });

View File

@@ -29,19 +29,19 @@ function createInitialState() {
step: STEPS.PRODUCT, step: STEPS.PRODUCT,
customerType: null, // 'business' | 'private' customerType: null, // 'business' | 'private'
product: null, product: null,
// Calculator-Felder // Calculator-Felder - Defaults auf günstigste Optionen
quantity: 100, quantity: 100,
format: 'a6h', // a4, a6h (a6p), a6q (a6l) format: 'a6p', // a6p ist günstiger als a4 (kein Aufpreis)
shippingMode: 'direct', // direct, bulk shippingMode: 'bulk', // Sammelversand ist günstiger (nur Pauschale statt pro Stück)
envelope: true, // Bei Sammelversand: Kuvert ja/nein envelope: false, // Kein Kuvert ist günstiger
envelopeMode: 'recipientData', // recipientData, customText, none (Beschriftungsart) envelopeMode: 'none', // Keine Beschriftung ist günstiger
followupYearlyVolume: '50-199', followupYearlyVolume: '50-199',
followupCreateMode: 'manual', // auto, manual followupCreateMode: 'manual', // Manuell ist günstiger (keine API-Kosten)
// Motiv (nur für Postkarten/Einladungen mit A6) // Motiv (nur für Postkarten/Einladungen mit A6)
motifNeed: false, motifNeed: false, // Kein Motiv ist günstiger
motifSource: null, // upload, design motifSource: null, // upload, design
// Inhalt // Inhalt
contentCreateMode: 'self', // self, textservice contentCreateMode: 'self', // Selbst erstellen ist günstiger
}; };
} }
@@ -55,8 +55,14 @@ function reducer(state, action) {
}; };
case 'SET_PRODUCT': { case 'SET_PRODUCT': {
// Default-Format setzen // Default-Format: Günstigstes zuerst (A6 hat keinen Aufpreis bei Postkarten/Einladungen)
const defaultFormat = action.product.formats?.[0] || 'a4'; // Bei Produkten mit supportsMotif ist A6 günstiger, also bevorzugen
const formats = action.product.formats || [];
const hasA6 = formats.some(f => f === 'a6p' || f === 'a6l');
const hasA4 = formats.includes('a4');
// A6 bevorzugen wenn verfügbar (günstiger), sonst erstes Format
const defaultFormat = hasA6 ? 'a6p' : (formats[0] || 'a4');
// Default-Menge je nach Kundentyp // Default-Menge je nach Kundentyp
const settings = window.SkriftPreisrechner?.settings || window.SkriftConfigurator?.settings || {}; const settings = window.SkriftPreisrechner?.settings || window.SkriftConfigurator?.settings || {};
const dynamicPricing = settings.dynamic_pricing || {}; const dynamicPricing = settings.dynamic_pricing || {};