Verlauf: klarere Bewegungs-Icons, Spalte nach vorn

Die beiden Kisten unterschieden sich nur in einem kleinen Pfeil. Bei 18 px war
das nicht zu trennen - erkannt hat man sie allein an der Farbe, und die war
gerade wegen der Wertung (gruen rein, orange raus) rausgeflogen. Ohne Farbe
blieb nichts uebrig, woran man sie auseinanderhaelt.

Jetzt die Strich-Icons checkin/checkout/edit: grosser Pfeil auf eine Linie zu
bzw. von ihr weg, Stift fuer die Korrektur. Dieselben Zeichen wie auf den
Ein-/Auslagern-Knoepfen, also schon vertraut, und auch klein eindeutig - die
Richtung liegt im Zeichen statt in der Farbe.

Auf der Verlaufsseite steht die Spalte jetzt an erster Stelle; sie sagt in
einem Blick worum es geht und braucht als Zeichen kaum Breite.

Der gefuellte Icon-Zweig in Icon.jsx wird damit nicht mehr gebraucht und ist
raus. Die SVG-Quellen bleiben unter brand/icons/ liegen - das README sagt jetzt
ausdruecklich, dass sie ungenutzt sind, warum, und wie man sie zurueckholt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-08-16 12:48:25 +02:00
parent e9738e28e7
commit ad3100462b
5 changed files with 41 additions and 106 deletions

View File

