feat: edit mode + steal price entry
This commit is contained in:
327
index.html
327
index.html
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user