Gruppen hierarchisch: in allen Auswahlfeldern und in der Artikelliste
Die Gruppen-Dropdowns waren flache, alphabetische Listen - man sah nicht, dass "Grillwurst" unter "Wurst" haengt. Ein gemeinsamer Helfer (gruppenOptionen) rollt den Graphen zum Baum aus und rueckt ein; eine Gruppe mit mehreren Obergruppen erscheint unter jeder, denn genau das ist ihr Sinn, und ausgewaehlt wird ohnehin dieselbe ID. Eingerueckt wird mit GESCHUETZTEN Leerzeichen - normale fasst der Browser in einem <option> zusammen, die Einrueckung waere sonst wirkungslos. Betroffen: Gruppen-Auswahl im Artikelformular (beide Zweige), beim Einlagern, die Ziel-Auswahl der Mindestbestaende und die Obergruppen-Mehrfachauswahl (die beim Suchen bewusst abflacht, weil eine Einrueckung ohne die Eltern in die Irre fuehrt). In der App dasselbe in beiden Gruppen-Pickern. Die Gruppen-Spalte der Artikelliste zeigt jetzt den Weg von oben statt eines Etiketts - "Wurst → Grillwurst", mit gedaempften Vorfahren wie bei der Kategorie. Gefiltert wird ueber ALLE Wege: wer auf "Wurst" filtert, sieht auch die Artikel aus den Untergruppen. Dafuer gibt es gruppenInfoMap als Gegenstueck zu categoryInfoMap, das mit mehreren Wegen je Gruppe umgehen kann. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useEffect, useLayoutEffect, useRef, useState } from "react";
|
||||
import { createPortal } from "react-dom";
|
||||
import Icon from "./Icon";
|
||||
import { nachfahrenIds, pfadText } from "../groupGraph";
|
||||
import { gruppenOptionen, nachfahrenIds, pfadText } from "../groupGraph";
|
||||
|
||||
/**
|
||||
* Mehrfachauswahl der Obergruppen einer Gruppe.
|
||||
@@ -35,10 +35,15 @@ export default function GroupParentSelect({
|
||||
const gesperrt = selfId == null ? new Set() : new Set([selfId, ...nachfahrenIds(groups, selfId)]);
|
||||
const nameById = Object.fromEntries(groups.map((g) => [g.id, g.name]));
|
||||
|
||||
const sichtbar = groups
|
||||
.filter((g) => !filter || g.name.toLowerCase().includes(filter.toLowerCase()))
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name, "de"));
|
||||
// Als Baum, damit die Struktur beim Zuordnen sichtbar ist. Beim Suchen
|
||||
// flacht die Liste ab – eine Einrückung ohne ihre Eltern wäre irreführend.
|
||||
const sichtbar = filter
|
||||
? groups
|
||||
.filter((g) => g.name.toLowerCase().includes(filter.toLowerCase()))
|
||||
.slice()
|
||||
.sort((a, b) => a.name.localeCompare(b.name, "de"))
|
||||
.map((g) => ({ id: g.id, key: `f${g.id}`, gruppe: g, tiefe: 0, label: g.name }))
|
||||
: gruppenOptionen(groups);
|
||||
|
||||
const label = value?.length
|
||||
? value.map((id) => nameById[id]).filter(Boolean).join(", ")
|
||||
@@ -115,22 +120,23 @@ export default function GroupParentSelect({
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{sichtbar.map((g) => {
|
||||
const aus = gesperrt.has(g.id);
|
||||
const an = gewaehlt.has(g.id);
|
||||
{sichtbar.map((o) => {
|
||||
const aus = gesperrt.has(o.id);
|
||||
const an = gewaehlt.has(o.id);
|
||||
return (
|
||||
<div key={g.id} className="tree-pop-line">
|
||||
<div key={o.key} className="tree-pop-line" style={{ paddingLeft: o.tiefe * 14 }}>
|
||||
<button
|
||||
type="button"
|
||||
className={`tree-pop-opt ${an ? "sel" : ""}`}
|
||||
disabled={aus}
|
||||
title={aus
|
||||
? "Würde einen Ring erzeugen (die Gruppe selbst oder eine ihrer Untergruppen)"
|
||||
: pfadText(groups, g.id)}
|
||||
onClick={() => umschalten(g.id)}
|
||||
: pfadText(groups, o.id)}
|
||||
onClick={() => umschalten(o.id)}
|
||||
>
|
||||
<span style={{ display: "inline-block", width: 18 }}>{an ? "✓" : ""}</span>
|
||||
{g.name}
|
||||
{o.tiefe > 0 && <span className="muted">↳ </span>}
|
||||
{o.gruppe.name}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user