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
abgerundete Ecken. Für App-Icon (iOS/macOS via Icon Composer oder Xcode
„AppIcon", App Store). Die Rundung macht das System selbst.
- **`icons/`** Quellgrafiken der gefüllten Icons (Ein-/Auslagern, Korrektur).
Ihre Pfaddaten stehen inline in `../web/src/components/Icon.jsx`; siehe
`icons/README.md`.
- **`icons/`** Quellgrafiken für Ein-/Auslagern und Korrektur. Derzeit nicht
im Einsatz; siehe `icons/README.md`.
- **Favicon / Web-Logo:** `../web/public/favicon.svg` dasselbe Zeichen mit
abgerundeter Platte und Hell-/Dunkel-Umschaltung (für den Browser-Tab).

View File

@@ -1,38 +1,32 @@
# Icon-Quellen
Hier liegen zugekaufte/heruntergeladene Grafiken, deren Pfaddaten **inline** in
`web/src/components/Icon.jsx` stehen (Abschnitt `GEFUELLT`). Zur Laufzeit lädt
niemand diese Dateien sie sind die Quelle, nicht das Auslieferungsformat.
Warum inline statt `<img src>`: Die Icons sollen die Textfarbe annehmen
(`fill="currentColor"`), damit sie sich mit Thema und Zustand mitfärben. Als
externe Datei ginge das nicht; die Originale tragen eine feste schwarze Füllung.
> **Derzeit nicht im Einsatz.** Die Verlaufsansicht nutzt die Strich-Icons
> `checkin` / `checkout` / `edit` aus `web/src/components/Icon.jsx` dieselben
> wie die Ein-/Auslagern-Knöpfe.
>
> Grund: Die beiden Kisten unterscheiden sich **nur in der Pfeilrichtung**. Bei
> 1820 px war das nicht zu trennen; erkannt hat man sie allein an der Farbe,
> 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
| Datei | Icon-Name | Bedeutung |
|---|---|---|
| `einlagern.svg` | `einlagern` | Kiste, Pfeil **hinein** Bewegungsart „Eingelagert" |
| `auslagern.svg` | `auslagern` | Kiste, Pfeil **heraus** Bewegungsart „Ausgelagert" |
| `korrektur.svg` | `korrektur` | Stapel mit Stift Bewegungsart „Korrektur" |
| Datei | Bedeutung |
|---|---|
| `einlagern.svg` | Kiste, Pfeil **hinein** Bewegungsart „Eingelagert" |
| `auslagern.svg` | Kiste, Pfeil **heraus** Bewegungsart „Ausgelagert" |
| `korrektur.svg` | Stapel mit Stift Bewegungsart „Korrektur" |
Die beiden Kisten unterscheiden sich nur in **einem** Pfad: der Pfeilrichtung.
Wer sie verwechselt, dreht Ein- und Auslagern um, ohne dass etwas kaputtgeht
deshalb die ausdrückliche Zuordnung oben.
## Wieder einbauen
## 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
ersetzt, dieselbe Extraktion wiederholen und das Ergebnis in `GEFUELLT`
eintragen (`viewBox` mitnehmen die Kisten sind 24×24, die Korrektur 64×64):
```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
```
Die Pfaddaten übernimmt man mit einem kurzen Skript, statt sie abzutippen:
`viewBox` aus dem `<svg>`-Tag lesen und alle `d="…"` der enthaltenen `<path>`
ausgeben. Ein Tippfehler in den Kurvendaten fällt sonst erst am gerenderten
Bild auf.

View File

@@ -3,18 +3,23 @@ import Icon from "./Icon";
/**
* Die Art einer Bestandsbewegung als Sinnbild statt als Wort.
*
* Kiste mit Pfeil hinein (Einlagern), Kiste mit Pfeil heraus (Auslagern),
* Stapel mit Stift (Korrektur). Alle drei in derselben Farbe: gruen fuer rein
* und orange fuer raus las sich wie gut und schlecht, dabei ist Auslagern
* schlicht der Normalfall. Unterschieden wird ueber die Pfeilrichtung.
* Grosser Pfeil auf eine Linie zu (Einlagern), von ihr weg (Auslagern), Stift
* (Korrektur) dieselben Zeichen wie auf den Ein-/Auslagern-Knoepfen.
*
* 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
* ein Sinnbild nicht auf Anhieb lesen.
*/
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;
return (
<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 }) {
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];
if (!path) return null;
return (

View File

@@ -27,8 +27,10 @@ export default function History() {
<table className="table">
<thead>
<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>Aktion</th>
<th>Produkt</th>
<th className="num">Menge</th>
<th>Benutzer</th>
@@ -37,11 +39,11 @@ export default function History() {
<tbody>
{movements.map((m) => (
<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
Benutzer zu zeigen, und das Wort stand ohnehin dreimal
untereinander. Titel und aria-label tragen es weiter. */}
<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="Menge" className="num">
{articlePrimary(m)}