Exportbedingungen: Vergleichsart je Feldart statt nacktem Textfeld
Bisher tippte man den Wert immer von Hand ein - auch bei einem Haken, wo man wissen musste, dass intern "true" steht, und bei einer Auswahl, wo ein Vertipper stillschweigend nichts fand. Das ist der unangenehmste Fehlschlag: Die CSV kommt einfach leer heraus, ohne Hinweis warum. Jetzt bringt jede Bedingung die passende Eingabe mit: Ja/Nein ein Haken Auswahl "ist gleich" / "ist nicht gleich" plus das Dropdown der Optionen Zahl "ist groesser =" / "ist =" / "ist kleiner =" plus Zahlenfeld Text wie bisher freie Eingabe, leer = ohne Wert Damit die Oberflaeche das kann, traegt /etiketten/spalten jetzt Art und Auswahlwerte je Feld mit - vorher gab es nur Schluessel und Titel. Der Filter bekommt dafuer ein op (=, !=, >=, <=), Vorgabe "=", damit bestehende Vorlagen unveraendert weiterlaufen. Groesser/kleiner vergleicht als Zahl und nimmt Komma wie Punkt; Zeilen ohne Wert treffen dabei NIE - "leer" ist nicht kleiner als drei, es ist unvergleichbar. Eine unbekannte Vergleichsart faellt auf "=" zurueck statt den Export zu zerreissen. Die Sicherung traegt das op mit, wie schon das Feld selbst. Sieben neue Tests, 547 gruen. Diesmal zusaetzlich in der laufenden Oberflaeche durchgespielt: Alle vier Feldarten zeigen die richtige Eingabe, der Filter wird gespeichert, und die CSV enthaelt bei "Laenge >= 5" genau die Artikel mit 5 und 12 - nicht den mit 2 und nicht den ohne Wert. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
dd57d3356a
commit
8de222072c
@@ -155,6 +155,65 @@ export default function EtikettenVorlagen({ onError }) {
|
||||
}
|
||||
|
||||
/** Bearbeiten einer einzelnen Vorlage. */
|
||||
/**
|
||||
* Die Eingabe einer Filterbedingung, passend zur Art des Feldes.
|
||||
*
|
||||
* Ohne das waere jede Bedingung ein nacktes Textfeld: Man muesste wissen, dass
|
||||
* ein Haken intern „true" heisst und wie eine Auswahl genau geschrieben ist.
|
||||
* Ein Vertipper faende dann stillschweigend nichts – der unangenehmste
|
||||
* Fehlschlag, weil die CSV einfach leer herauskommt.
|
||||
*/
|
||||
function FilterEingabe({ feld, bedingung, onAendern }) {
|
||||
const typ = feld?.typ || "text";
|
||||
|
||||
if (typ === "boolean") {
|
||||
return (
|
||||
<label className="check-inline" style={{ margin: 0 }}>
|
||||
<input type="checkbox" checked={bedingung.wert === "true"}
|
||||
onChange={(e) => onAendern({ op: "=", wert: e.target.checked ? "true" : "false" })} />
|
||||
<span className="muted small">{bedingung.wert === "true" ? "ja" : "nein"}</span>
|
||||
</label>
|
||||
);
|
||||
}
|
||||
|
||||
if (typ === "select") {
|
||||
return (
|
||||
<>
|
||||
<select value={bedingung.op} style={{ width: 110 }}
|
||||
onChange={(e) => onAendern({ op: e.target.value })}>
|
||||
<option value="=">ist gleich</option>
|
||||
<option value="!=">ist nicht gleich</option>
|
||||
</select>
|
||||
<select value={bedingung.wert} style={{ width: 120 }}
|
||||
onChange={(e) => onAendern({ wert: e.target.value })}>
|
||||
{(feld.optionen || []).map((o) => <option key={o} value={o}>{o}</option>)}
|
||||
</select>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
if (typ === "number") {
|
||||
return (
|
||||
<>
|
||||
<select value={bedingung.op} style={{ width: 110 }}
|
||||
onChange={(e) => onAendern({ op: e.target.value })}>
|
||||
<option value=">=">ist größer =</option>
|
||||
<option value="=">ist =</option>
|
||||
<option value="<=">ist kleiner =</option>
|
||||
</select>
|
||||
<input value={bedingung.wert} inputMode="decimal" style={{ width: 90 }}
|
||||
onChange={(e) => onAendern({ wert: e.target.value })} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
// Text, mehrzeiliger Text und Datum: freie Eingabe, leer = ohne Wert.
|
||||
return (
|
||||
<input value={bedingung.wert} placeholder="ohne Wert" style={{ width: 120 }}
|
||||
onChange={(e) => onAendern({ wert: e.target.value })} />
|
||||
);
|
||||
}
|
||||
|
||||
function VorlageEditor({ vorlage, onGespeichert, onError }) {
|
||||
const toast = useToast();
|
||||
const [art, setArt] = useState(vorlage.art);
|
||||
@@ -265,18 +324,21 @@ function VorlageEditor({ vorlage, onGespeichert, onError }) {
|
||||
Nur Zeilen mit bestimmten Feldwerten (alle Bedingungen müssen zutreffen)
|
||||
</span>
|
||||
<ul className="clean-list" style={{ marginTop: "var(--sp-2)" }}>
|
||||
{feldfilter.map((f, i) => (
|
||||
<li key={f.feld_id}>
|
||||
<span style={{ flex: 1, minWidth: 0 }}>{titelVon(`feld:${f.feld_id}`)}</span>
|
||||
<input value={f.wert} placeholder="ohne Wert" style={{ width: 120 }}
|
||||
onChange={(e) => setFeldfilter(feldfilter.map((x, j) =>
|
||||
(j === i ? { ...x, wert: e.target.value } : x)))} />
|
||||
<button type="button" className="btn-icon danger" title="Bedingung entfernen"
|
||||
onClick={() => setFeldfilter(feldfilter.filter((_, j) => j !== i))}>
|
||||
<Icon name="close" size={14} />
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
{feldfilter.map((f, i) => {
|
||||
const feld = moeglich.find((m) => m.schluessel === `feld:${f.feld_id}`);
|
||||
const aendern = (teil) => setFeldfilter(feldfilter.map((x, j) =>
|
||||
(j === i ? { ...x, ...teil } : x)));
|
||||
return (
|
||||
<li key={f.feld_id}>
|
||||
<span style={{ flex: 1, minWidth: 0 }}>{titelVon(`feld:${f.feld_id}`)}</span>
|
||||
<FilterEingabe feld={feld} bedingung={f} onAendern={aendern} />
|
||||
<button type="button" className="btn-icon danger" title="Bedingung entfernen"
|
||||
onClick={() => setFeldfilter(feldfilter.filter((_, j) => j !== i))}>
|
||||
<Icon name="close" size={14} />
|
||||
</button>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
<select value="" style={{ marginTop: "var(--sp-1)" }}
|
||||
onChange={(e) => {
|
||||
@@ -284,8 +346,13 @@ function VorlageEditor({ vorlage, onGespeichert, onError }) {
|
||||
if (!id) return;
|
||||
// Ein Feld nur einmal: zwei Bedingungen darauf koennten sich nie
|
||||
// beide erfuellen.
|
||||
const feld = moeglich.find((m) => m.schluessel === e.target.value);
|
||||
// Ja/Nein startet als gesetzter Haken, eine Auswahl auf ihrem
|
||||
// ersten Wert - so steht nie eine Bedingung da, die nichts trifft.
|
||||
const start = feld?.typ === "boolean" ? "true"
|
||||
: feld?.typ === "select" ? (feld.optionen?.[0] || "") : "";
|
||||
setFeldfilter([...feldfilter.filter((x) => x.feld_id !== id),
|
||||
{ feld_id: id, wert: "" }]);
|
||||
{ feld_id: id, op: "=", wert: start }]);
|
||||
}}>
|
||||
<option value="">+ Bedingung auf einem eigenen Feld …</option>
|
||||
{moeglich.filter((m) => m.schluessel.startsWith("feld:")).map((m) => (
|
||||
@@ -293,9 +360,10 @@ function VorlageEditor({ vorlage, onGespeichert, onError }) {
|
||||
))}
|
||||
</select>
|
||||
<p className="muted small">
|
||||
Wert genau so eingeben, wie er im Feld steht; bei Ja/Nein-Feldern
|
||||
„ja" oder „nein". <strong>Leer heißt: ohne Wert</strong> – also alle,
|
||||
bei denen das Feld nicht ausgefüllt ist.
|
||||
Bei Textfeldern genau so eingeben, wie es im Feld steht;{" "}
|
||||
<strong>leer heißt dort: ohne Wert</strong> – also alle, bei denen
|
||||
das Feld nicht ausgefüllt ist. Größer/kleiner vergleicht als Zahl;
|
||||
Zeilen ohne Wert treffen dabei nie.
|
||||
</p>
|
||||
</>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user