This commit is contained in:
seb
2026-10-04 11:25:10 +02:00
parent dd8566eabc
commit 874d6c010f
16 changed files with 345 additions and 346 deletions

View File

@@ -8,9 +8,6 @@ import ListItem from '@mui/material/ListItem';
import Stack from '@mui/material/Stack';
import Typography from '@mui/material/Typography';
import Chip from '@mui/material/Chip';
import Tooltip from '@mui/material/Tooltip';
import FormControlLabel from '@mui/material/FormControlLabel';
import Checkbox from '@mui/material/Checkbox';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
import TuneIcon from '@mui/icons-material/Tune';
@@ -47,12 +44,10 @@ class ArtikelMerkmalePage extends PureComponent {
properties: initialProperties,
propDrafts: draftsFrom(initialProperties, 'name'),
newPropName: '',
newPropExclusive: true,
attributes: initialAttributes,
attrDrafts: draftsFrom(initialAttributes, 'name'),
newAttrName: '',
newAttrExclusive: true,
selectedAttributeId: initialSelectedAttrId,
valDrafts: draftsFrom(
@@ -333,13 +328,13 @@ class ArtikelMerkmalePage extends PureComponent {
async handleCreateProperty() {
const name = this.state.newPropName.trim();
if (!name || this.state.busyKey != null) return;
if (!name) return;
this.setState({ busyKey: 'new-prop' });
try {
const row = await propertyAutomat.actions.createProperty({
name,
exclusive: this.state.newPropExclusive,
exclusive: true,
});
if (this._cancelled) return;
this.setState({ newPropName: '', busyKey: null });
@@ -353,7 +348,6 @@ class ArtikelMerkmalePage extends PureComponent {
async handleTogglePropertyExclusive(prop, e) {
e?.stopPropagation();
if (this.state.busyKey != null) return;
const nextExclusive = !prop.exclusive;
this.setState({ busyKey: `prop-ex-${prop.id}` });
@@ -380,7 +374,7 @@ class ArtikelMerkmalePage extends PureComponent {
}));
return;
}
if (draft === prop.name || this.state.busyKey != null) return;
if (draft === prop.name) return;
this.setState({ busyKey: `prop-${prop.id}` });
try {
@@ -399,7 +393,6 @@ class ArtikelMerkmalePage extends PureComponent {
}
async handleDeleteProperty(prop) {
if (this.state.busyKey != null) return;
if (!window.confirm(`Merkmal „${prop.name}" wirklich löschen?`)) {
return;
}
@@ -421,13 +414,13 @@ class ArtikelMerkmalePage extends PureComponent {
async handleCreateAttribute() {
const name = this.state.newAttrName.trim();
if (!name || this.state.busyKey != null) return;
if (!name) return;
this.setState({ busyKey: 'new-attr' });
try {
const row = await attributeAutomat.actions.createAttribute({
name,
exclusive: this.state.newAttrExclusive,
exclusive: true,
});
if (this._cancelled) return;
this.setState({
@@ -451,7 +444,7 @@ class ArtikelMerkmalePage extends PureComponent {
}));
return;
}
if (draft === attr.name || this.state.busyKey != null) return;
if (draft === attr.name) return;
this.setState({ busyKey: `attr-${attr.id}` });
try {
@@ -471,7 +464,6 @@ class ArtikelMerkmalePage extends PureComponent {
async handleToggleAttributeExclusive(attr, e) {
e?.stopPropagation();
if (this.state.busyKey != null) return;
const nextExclusive = !attr.exclusive;
this.setState({ busyKey: `attr-ex-${attr.id}` });
@@ -492,7 +484,6 @@ class ArtikelMerkmalePage extends PureComponent {
async handleDeleteAttribute(attr, e) {
e?.stopPropagation();
if (this.state.busyKey != null) return;
if (!window.confirm(`Attribut „${attr.name}" und alle zugehörigen Werte wirklich löschen?`)) {
return;
}
@@ -523,7 +514,7 @@ class ArtikelMerkmalePage extends PureComponent {
async handleCreateValue(attrId) {
const text = this.state.newValText.trim();
if (!text || this.state.busyKey != null || !attrId) return;
if (!text || !attrId) return;
this.setState({ busyKey: 'new-val' });
try {
@@ -546,7 +537,7 @@ class ArtikelMerkmalePage extends PureComponent {
}));
return;
}
if (draft === val.value || this.state.busyKey != null) return;
if (draft === val.value) return;
this.setState({ busyKey: `val-${val.id}` });
try {
@@ -565,7 +556,6 @@ class ArtikelMerkmalePage extends PureComponent {
}
async handleDeleteValue(val) {
if (this.state.busyKey != null) return;
if (!window.confirm(`Wert „${val.value}" wirklich löschen?`)) {
return;
}
@@ -591,18 +581,15 @@ class ArtikelMerkmalePage extends PureComponent {
properties,
propDrafts,
newPropName,
newPropExclusive,
attributes,
attrDrafts,
newAttrName,
newAttrExclusive,
selectedAttributeId,
valDrafts,
newValText,
busyKey,
} = this.state;
const busy = busyKey != null;
if (attrStatus === 'loading' && propStatus === 'loading' && !properties.length && !attributes.length) {
return (
@@ -638,7 +625,6 @@ class ArtikelMerkmalePage extends PureComponent {
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Auswahl-Merkmale
</Typography>
<Chip label={attributes.length} size="small" sx={{ ml: 'auto', fontWeight: 600, height: 20 }} />
</Box>
<List dense disablePadding ref={this.attrListRef}>
@@ -693,7 +679,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={attrDrafts[a.id] ?? ''}
disabled={busy}
onChange={(next) =>
this.setState((prev) => ({
attrDrafts: { ...prev.attrDrafts, [a.id]: next },
@@ -717,34 +702,29 @@ class ArtikelMerkmalePage extends PureComponent {
/>
</Box>
<Tooltip
title={
a.exclusive
? 'Einzelauswahl (Artikel kann max. 1 Wert wählen)'
: 'Mehrfachauswahl (Artikel kann mehrere Werte wählen)'
}
>
<Chip
label={a.exclusive ? '1x' : 'Multi'}
<Chip
label="Multi"
size="small"
color={a.exclusive ? 'primary' : 'default'}
variant={a.exclusive ? 'filled' : 'outlined'}
color={a.exclusive ? 'default' : 'success'}
variant="filled"
onClick={(e) => void this.handleToggleAttributeExclusive(a, e)}
aria-pressed={!a.exclusive}
sx={{
height: 22,
fontSize: '0.72rem',
fontWeight: 600,
cursor: 'pointer',
flexShrink: 0,
...(a.exclusive
? { bgcolor: 'grey.300', color: 'text.secondary' }
: null),
}}
/>
</Tooltip>
<IconButton
edge="end"
size="small"
aria-label={`„${a.name}" löschen`}
disabled={busy}
onClick={(e) => void this.handleDeleteAttribute(a, e)}
sx={{ flexShrink: 0 }}
>
@@ -761,7 +741,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={newAttrName}
disabled={busy}
onChange={(next) => this.setState({ newAttrName: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@@ -774,29 +753,12 @@ class ArtikelMerkmalePage extends PureComponent {
<Button
variant="contained"
size="small"
disabled={busy || !newAttrName.trim()}
disabled={!newAttrName.trim()}
onClick={() => void this.handleCreateAttribute()}
>
{busyKey === 'new-attr' ? '…' : 'Hinzufügen'}
</Button>
</Box>
<FormControlLabel
control={
<Checkbox
size="small"
checked={newAttrExclusive}
disabled={busy}
onChange={(e) => this.setState({ newAttrExclusive: e.target.checked })}
sx={{ p: 0.5 }}
/>
}
label={
<Typography variant="caption" color="text.secondary">
Einzelauswahl (max. 1 Wert pro Artikel)
</Typography>
}
sx={{ ml: 0.5, mt: -0.5 }}
/>
</Box>
</Stack>
</PanelTile>
@@ -807,15 +769,8 @@ class ArtikelMerkmalePage extends PureComponent {
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<LabelImportantIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
{selectedAttr ? `Werte für „${selectedAttr.name}“` : 'Werte'}
{selectedAttr ? `Werte für Auswahl-Merkmal „${selectedAttr.name}“` : 'Werte'}
</Typography>
{selectedAttr ? (
<Chip
label={selectedValues.length}
size="small"
sx={{ ml: 'auto', fontWeight: 600, height: 20 }}
/>
) : null}
</Box>
{!selectedAttr ? (
@@ -825,12 +780,7 @@ class ArtikelMerkmalePage extends PureComponent {
) : (
<>
<List dense disablePadding ref={this.valListRef}>
{selectedValues.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
Noch keine Werte für „{selectedAttr.name}“ hinterlegt.
</Typography>
) : (
selectedValues.map((v) => (
{selectedValues.map((v) => (
<ListItem
key={v.id}
disableGutters
@@ -855,7 +805,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={valDrafts[v.id] ?? ''}
disabled={busy}
onChange={(next) =>
this.setState((prev) => ({
valDrafts: { ...prev.valDrafts, [v.id]: next },
@@ -881,14 +830,12 @@ class ArtikelMerkmalePage extends PureComponent {
edge="end"
size="small"
aria-label={`„${v.value}" löschen`}
disabled={busy}
onClick={() => void this.handleDeleteValue(v)}
>
<DeleteOutlinedIcon fontSize="small" />
</IconButton>
</ListItem>
))
)}
))}
</List>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
@@ -896,7 +843,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={newValText}
disabled={busy}
onChange={(next) => this.setState({ newValText: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@@ -909,7 +855,7 @@ class ArtikelMerkmalePage extends PureComponent {
<Button
variant="contained"
size="small"
disabled={busy || !newValText.trim()}
disabled={!newValText.trim()}
onClick={() => void this.handleCreateValue(selectedAttr.id)}
>
{busyKey === 'new-val' ? '…' : 'Hinzufügen'}
@@ -928,7 +874,6 @@ class ArtikelMerkmalePage extends PureComponent {
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Text-Merkmale
</Typography>
<Chip label={properties.length} size="small" sx={{ ml: 'auto', fontWeight: 600, height: 20 }} />
</Box>
<List dense disablePadding ref={this.propListRef}>
@@ -957,7 +902,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={propDrafts[p.id] ?? ''}
disabled={busy}
onChange={(next) =>
this.setState((prev) => ({
propDrafts: { ...prev.propDrafts, [p.id]: next },
@@ -979,33 +923,28 @@ class ArtikelMerkmalePage extends PureComponent {
}}
sx={{ flex: 1 }}
/>
<Tooltip
title={
p.exclusive
? 'Einzelauswahl (Artikel kann max. 1 Wert für dieses Merkmal haben)'
: 'Mehrfachauswahl (Artikel kann mehrere Werte für dieses Merkmal haben)'
}
>
<Chip
label={p.exclusive ? '1x' : 'Multi'}
<Chip
label="Multi"
size="small"
color={p.exclusive ? 'primary' : 'default'}
variant={p.exclusive ? 'filled' : 'outlined'}
color={p.exclusive ? 'default' : 'success'}
variant="filled"
onClick={(e) => void this.handleTogglePropertyExclusive(p, e)}
aria-pressed={!p.exclusive}
sx={{
height: 22,
fontSize: '0.72rem',
fontWeight: 600,
cursor: 'pointer',
flexShrink: 0,
...(p.exclusive
? { bgcolor: 'grey.300', color: 'text.secondary' }
: null),
}}
/>
</Tooltip>
<IconButton
edge="end"
size="small"
aria-label={`„${p.name}" löschen`}
disabled={busy}
onClick={() => void this.handleDeleteProperty(p)}
>
<DeleteOutlinedIcon fontSize="small" />
@@ -1020,7 +959,6 @@ class ArtikelMerkmalePage extends PureComponent {
size="small"
label=""
value={newPropName}
disabled={busy}
onChange={(next) => this.setState({ newPropName: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
@@ -1033,29 +971,12 @@ class ArtikelMerkmalePage extends PureComponent {
<Button
variant="contained"
size="small"
disabled={busy || !newPropName.trim()}
disabled={!newPropName.trim()}
onClick={() => void this.handleCreateProperty()}
>
{busyKey === 'new-prop' ? '…' : 'Hinzufügen'}
</Button>
</Box>
<FormControlLabel
control={
<Checkbox
size="small"
checked={newPropExclusive}
disabled={busy}
onChange={(e) => this.setState({ newPropExclusive: e.target.checked })}
sx={{ p: 0.5 }}
/>
}
label={
<Typography variant="caption" color="text.secondary">
Einzelauswahl (max. 1 Wert pro Artikel)
</Typography>
}
sx={{ ml: 0.5, mt: -0.5 }}
/>
</Box>
</Stack>
</PanelTile>