From 98db3339a2adcab8db8cf46dcd79eb1371fdd5b5 Mon Sep 17 00:00:00 2001 From: Scarriffle Date: Fri, 2 Oct 2026 15:39:44 +0200 Subject: [PATCH] Vier Anzeigefehler aus dem Gebrauch "NaN Glaeser" auf der Karte "Artikel im Blick". Die Karte reichte einen ARTIKEL an articlePrimary() weiter, das fuer CHARGEN geschrieben ist und die Menge unter dem Namen `quantity` sucht. Ein Artikel nennt sie `stock` - also undefined / 190 = NaN. Neu bestandPrimary() und bestandSecondary() in units.js als die eine Stelle, an der zwischen den beiden Namen uebersetzt wird; sie delegieren an die vorhandenen Formatierer, damit es nicht zwei Fassungen gibt. Steckte seit 8959814 drin. Dazu gibt fmt() fuer nicht-endliche Zahlen jetzt einen Strich aus statt "NaN". Dem Benutzer "NaN" hinzuschreiben ist nie richtig - der eigentliche Fehler wird trotzdem an der Quelle behoben, nicht hier. Die Spalte "Verbrauch" schnitt ab. Zwei beschriftete Knoepfe brauchen rund 230 px, die Spalte hatte 190, und .datatable ist table-layout:fixed mit overflow:hidden. Weil die Zelle rechtsbuendig ist, fiel der ANFANG weg - sichtbar stand dort "rbrauchen". Jetzt 240 px, dazu flex-wrap in der Zelle, damit die Knoepfe umbrechen statt abzuschneiden, wenn jemand die Spalte von Hand schmaler zieht. Die LETZTE Spalte liess sich nicht verbreitern. Ihr Anfasser sitzt exakt am rechten Rand des Rollbereichs (gemessen: 0 px Luft), und nach rechts ist dort Schluss - der Zeiger kann nicht weiter, also kam nie ein positiver Abstand zum Startpunkt zustande. Der Resize rechnet jetzt mit der BEWEGUNG seit dem letzten Schritt statt mit dem Abstand zum Start, und in der Randzone waechst die Spalte von selbst weiter, solange die Taste haelt; ein Taktgeber treibt das an, denn ein stehender Zeiger meldet keine Bewegung. Die Tabelle rollt dabei mit, damit der Anfasser unter dem Zeiger bleibt. "3 Glas" statt "3 Glaeser" in der Chargenliste, waehrend die Uebersicht "3 Glaeser" schrieb. Die Mehrzahlformen liegen in einer Modulvariablen, weil auch Nicht-Komponenten sie brauchen - eine Aenderung daran loest aber kein Neuzeichnen aus. Wer seine Liste zeichnete, bevor /package-types zurueck war, blieb bei der Einzahl. Ein Zaehler im Einstellungs-Kontext zieht die Formatierer jetzt nach. Von Hand geprueft: Karte zeigt "3 Glaeser / 570 g"; beide Knoepfe stehen vollstaendig in der Spalte; die letzte Spalte waechst beim Halten am Rand von 240 auf ueber 470 und bleibt beim Loslassen stehen, waehrend eine mittlere Spalte weiterhin exakt der Ziehstrecke folgt (170 -> 230); Chargenliste schreibt "3 Glaeser". Co-Authored-By: Claude Opus 5 --- web/src/components/DataTable.jsx | 55 ++++++++++++++++++++++++++++---- web/src/dashboard/cards.jsx | 9 ++++-- web/src/pages/Charges.jsx | 9 ++++-- web/src/settings.jsx | 10 ++++++ web/src/units.js | 24 +++++++++++++- 5 files changed, 95 insertions(+), 12 deletions(-) diff --git a/web/src/components/DataTable.jsx b/web/src/components/DataTable.jsx index 7d84b52..3803b42 100644 --- a/web/src/components/DataTable.jsx +++ b/web/src/components/DataTable.jsx @@ -265,34 +265,77 @@ export default function DataTable({ } // --- Resize (Pointer) --- + // + // Gerechnet wird mit der BEWEGUNG seit dem letzten Schritt, nicht mit dem + // Abstand zum Startpunkt. Nur so laesst sich die letzte Spalte verbreitern: + // Ihr Anfasser sitzt am rechten Rand, und dort ist fuer den Zeiger Schluss - + // nach rechts gibt es kein Fenster mehr. Steht er in der Randzone, waechst die + // Spalte deshalb von selbst weiter, Schritt fuer Schritt, solange die Taste + // haelt. Ein Taktgeber treibt das an, denn ein stehender Zeiger meldet keine + // Bewegung mehr. const resizing = useRef(null); + const rollTimer = useRef(null); + const RAND = 36; // Breite der Randzone + const SCHRITT = 14; // Pixel je Takt in der Randzone + function startResize(e, c) { e.preventDefault(); e.stopPropagation(); - const startW = widthOf(c); // Die flexible (grow-)Spalte auf ihre aktuelle Pixelbreite einfrieren. Sonst - // "klaut" das Breiterziehen einer Spalte der grow-Spalte den Platz – dann + // "klaut" das Breiterziehen einer Spalte der grow-Spalte den Platz - dann // schrumpft die Nachbarspalte und alles wandert nach links. Danach verhalten - // sich die Spalten wie in Excel (unabhängig, notfalls scrollt die Tabelle). + // sich die Spalten wie in Excel (unabhaengig, notfalls scrollt die Tabelle). setWidths((old) => { const next = { ...old }; for (const gc of growCols) if (next[gc.key] == null) next[gc.key] = Math.round(widthOf(gc)); return next; }); - resizing.current = { key: c.key, startX: e.clientX, startW }; + resizing.current = { key: c.key, breite: widthOf(c), zeigerX: e.clientX, letzterX: e.clientX }; + rollTimer.current = window.setInterval(anwenden, 30); window.addEventListener("pointermove", onResizeMove); window.addEventListener("pointerup", endResize); } + + function anwenden() { + const r = resizing.current; if (!r) return; + let delta = r.zeigerX - r.letzterX; + r.letzterX = r.zeigerX; + + const el = wrapRef.current; + let amRand = false; + if (el) { + const kasten = el.getBoundingClientRect(); + if (r.zeigerX > kasten.right - RAND) { delta += SCHRITT; amRand = true; } + else if (r.zeigerX < kasten.left + RAND) delta -= SCHRITT; + } + if (!delta) return; + + const neu = Math.max(60, r.breite + delta); + if (neu === r.breite) return; + r.breite = neu; + setWidths((old) => ({ ...old, [r.key]: Math.round(neu) })); + // Mitlaufen, damit der Anfasser unter dem Zeiger bleibt, waehrend die + // Tabelle nach rechts waechst. + if (amRand && el) requestAnimationFrame(() => { el.scrollLeft = el.scrollWidth; }); + } + function onResizeMove(e) { const r = resizing.current; if (!r) return; - const w = Math.max(60, Math.round(r.startW + (e.clientX - r.startX))); - setWidths((old) => ({ ...old, [r.key]: w })); + r.zeigerX = e.clientX; + anwenden(); } + function endResize() { resizing.current = null; + if (rollTimer.current) { window.clearInterval(rollTimer.current); rollTimer.current = null; } window.removeEventListener("pointermove", onResizeMove); window.removeEventListener("pointerup", endResize); } + // Beim Verlassen der Seite mitten im Ziehen bliebe sonst ein Taktgeber laufen. + useEffect(() => () => { + if (rollTimer.current) window.clearInterval(rollTimer.current); + }, []); + // --- Reorder (HTML5 Drag) --- const dragKey = useRef(null); const [dropKey, setDropKey] = useState(null); diff --git a/web/src/dashboard/cards.jsx b/web/src/dashboard/cards.jsx index 7dff2fc..4fea064 100644 --- a/web/src/dashboard/cards.jsx +++ b/web/src/dashboard/cards.jsx @@ -8,7 +8,7 @@ import BewegungIcon from "../components/BewegungIcon"; import { FristBadge, verleihRowClass } from "../components/Verleih"; import { useSettings } from "../settings"; import { - articlePrimary, articleSecondary, fmt, relativeExpiry, + articlePrimary, articleSecondary, bestandPrimary, bestandSecondary, fmt, relativeExpiry, } from "../units"; import Bars from "./charts/Bars"; import Donut from "./charts/Donut"; @@ -192,9 +192,12 @@ function KarteArtikel({ props: karteProps }) {
{daten.name}
-
{articlePrimary(daten)}
+ {/* Artikel, nicht Charge: Der Bestand heisst hier ``stock``. + ``articlePrimary`` sucht ``quantity`` und lieferte deshalb + "NaN Glaeser". */} +
{bestandPrimary(daten)}
-
{articleSecondary(daten)}
+
{bestandSecondary(daten)}
)} diff --git a/web/src/pages/Charges.jsx b/web/src/pages/Charges.jsx index 5d0725e..eeedfcd 100644 --- a/web/src/pages/Charges.jsx +++ b/web/src/pages/Charges.jsx @@ -254,9 +254,14 @@ export default function Charges() { { key: "created", header: "Eingelagert", width: 130, sortValue: (l) => new Date(l.created_at).getTime(), render: (l) => {new Date(l.created_at).toLocaleDateString("de-DE")} }, - { key: "verbrauch", header: "Verbrauch", label: "Verbrauch", width: 190, align: "num", + // 240 statt 190: Zwei beschriftete Knoepfe brauchen den Platz, und die + // Tabelle schneidet ab statt umzubrechen (.datatable ist table-layout:fixed + // mit overflow:hidden). Bei rechtsbuendigen Zellen faellt dabei der ANFANG + // weg - aus "Verbrauchen" wurde sichtbar "rbrauchen". Das Umbrechen unten + // faengt ab, wenn jemand die Spalte von Hand schmaler zieht. + { key: "verbrauch", header: "Verbrauch", label: "Verbrauch", width: 240, align: "num", render: (l) => ( - +