feat: edit mode + steal price entry

This commit is contained in:
2026-05-20 11:17:50 +02:00
parent 9a43ea96e4
commit a3fb217949
7 changed files with 2182 additions and 82 deletions

View File

@@ -122,6 +122,51 @@
};
}
function getSaveMonth(surcharges, editMode, editMonth) {
if (editMode) return editMonth;
return (
surcharges["316"]?.month ||
surcharges["304"]?.month ||
surcharges["444"]?.month ||
""
);
}
function cloneSurcharges(surcharges) {
return {
"304": {
...(surcharges["304"] || EMPTY_SURCHARGES["304"]),
},
"316": {
...(surcharges["316"] || EMPTY_SURCHARGES["316"]),
},
"444": {
...(surcharges["444"] || EMPTY_SURCHARGES["444"]),
},
};
}
function shouldUseEditedCurrentValues(
editMode,
editMonth,
editSnapshot,
) {
if (!editMode || !editSnapshot) return true;
return (
getSaveMonth(editSnapshot.surcharges, false, "") ===
editMonth
);
}
function buildSaveEntry(surcharges, baseSS, saveMonth) {
const entry = buildHistoryEntry(surcharges, baseSS);
return {
...entry,
m: saveMonth,
y: monthToYear(saveMonth),
};
}
function upsertHistory(history, entry) {
if (!entry.m) return history;
const idx = history.findIndex((d) => d.m === entry.m);
@@ -877,6 +922,10 @@
finalIdx: true,
});
const [editMode, setEditMode] = useState(false);
const [editMonth, setEditMonth] = useState("");
const [editSnapshot, setEditSnapshot] = useState(null);
useEffect(() => {
fetch(DATA_URL, { cache: "no-store" })
.then((res) => {
@@ -910,11 +959,16 @@
const updSC = (id, field, val) =>
setSurcharges((prev) => ({
...prev,
[id]: { ...(prev[id] || EMPTY_SURCHARGES[id]), [field]: val },
[id]: {
...(prev[id] || EMPTY_SURCHARGES[id]),
[field]: val,
},
}));
const downloadData = (data) => {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
@@ -923,12 +977,73 @@
URL.revokeObjectURL(url);
};
const saveData = async () => {
const entry = buildHistoryEntry(surcharges, baseSS);
const openEditMode = (month) => {
const entry = history.find((d) => d.m === month);
if (!entry) return;
if (!editSnapshot) {
setEditSnapshot({
baseSS: Number.isFinite(Number(baseSS))
? Number(baseSS)
: 0,
surcharges: cloneSurcharges(surcharges),
});
}
setEditMode(true);
setEditMonth(month);
setBaseSS(entry.ss != null ? entry.ss : 0);
setSurcharges({
"304": { month, value: entry.lz304 || 0 },
"316": { month, value: entry.lz316 || 0 },
"444": { month, value: entry.lz444 || 0 },
});
};
const finishEditMode = () => {
setEditMode(false);
setEditMonth("");
setEditSnapshot(null);
};
const startNewEntryMode = () => {
if (editSnapshot) {
setBaseSS(editSnapshot.baseSS);
setSurcharges(editSnapshot.surcharges);
}
finishEditMode();
};
const handleSaveData = async () => {
const saveMonth = getSaveMonth(
surcharges,
editMode,
editMonth,
);
const entry = buildSaveEntry(surcharges, baseSS, saveMonth);
const nextHistory = upsertHistory(history, entry);
const nextData = { surcharges, history: nextHistory, baseSS: Number(baseSS) || 0 };
const normalizedBaseSS = Number.isFinite(Number(baseSS))
? Number(baseSS)
: 0;
const useEditedCurrentValues = shouldUseEditedCurrentValues(
editMode,
editMonth,
editSnapshot,
);
const currentBaseSS = useEditedCurrentValues
? normalizedBaseSS
: editSnapshot.baseSS;
const currentSurcharges = useEditedCurrentValues
? surcharges
: editSnapshot.surcharges;
const nextData = {
surcharges: currentSurcharges,
history: nextHistory,
baseSS: currentBaseSS,
};
setBaseSS(currentBaseSS);
setSurcharges(currentSurcharges);
setHistory(nextHistory);
setSaveStatus("Speichere …");
try {
const res = await fetch("/api/data", {
method: "POST",
@@ -938,10 +1053,17 @@
if (!res.ok) throw new Error(`HTTP ${res.status}`);
setSaveStatus("Gespeichert in data.json.");
} catch (err) {
console.warn("Direktes Speichern nicht möglich, JSON wird heruntergeladen", err);
console.warn(
"Direktes Speichern nicht möglich, JSON wird heruntergeladen",
err,
);
downloadData(nextData);
setSaveStatus("Kein Schreibzugriff im statischen Browser-Modus. data.json wurde heruntergeladen; bitte Datei im Projekt ersetzen.");
setSaveStatus(
"Kein Schreibzugriff im statischen Browser-Modus. data.json wurde heruntergeladen; bitte Datei im Projekt ersetzen.",
);
}
finishEditMode();
};
const chartData = THICKNESSES.map((t) => ({
@@ -1066,6 +1188,7 @@
["methodology", "∑ Methodik"],
["surcharge", "◈ Legierungszuschlag"],
];
const historyMonthOptions = [...history].reverse();
return (
<div
@@ -2326,6 +2449,156 @@
paddingTop: 16,
}}
>
<div
style={{
display: "grid",
gridTemplateColumns:
"1.2fr 1fr",
gap: 12,
marginBottom: 12,
}}
>
<div
style={{
background: S2,
border: `1px solid ${BD}`,
padding: "10px 12px",
}}
>
<label style={lbl}>
Bestehenden Eintrag bearbeiten
</label>
<select
value={
editMode
? editMonth
: ""
}
onChange={(e) => {
const month =
e.target
.value;
if (!month) {
startNewEntryMode();
return;
}
openEditMode(
month,
);
}}
style={field}
>
<option value="">
Neuer Eintrag
</option>
{historyMonthOptions.map(
(entry) => (
<option
key={
entry.m
}
value={
entry.m
}
>
{
entry.m
}
</option>
),
)}
</select>
<div style={hint}>
Bestehende Monate
korrigieren oder neue
Werte erfassen
</div>
</div>
<div
style={{
background: S2,
border: `1px solid ${BD}`,
padding: "10px 12px",
}}
>
<label style={lbl}>
Basis Edelstahlpreis
(/t)
</label>
<input
type="number"
step="10"
value={baseSS}
onChange={(e) =>
setBaseSS(
parseFloat(
e
.target
.value,
) || 0,
)
}
style={field}
/>
<div style={hint}>
Wird in Verlauf als
Basispreis (ss)
gespeichert
</div>
</div>
</div>
{editMode && (
<div
style={{
display: "flex",
justifyContent:
"space-between",
alignItems:
"center",
gap: 12,
flexWrap: "wrap",
marginBottom: 12,
background: `${R}10`,
borderLeft: `3px solid ${R}`,
padding: "10px 12px",
}}
>
<div
style={{
color: TF,
fontSize:
"0.72rem",
fontWeight: 700,
}}
>
Bearbeitung: {editMonth}
</div>
<button
type="button"
onClick={
startNewEntryMode
}
style={{
background: "transparent",
border: `1px solid ${BD}`,
color: TH,
padding:
"6px 10px",
cursor: "pointer",
fontSize:
"0.64rem",
letterSpacing:
"0.08em",
textTransform:
"uppercase",
}}
>
Neuen Eintrag erfassen
</button>
</div>
)}
<div
style={{
fontSize: "0.58rem",
@@ -2336,9 +2609,9 @@
marginBottom: 12,
}}
>
Aktuellen Monat eintragen
wird sofort im Rechner
übernommen
{editMode
? "Korrigierte Monatswerte speichern"
: "Aktuellen Monat eintragen → wird sofort im Rechner übernommen"}
</div>
<div
style={{
@@ -2404,7 +2677,12 @@
value={
(surcharges[
g.id
] || EMPTY_SURCHARGES[g.id]).month
] ||
EMPTY_SURCHARGES[
g
.id
])
.month
}
onChange={(
e,
@@ -2441,7 +2719,12 @@
value={
(surcharges[
g.id
] || EMPTY_SURCHARGES[g.id]).value
] ||
EMPTY_SURCHARGES[
g
.id
])
.value
}
onChange={(
e,
@@ -2478,7 +2761,7 @@
>
<button
type="button"
onClick={saveData}
onClick={handleSaveData}
style={{
background: R,
color: "#fff",
@@ -2495,12 +2778,18 @@
</button>
<span
style={{
color: saveStatus.startsWith("Gespeichert") ? "#059669" : TH,
fontSize: "0.66rem",
color: saveStatus.startsWith(
"Gespeichert",
)
? "#059669"
: TH,
fontSize:
"0.66rem",
lineHeight: 1.6,
}}
>
{saveStatus || "Speichert den aktuellen Monat auch im Verlauf."}
{saveStatus ||
"Speichert den aktuellen Monat auch im Verlauf."}
</span>
</div>
</div>
@@ -2550,9 +2839,9 @@
ablesen (/t)
</li>
<li>
Im Tab oben Monat + Zuschlag
für alle drei Güten
eintragen
Im Tab oben Basispreis,
Monat + Zuschlag für alle
drei Güten eintragen
</li>
<li>
Button