Upload Neu

This commit is contained in:
s4luorth
2026-05-04 09:40:27 +02:00
parent a9c86c6dad
commit 290969f848
6 changed files with 499 additions and 291 deletions

View File

@@ -10,7 +10,7 @@ import {
normalizePlaceholderName,
validateStep,
getAvailableProductsForCustomerType,
} from "./configurator-state.js?ver=0.3.0";
} from "./configurator-state.js?ver=0.3.1";
import {
calculatePricePerPiece,
@@ -725,7 +725,16 @@ function renderTopbar(dom, state) {
if (state?.ctx?.product) {
productInfo.appendChild(
h("div", { class: "sk-product-icon", text: state.ctx.product.label[0] }),
state.ctx.product.imageUrl
? h("img", {
src: state.ctx.product.imageUrl,
alt: state.ctx.product.label,
class: "sk-product-icon sk-product-icon-img",
})
: h("div", {
class: "sk-product-icon",
text: state.ctx.product.label[0],
}),
);
productInfo.appendChild(
h("div", {}, [
@@ -835,7 +844,13 @@ function renderProductStep(state, dispatch) {
},
[
h("div", { class: "sk-selection-card-image" }, [
h("div", { text: "📄", style: "font-size: 48px" }), // Placeholder
p.imageUrl
? h("img", {
src: p.imageUrl,
alt: p.label,
class: "sk-selection-card-img",
})
: h("div", { text: "📄", style: "font-size: 48px" }),
]),
h("div", { class: "sk-selection-card-content" }, [
h("div", { class: "sk-selection-card-title", text: p.label }),
@@ -1059,20 +1074,6 @@ function renderQuantityStep(state, dispatch) {
h("div", { class: "sk-options" }, formatBody),
]),
);
} else if (formats.length === 1) {
// Auto-set single format (nur wenn noch nicht gesetzt)
if (!state.answers.format) {
dispatch({ type: "ANSWER", patch: { format: formats[0] } });
}
}
// Schriftart wird jetzt in Umschlag und Inhalt Steps separat gewählt (nicht hier)
// Default auf tilda wenn noch nicht gesetzt
if (!state.answers.font) {
dispatch({ type: "ANSWER", patch: { font: "tilda" } });
}
if (!state.answers.envelopeFont) {
dispatch({ type: "ANSWER", patch: { envelopeFont: "tilda" } });
}
// Follow-ups: Erstellungsmodus
@@ -2223,6 +2224,9 @@ async function handleOrderSubmit(state) {
finalizeResult = await api.finalizeOrder(api.sessionId, orderNumber, {
customer: state.order,
quote: state.quote,
letterFormat: { a4: "A4", a6p: "A6 Hochformat", a6l: "A6 Querformat" }[state.answers.format] || state.answers.format || null,
envelopeFormat: state.answers.format === "a4" ? "DIN Lang" : (state.answers.format === "a6p" || state.answers.format === "a6l") ? "C6" : null,
envelopeBeschriftung: { recipientData: "Empfängeradresse", customText: "Individueller Text", none: "Keine Beschriftung" }[state.answers.envelopeMode] || state.answers.envelopeMode || null,
});
console.log("[Order] Finalize result:", finalizeResult);
} catch (error) {
@@ -3356,6 +3360,9 @@ async function handlePayPalSuccess(state, paypalDetails) {
status: paypalDetails.status,
},
quote: state.quote,
letterFormat: { a4: "A4", a6p: "A6 Hochformat", a6l: "A6 Querformat" }[state.answers.format] || state.answers.format || null,
envelopeFormat: state.answers.format === "a4" ? "DIN Lang" : (state.answers.format === "a6p" || state.answers.format === "a6l") ? "C6" : null,
envelopeBeschriftung: { recipientData: "Empfängeradresse", customText: "Individueller Text", none: "Keine Beschriftung" }[state.answers.envelopeMode] || state.answers.envelopeMode || null,
});
console.log("[PayPal] Finalize result:", finalizeResult);
} catch (error) {
@@ -3671,6 +3678,7 @@ function createTableModal(dispatch, config) {
newData.push({});
}
// Werte in lokale Daten UND direkt ins DOM schreiben (kein Re-Render)
let pastedCells = 0;
rows.forEach((rowText, rowOffset) => {
const cells = rowText.split("\t");
@@ -3686,23 +3694,30 @@ function createTableModal(dispatch, config) {
if (!col.readOnly) {
if (!newData[targetRow]) newData[targetRow] = {};
newData[targetRow][col.key] = cellValue.trim();
// DOM direkt aktualisieren (kein Re-Render nötig)
const input = table.querySelector(
`input[data-row="${targetRow}"][data-key="${col.key}"]`,
);
if (input) input.value = cellValue.trim();
pastedCells++;
}
}
});
});
// Daten direkt in globalen State schreiben (OHNE Re-Render)
if (pastedCells > 0) {
config.setData(dispatch, newData);
// Tabelle im Modal aktualisieren
renderTable();
const globalState = window.currentGlobalState;
if (globalState && config.stateKey) {
globalState[config.stateKey] = newData;
}
markPersistDirty();
}
// Flag nach kurzem Delay zurücksetzen (für evtl. verzögerte input-Events)
setTimeout(() => {
isPasting = false;
}, 50);
// Flag zurücksetzen
isPasting = false;
});
return table;
@@ -3933,7 +3948,9 @@ function createTableModal(dispatch, config) {
{
class: "sk-modal",
onclick: (e) => {
if (e.target.classList.contains("sk-modal")) modal.remove();
if (e.target.classList.contains("sk-modal")) {
modal.remove();
}
},
},
[modalContent],
@@ -4013,6 +4030,12 @@ function renderRecipientsTable(state, dispatch) {
};
}
currentRows[idx][key] = e.target.value;
// Auch globalen State aktualisieren für SYNC_GLOBAL_STATE
const globalState = window.currentGlobalState;
if (globalState) {
globalState.recipientRows = currentRows;
}
},
}),
]);
@@ -4040,6 +4063,12 @@ function renderRecipientsTable(state, dispatch) {
};
}
currentRows[idx].country = e.target.value;
// Auch globalen State aktualisieren für SYNC_GLOBAL_STATE
const globalState = window.currentGlobalState;
if (globalState) {
globalState.recipientRows = currentRows;
}
},
}),
]);
@@ -4085,7 +4114,7 @@ function renderRecipientsTable(state, dispatch) {
hasLocalChanges = true;
});
// Paste-Handler
// Paste-Handler - Daten direkt in globalen State schreiben (OHNE Re-Render)
table.addEventListener("paste", (e) => {
e.preventDefault();
const text = e.clipboardData.getData("text/plain");
@@ -4121,25 +4150,30 @@ function renderRecipientsTable(state, dispatch) {
"city",
"country",
];
const currentRows = table._rows;
const newData = [];
for (let i = 0; i < want; i++) {
newData.push(
currentRows[i]
? { ...currentRows[i] }
: {
firstName: "",
lastName: "",
street: "",
houseNumber: "",
zip: "",
city: "",
country: isDirectShipping ? "Deutschland" : "",
},
);
// Aktuellen globalen State holen
const globalState = window.currentGlobalState;
if (!globalState) return;
// Kopie der aktuellen Rows erstellen
const currentRows = Array.isArray(globalState.recipientRows)
? globalState.recipientRows.map((r) => ({ ...r }))
: [];
// Sicherstellen dass genug Zeilen existieren
while (currentRows.length < want) {
currentRows.push({
firstName: "",
lastName: "",
street: "",
houseNumber: "",
zip: "",
city: "",
country: isDirectShipping ? "Deutschland" : "",
});
}
let pastedCells = 0;
// Werte in Daten UND direkt ins DOM schreiben
pasteRows.forEach((rowText, rowOffset) => {
const targetRow = startRow + rowOffset;
if (targetRow >= want) return;
@@ -4149,15 +4183,35 @@ function renderRecipientsTable(state, dispatch) {
const targetCol = startCol + colOffset;
if (targetCol >= keys.length) return;
const key = keys[targetCol];
newData[targetRow][key] = cellValue;
pastedCells++;
// Daten aktualisieren
if (!currentRows[targetRow]) {
currentRows[targetRow] = {
firstName: "",
lastName: "",
street: "",
houseNumber: "",
zip: "",
city: "",
country: isDirectShipping ? "Deutschland" : "",
};
}
currentRows[targetRow][key] = cellValue;
// DOM direkt aktualisieren (kein Re-Render nötig)
const input = table.querySelector(
`input[data-row="${targetRow}"][data-col="${key}"]`,
);
if (input) input.value = cellValue;
});
});
if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_RECIPIENT_ROWS", rows: newData });
}
// Globalen State DIREKT aktualisieren (OHNE dispatch/render)
globalState.recipientRows = currentRows;
table._rows = currentRows;
// Für Persistenz markieren
markPersistDirty();
});
return table;
@@ -4174,6 +4228,9 @@ function renderRecipientsTable(state, dispatch) {
{ class: "sk-table sk-table-pasteable", tabindex: "0" },
[],
);
// Rows-Referenz am Table speichern für Event-Handler
table._rows = rows;
table.appendChild(
h("thead", {}, [
h("tr", {}, [
@@ -4200,8 +4257,9 @@ function renderRecipientsTable(state, dispatch) {
"data-col": key,
"data-sk-focus": `freeaddr.${key}.${idx}`,
oninput: (e) => {
if (!rows[idx]) {
rows[idx] = {
const currentRows = table._rows;
if (!currentRows[idx]) {
currentRows[idx] = {
line1: "",
line2: "",
line3: "",
@@ -4209,7 +4267,13 @@ function renderRecipientsTable(state, dispatch) {
line5: "",
};
}
rows[idx][key] = e.target.value;
currentRows[idx][key] = e.target.value;
// Auch globalen State aktualisieren für SYNC_GLOBAL_STATE
const globalState = window.currentGlobalState;
if (globalState) {
globalState.freeAddressRows = currentRows;
}
},
}),
]);
@@ -4231,7 +4295,7 @@ function renderRecipientsTable(state, dispatch) {
let hasLocalChanges = false;
const saveLocalChanges = () => {
if (hasLocalChanges) {
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: [...rows] });
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: [...table._rows] });
hasLocalChanges = false;
}
};
@@ -4253,7 +4317,7 @@ function renderRecipientsTable(state, dispatch) {
hasLocalChanges = true;
});
// Paste-Handler
// Paste-Handler - Daten direkt in globalen State schreiben (OHNE Re-Render)
table.addEventListener("paste", (e) => {
e.preventDefault();
const text = e.clipboardData.getData("text/plain");
@@ -4281,16 +4345,28 @@ function renderRecipientsTable(state, dispatch) {
}
const keys = ["line1", "line2", "line3", "line4", "line5"];
const newData = [];
for (let i = 0; i < want; i++) {
newData.push(
rows[i]
? { ...rows[i] }
: { line1: "", line2: "", line3: "", line4: "", line5: "" },
);
// Aktuellen globalen State holen
const globalState = window.currentGlobalState;
if (!globalState) return;
// Kopie der aktuellen Rows erstellen
const currentRows = Array.isArray(globalState.freeAddressRows)
? globalState.freeAddressRows.map((r) => ({ ...r }))
: [];
// Sicherstellen dass genug Zeilen existieren
while (currentRows.length < want) {
currentRows.push({
line1: "",
line2: "",
line3: "",
line4: "",
line5: "",
});
}
let pastedCells = 0;
// Werte in Daten UND direkt ins DOM schreiben
pasteRows.forEach((rowText, rowOffset) => {
const targetRow = startRow + rowOffset;
if (targetRow >= want) return;
@@ -4299,15 +4375,34 @@ function renderRecipientsTable(state, dispatch) {
cells.forEach((cellValue, colOffset) => {
const targetCol = startCol + colOffset;
if (targetCol >= keys.length) return;
newData[targetRow][keys[targetCol]] = cellValue;
pastedCells++;
const key = keys[targetCol];
// Daten aktualisieren
if (!currentRows[targetRow]) {
currentRows[targetRow] = {
line1: "",
line2: "",
line3: "",
line4: "",
line5: "",
};
}
currentRows[targetRow][key] = cellValue;
// DOM direkt aktualisieren (kein Re-Render nötig)
const input = table.querySelector(
`input[data-row="${targetRow}"][data-col="${key}"]`,
);
if (input) input.value = cellValue;
});
});
if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: newData });
}
// Globalen State DIREKT aktualisieren (OHNE dispatch/render)
globalState.freeAddressRows = currentRows;
table._rows = currentRows;
// Für Persistenz markieren
markPersistDirty();
});
return table;
@@ -4346,6 +4441,7 @@ function renderRecipientsTable(state, dispatch) {
setData: (dispatch, data) => {
dispatch({ type: "SET_FREE_ADDRESS_ROWS", rows: data });
},
stateKey: "freeAddressRows",
rowCount: () => want,
exportFilename: "freie_adressen.csv",
helpText: "Geben Sie bis zu 5 Zeilen pro Empfänger ein.",
@@ -4390,6 +4486,7 @@ function renderRecipientsTable(state, dispatch) {
setData: (dispatch, data) => {
dispatch({ type: "SET_RECIPIENT_ROWS", rows: data });
},
stateKey: "recipientRows",
rowCount: () => want,
exportFilename: "empfaenger.csv",
helpText:
@@ -4585,6 +4682,22 @@ function renderCombinedPlaceholderTable(
// Nur lokal speichern - kein Re-Render!
localValues[name][row] = e.target.value;
hasLocalChanges = true;
// Auch globalen State aktualisieren für SYNC_GLOBAL_STATE
const globalState = window.currentGlobalState;
if (globalState) {
if (!globalState.placeholderValues) {
globalState.placeholderValues = {};
}
if (!globalState.placeholderValues[name]) {
globalState.placeholderValues[name] = [];
}
globalState.placeholderValues[name][row] = e.target.value;
// Array auf requiredRowCount-Länge bringen (für validateStep)
while (globalState.placeholderValues[name].length < want) {
globalState.placeholderValues[name].push("");
}
}
}
},
}),
@@ -4624,7 +4737,16 @@ function renderCombinedPlaceholderTable(
}
}
// Alle Änderungen in localValues sammeln und Inputs aktualisieren
// Aktuellen globalen State holen
const globalState = window.currentGlobalState;
if (!globalState) return;
// Kopie der aktuellen Platzhalter-Werte erstellen
const currentValues = globalState.placeholderValues
? JSON.parse(JSON.stringify(globalState.placeholderValues))
: {};
// Alle Änderungen in currentValues sammeln und Inputs aktualisieren
let pastedCells = 0;
pasteRows.forEach((rowText, rowOffset) => {
const targetRow = startRow + rowOffset;
@@ -4640,7 +4762,11 @@ function renderCombinedPlaceholderTable(
// Nur editierbare Spalten aktualisieren
if (!isReadOnly) {
// Daten aktualisieren
if (!currentValues[name]) currentValues[name] = [];
currentValues[name][targetRow] = cellValue;
localValues[name][targetRow] = cellValue;
// Input-Element direkt aktualisieren
const input = table.querySelector(
`input[data-row="${targetRow}"][data-col="${name}"]`,
@@ -4651,10 +4777,15 @@ function renderCombinedPlaceholderTable(
});
});
// Nach Paste: Einmal speichern
// Globalen State DIREKT aktualisieren (OHNE dispatch/render)
if (pastedCells > 0) {
hasLocalChanges = true;
saveLocalChanges();
// Arrays auf want-Länge normalisieren (für validateStep)
for (const name of Object.keys(currentValues)) {
while (currentValues[name].length < want)
currentValues[name].push("");
}
globalState.placeholderValues = currentValues;
markPersistDirty();
}
});
@@ -4827,6 +4958,22 @@ function renderPlaceholderTable(
oninput: (e) => {
// Wert nur lokal speichern (kein Re-Render)
localValues[name][row] = e.target.value;
// Auch globalen State aktualisieren für SYNC_GLOBAL_STATE
const globalState = window.currentGlobalState;
if (globalState) {
if (!globalState.placeholderValues) {
globalState.placeholderValues = {};
}
if (!globalState.placeholderValues[name]) {
globalState.placeholderValues[name] = [];
}
globalState.placeholderValues[name][row] = e.target.value;
// Array auf requiredRowCount-Länge bringen (für validateStep)
while (globalState.placeholderValues[name].length < want) {
globalState.placeholderValues[name].push("");
}
}
},
}),
]),
@@ -4890,7 +5037,16 @@ function renderPlaceholderTable(
}
}
// Alle Änderungen in localValues sammeln
// Aktuellen globalen State holen
const globalState = window.currentGlobalState;
if (!globalState) return;
// Kopie der aktuellen Platzhalter-Werte erstellen
const currentValues = globalState.placeholderValues
? JSON.parse(JSON.stringify(globalState.placeholderValues))
: {};
// Alle Änderungen sammeln UND direkt ins DOM schreiben
let pastedCells = 0;
pasteRows.forEach((rowText, rowOffset) => {
const targetRow = startRow + rowOffset;
@@ -4902,15 +5058,31 @@ function renderPlaceholderTable(
if (targetCol >= clean.length) return;
const name = clean[targetCol];
// Daten aktualisieren
if (!currentValues[name]) currentValues[name] = [];
currentValues[name][targetRow] = cellValue;
localValues[name][targetRow] = cellValue;
// DOM direkt aktualisieren (kein Re-Render nötig)
const input = table.querySelector(
`input[data-row="${targetRow}"][data-col="${name}"]`,
);
if (input) input.value = cellValue;
pastedCells++;
});
});
// Einmal dispatch für alle Änderungen
// Globalen State DIREKT aktualisieren (OHNE dispatch/render)
if (pastedCells > 0) {
hasLocalChanges = false; // Paste übernimmt - keine alten Änderungen mehr relevant
dispatch({ type: "SET_PLACEHOLDER_VALUES", values: localValues });
// Arrays auf want-Länge normalisieren (für validateStep)
for (const name of Object.keys(currentValues)) {
while (currentValues[name].length < want)
currentValues[name].push("");
}
globalState.placeholderValues = currentValues;
markPersistDirty();
}
});