@@ -8,9 +8,8 @@ Name `box`) in Weiß auf der Akzentfläche.
- **`app-icon.svg`** 1024×1024, vollflächig, feste Farbe (`#4d5cd4`), ohne - **`app-icon.svg`** 1024×1024, vollflächig, feste Farbe (`#4d5cd4`), ohne
abgerundete Ecken. Für App-Icon (iOS/macOS via Icon Composer oder Xcode abgerundete Ecken. Für App-Icon (iOS/macOS via Icon Composer oder Xcode
„AppIcon", App Store). Die Rundung macht das System selbst. „AppIcon", App Store). Die Rundung macht das System selbst.
- **`icons/`** Quellgrafiken der gefüllten Icons (Ein-/Auslagern, Korrektur). - **`icons/`** Quellgrafiken für Ein-/Auslagern und Korrektur. Derzeit nicht
Ihre Pfaddaten stehen inline in `../web/src/components/Icon.jsx`; siehe im Einsatz; siehe `icons/README.md`.
`icons/README.md`.
- **Favicon / Web-Logo:** `../web/public/favicon.svg` dasselbe Zeichen mit - **Favicon / Web-Logo:** `../web/public/favicon.svg` dasselbe Zeichen mit
abgerundeter Platte und Hell-/Dunkel-Umschaltung (für den Browser-Tab). abgerundeter Platte und Hell-/Dunkel-Umschaltung (für den Browser-Tab).

View File

@@ -1,38 +1,32 @@
# Icon-Quellen # Icon-Quellen
Hier liegen zugekaufte/heruntergeladene Grafiken, deren Pfaddaten **inline** in > **Derzeit nicht im Einsatz.** Die Verlaufsansicht nutzt die Strich-Icons
`web/src/components/Icon.jsx` stehen (Abschnitt `GEFUELLT`). Zur Laufzeit lädt > `checkin` / `checkout` / `edit` aus `web/src/components/Icon.jsx` dieselben
niemand diese Dateien sie sind die Quelle, nicht das Auslieferungsformat. > wie die Ein-/Auslagern-Knöpfe.
>
Warum inline statt `<img src>`: Die Icons sollen die Textfarbe annehmen > Grund: Die beiden Kisten unterscheiden sich **nur in der Pfeilrichtung**. Bei
(`fill="currentColor"`), damit sie sich mit Thema und Zustand mitfärben. Als > 1820 px war das nicht zu trennen; erkannt hat man sie allein an der Farbe,
externe Datei ginge das nicht; die Originale tragen eine feste schwarze Füllung. > und die trug mit Grün/Orange eine Wertung, die es nicht geben soll
> Auslagern ist kein Missstand. Die Dateien bleiben liegen, falls sie später
> woanders passen; größer dargestellt trennen sie sehr wohl.
## Dateien ## Dateien
| Datei | Icon-Name | Bedeutung | | Datei | Bedeutung |
|---|---|---| |---|---|
| `einlagern.svg` | `einlagern` | Kiste, Pfeil **hinein** Bewegungsart „Eingelagert" | | `einlagern.svg` | Kiste, Pfeil **hinein** Bewegungsart „Eingelagert" |
| `auslagern.svg` | `auslagern` | Kiste, Pfeil **heraus** Bewegungsart „Ausgelagert" | | `auslagern.svg` | Kiste, Pfeil **heraus** Bewegungsart „Ausgelagert" |
| `korrektur.svg` | `korrektur` | Stapel mit Stift Bewegungsart „Korrektur" | | `korrektur.svg` | Stapel mit Stift Bewegungsart „Korrektur" |
Die beiden Kisten unterscheiden sich nur in **einem** Pfad: der Pfeilrichtung. ## Wieder einbauen
Wer sie verwechselt, dreht Ein- und Auslagern um, ohne dass etwas kaputtgeht
deshalb die ausdrückliche Zuordnung oben.
## Nach einer Änderung neu übernehmen Es sind gefüllte Grafiken mit eigenem `viewBox`; die übrigen in `Icon.jsx` sind
Strichzeichnungen (`fill:none`, `stroke:currentColor`) und vertragen sich nicht
damit. Es braucht also wieder einen eigenen Zweig, der mit
`fill="currentColor"` rendert inline, damit die Icons die Textfarbe annehmen
(die Originale tragen festes Schwarz).
Die Pfaddaten wurden per Skript übernommen, nicht abgetippt. Wird eine Datei Die Pfaddaten übernimmt man mit einem kurzen Skript, statt sie abzutippen:
ersetzt, dieselbe Extraktion wiederholen und das Ergebnis in `GEFUELLT` `viewBox` aus dem `<svg>`-Tag lesen und alle `d="…"` der enthaltenen `<path>`
eintragen (`viewBox` mitnehmen die Kisten sind 24×24, die Korrektur 64×64): ausgeben. Ein Tippfehler in den Kurvendaten fällt sonst erst am gerenderten
Bild auf.
```sh
python3 - <<'PY'
import re, pathlib
for name in ("einlagern", "auslagern", "korrektur"):
roh = pathlib.Path(f"brand/icons/{name}.svg").read_text()
print(name, re.search(r'viewBox="([^"]+)"', roh).group(1))
for d in re.findall(r'<path[^>]*\sd="([^"]+)"', roh):
print(f' <path d="{d}" />')
PY
```

View File

@@ -3,18 +3,23 @@ import Icon from "./Icon";
/** /**
* Die Art einer Bestandsbewegung als Sinnbild statt als Wort. * Die Art einer Bestandsbewegung als Sinnbild statt als Wort.
* *
* Kiste mit Pfeil hinein (Einlagern), Kiste mit Pfeil heraus (Auslagern), * Grosser Pfeil auf eine Linie zu (Einlagern), von ihr weg (Auslagern), Stift
* Stapel mit Stift (Korrektur). Alle drei in derselben Farbe: gruen fuer rein * (Korrektur) dieselben Zeichen wie auf den Ein-/Auslagern-Knoepfen.
* und orange fuer raus las sich wie gut und schlecht, dabei ist Auslagern *
* schlicht der Normalfall. Unterschieden wird ueber die Pfeilrichtung. * Vorher standen hier zwei gefuellte Kisten, die sich NUR in einem kleinen
* Pfeil unterschieden. Bei 18 px war das nicht zu trennen; erkannt hat man sie
* allein an der Farbe und die trug mit Gruen/Orange eine Wertung, die es
* nicht geben soll. Auslagern ist kein Missstand.
*
* Deshalb: eine Farbe fuer alle drei, die Richtung liegt im Zeichen selbst.
* *
* Titel und aria-label behalten das Wort: fuer Screenreader und fuer alle, die * Titel und aria-label behalten das Wort: fuer Screenreader und fuer alle, die
* ein Sinnbild nicht auf Anhieb lesen. * ein Sinnbild nicht auf Anhieb lesen.
*/ */
const LABEL = { in: "Eingelagert", out: "Ausgelagert", adjust: "Korrektur" }; const LABEL = { in: "Eingelagert", out: "Ausgelagert", adjust: "Korrektur" };
const ICON = { in: "einlagern", out: "auslagern", adjust: "korrektur" }; const ICON = { in: "checkin", out: "checkout", adjust: "edit" };
export default function BewegungIcon({ typ, size = 18 }) { export default function BewegungIcon({ typ, size = 19 }) {
const wort = LABEL[typ] || typ; const wort = LABEL[typ] || typ;
return ( return (
<span className="bewegung-icon" title={wort} aria-label={wort} role="img"> <span className="bewegung-icon" title={wort} aria-label={wort} role="img">

View File

@@ -143,72 +143,7 @@ const PATHS = {
}; };
// Gefuellte Icons mit EIGENEM viewBox. Die uebrigen hier sind Strichzeichnungen
// (fill:none, stroke:currentColor); diese kommen als fertige Grafik und muessen
// deshalb getrennt gehalten werden.
//
// Quelle der Pfade: brand/icons/*.svg (dort steht auch, wie man sie nach einer
// Aenderung neu uebernimmt). Inline statt <img>, damit sie ueber currentColor
// die Textfarbe annehmen die Originaldateien tragen festes Schwarz.
const GEFUELLT = {
// Kiste mit Pfeil HINEIN aus open-box (1).svg
einlagern: {
viewBox: "0 0 24 24",
node: (
<>
<path d="m11.987 24c-.03 0-.061-.002-.09-.006l-8.243-1c-.377-.045-.66-.365-.66-.744v-6c0-.414.336-.75.75-.75s.75.336.75.75v5.336l6.743.817v-11.543c-.034-.236.044-.482.234-.655.221-.2.543-.258.813-.136.271.12.452.384.452.681v12.5c0 .215-.092.419-.253.562-.136.122-.313.188-.496.188z" />
<path d="m12.007 24c-.183 0-.359-.066-.497-.188-.161-.143-.253-.347-.253-.562v-12.25c0-.414.336-.75.75-.75s.75.336.75.75v11.403l6.742-.817v-5.336c0-.414.336-.75.75-.75s.75.336.75.75v6c0 .379-.283.699-.659.744l-8.242 1c-.031.004-.061.006-.091.006z" />
<path d="m22.25 17h-6.753c-.257 0-.495-.131-.633-.348l-3.494-5.5c-.155-.245-.156-.558-.002-.803.155-.244.439-.379.725-.344l8.246 1c.263.031.488.198.596.439l2.001 4.5c.103.232.082.5-.057.713s-.375.343-.629.343zm-6.341-1.5h5.187l-1.359-3.057-6.251-.758z" />
<path d="m8.503 17h-6.753c-.254 0-.49-.129-.629-.342s-.159-.48-.057-.713l2.001-4.5c.107-.241.333-.408.596-.439l8.246-1c.286-.036.569.1.725.344.154.245.153.558-.002.803l-3.494 5.5c-.138.216-.376.347-.633.347zm-5.599-1.5h5.187l2.424-3.814-6.251.758z" />
<path d="m12 8c-.414 0-.75-.336-.75-.75v-6.5c0-.414.336-.75.75-.75s.75.336.75.75v6.5c0 .414-.336.75-.75.75z" />
<path d="m12 8c-.161 0-.322-.052-.457-.155l-3.25-2.5c-.328-.253-.39-.724-.138-1.052.253-.328.724-.391 1.052-.138l2.793 2.149 2.793-2.148c.328-.253.799-.19 1.052.138.252.328.19.799-.138 1.052l-3.25 2.5c-.135.102-.296.154-.457.154z" />
</>
),
},
// Kiste mit Pfeil HERAUS aus open-box.svg
auslagern: {
viewBox: "0 0 24 24",
node: (
<>
<path d="m11.987 24c-.03 0-.061-.002-.09-.006l-8.243-1c-.377-.045-.66-.365-.66-.744v-6c0-.414.336-.75.75-.75s.75.336.75.75v5.336l6.743.817v-11.543c-.034-.236.044-.482.234-.655.221-.2.543-.258.813-.136.271.12.452.384.452.681v12.5c0 .215-.092.419-.253.562-.136.122-.313.188-.496.188z" />
<path d="m12.007 24c-.183 0-.359-.066-.497-.188-.161-.143-.253-.347-.253-.562v-12.25c0-.414.336-.75.75-.75s.75.336.75.75v11.403l6.742-.817v-5.336c0-.414.336-.75.75-.75s.75.336.75.75v6c0 .379-.283.699-.659.744l-8.242 1c-.031.004-.061.006-.091.006z" />
<path d="m22.25 17h-6.753c-.257 0-.495-.131-.633-.348l-3.494-5.5c-.155-.245-.156-.558-.002-.803.155-.244.44-.379.725-.344l8.246 1c.263.031.488.198.596.439l2.001 4.5c.103.232.082.5-.057.713s-.375.343-.629.343zm-6.341-1.5h5.187l-1.359-3.057-6.251-.758z" />
<path d="m8.503 17h-6.753c-.254 0-.49-.129-.629-.342s-.159-.48-.057-.713l2.001-4.5c.107-.241.333-.408.596-.439l8.246-1c.288-.036.569.1.725.344.154.245.153.558-.002.803l-3.494 5.5c-.138.216-.376.347-.633.347zm-5.599-1.5h5.187l2.424-3.814-6.251.758z" />
<path d="m12 8c-.414 0-.75-.336-.75-.75v-6.5c0-.414.336-.75.75-.75s.75.336.75.75v6.5c0 .414-.336.75-.75.75z" />
<path d="m15.249 4c-.159 0-.32-.051-.456-.155l-2.793-2.149-2.793 2.149c-.328.252-.799.19-1.052-.138-.252-.328-.19-.799.138-1.052l3.25-2.5c.27-.207.645-.207.914 0l3.25 2.5c.328.253.39.724.138 1.052-.149.192-.37.293-.596.293z" />
</>
),
},
// Stapel mit Stift aus edit-server.svg
korrektur: {
viewBox: "0 0 64 64",
node: (
<>
<path d="m64 39.97-8.58-8.58-18.48 18.47-3.86 12.36 12.44-3.76zm-25.83 17.21 1.73-5.53 15.52-15.52 3.84 3.84-15.51 15.53z" />
<path d="m26.74 34.89c10.1 0 20.29-2.02 24.6-5.92h2.14v-18.79c0-6.67-13.45-10.16-26.73-10.16-13.3.01-26.75 3.5-26.75 10.16v43.62c0 6.67 13.45 10.17 26.74 10.17v-3.35c-14.28 0-23.39-4.04-23.39-6.82v-9.35c4.82 3.28 14.14 4.97 23.39 4.97 1.9 0 3.72-.07 5.42-.2l-.26-3.34c-1.61.12-3.35.19-5.16.19-14.28 0-23.39-4.04-23.39-6.82v-9.35c4.82 3.29 14.14 4.99 23.39 4.99zm0-31.51c14.28 0 23.38 4.03 23.38 6.81s-9.11 6.81-23.38 6.81-23.39-4.04-23.39-6.82c0-2.77 9.11-6.8 23.39-6.8zm-23.39 12c.02.01.04.02.06.04.28.19.57.37.88.55.03.02.06.03.08.05.29.17.6.33.91.48.05.03.1.05.16.08 5.14 2.5 13.25 3.78 21.3 3.78s16.16-1.28 21.3-3.78c.05-.03.11-.05.16-.08.32-.16.62-.32.91-.48.03-.01.05-.03.08-.04.31-.18.6-.36.88-.55.02-.01.04-.02.06-.04v9.35c0 2.78-9.11 6.81-23.38 6.81s-23.4-4.05-23.4-6.82z" />
</>
),
},
};
export default function Icon({ name, size = 18, className = "", strokeWidth = 2 }) { export default function Icon({ name, size = 18, className = "", strokeWidth = 2 }) {
const gefuellt = GEFUELLT[name];
if (gefuellt) {
return (
<svg
className={`icon ${className}`}
width={size}
height={size}
viewBox={gefuellt.viewBox}
fill="currentColor"
stroke="none"
aria-hidden="true"
focusable="false"
>
{gefuellt.node}
</svg>
);
}
const path = PATHS[name]; const path = PATHS[name];
if (!path) return null; if (!path) return null;
return ( return (

View File

@@ -27,8 +27,10 @@ export default function History() {
<table className="table"> <table className="table">
<thead> <thead>
<tr> <tr>
{/* Die Bewegungsart zuerst: sie sagt in einem Blick, worum
es geht, und braucht als Zeichen kaum Breite. */}
<th style={{ width: 64 }}>Aktion</th>
<th>Zeitpunkt</th> <th>Zeitpunkt</th>
<th>Aktion</th>
<th>Produkt</th> <th>Produkt</th>
<th className="num">Menge</th> <th className="num">Menge</th>
<th>Benutzer</th> <th>Benutzer</th>
@@ -37,11 +39,11 @@ export default function History() {
<tbody> <tbody>
{movements.map((m) => ( {movements.map((m) => (
<tr key={m.id}> <tr key={m.id}>
<td data-label="Zeitpunkt" className="muted">{new Date(m.created_at).toLocaleString("de-DE")}</td>
{/* Nur das Sinnbild die Tabelle hat noch Menge und {/* Nur das Sinnbild die Tabelle hat noch Menge und
Benutzer zu zeigen, und das Wort stand ohnehin dreimal Benutzer zu zeigen, und das Wort stand ohnehin dreimal
untereinander. Titel und aria-label tragen es weiter. */} untereinander. Titel und aria-label tragen es weiter. */}
<td data-label="Aktion"><BewegungIcon typ={m.type} /></td> <td data-label="Aktion"><BewegungIcon typ={m.type} /></td>
<td data-label="Zeitpunkt" className="muted">{new Date(m.created_at).toLocaleString("de-DE")}</td>
<td data-label="Produkt">{m.product_name}</td> <td data-label="Produkt">{m.product_name}</td>
<td data-label="Menge" className="num"> <td data-label="Menge" className="num">
{articlePrimary(m)} {articlePrimary(m)}