This commit is contained in:
seb
2026-10-11 23:42:20 +02:00
parent d0fed9a2d1
commit ed2ac2c2c3

View File

@@ -13,7 +13,7 @@ import { enqueueSnackbar } from 'notistack';
import BackendError from '../../components/BackendError';
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
import FillSpace from 'dashgui/FillSpace';
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
import { PanelTile, PanelTileGrid, TabbedPanelTileGrid } from 'dashgui/panels';
import withNavigate from '../../components/withNavigate';
import { backendErrorFrom } from '../../utils/backendError';
import { notifyError } from '../../utils/notify';
@@ -69,6 +69,7 @@ class ArtikelMerkmalePage extends PureComponent {
this._valSortable = null;
this.handleRetry = this.handleRetry.bind(this);
this.handleTabChange = this.handleTabChange.bind(this);
this.handleCreateAttribute = this.handleCreateAttribute.bind(this);
this.handleCreateValue = this.handleCreateValue.bind(this);
this.handleCreateProperty = this.handleCreateProperty.bind(this);
@@ -340,6 +341,15 @@ class ArtikelMerkmalePage extends PureComponent {
void this.loadAll();
}
handleTabChange(nextTab) {
if (nextTab === 0) {
this._initAttrSortable();
this._initValSortable();
} else if (nextTab === 1) {
this._initPropSortable();
}
}
handleSelectAttribute(attrId) {
this.setState({ selectedAttributeId: attrId, valFilter: '', valPage: 1 });
}
@@ -453,200 +463,206 @@ class ArtikelMerkmalePage extends PureComponent {
: properties;
return (
<PanelTileGrid>
{/* Tile 1: Auswahl-Merkmale */}
<PanelTile wide={2} minHeight={0} color={BLUE_PANEL}>
<Stack spacing={1.5}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<StyleIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Auswahl-Merkmale
</Typography>
</Box>
<TabbedPanelTileGrid onChange={this.handleTabChange}>
{/* Tab 1: Auswahl-Merkmale & Werte */}
<PanelTileGrid icon={<StyleIcon />} text="Auswahl-Merkmale">
{/* Tile 1: Auswahl-Merkmale */}
<PanelTile wide={2} minHeight={0} color={BLUE_PANEL}>
<Stack spacing={1.5}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<StyleIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Auswahl-Merkmale
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={attrFilter}
disabled={attrSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ attrFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateAttribute();
}
}}
/>
<Button
variant="contained"
disabled={!attrFilterTrimmed || attrSubmitting}
onClick={() => void this.handleCreateAttribute()}
>
Hinzufügen
</Button>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={attrFilter}
disabled={attrSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ attrFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateAttribute();
}
}}
/>
<Button
variant="contained"
disabled={!attrFilterTrimmed || attrSubmitting}
onClick={() => void this.handleCreateAttribute()}
>
Hinzufügen
</Button>
</Box>
{filteredAttributes.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{attrFilterTrimmed
? `Keine Treffer für „${attrFilterTrimmed}“.`
: 'Noch keine Auswahl-Merkmale angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredAttributes}
totalCount={attributes.length}
minPageSize={8}
maxPageSize={8}
listRef={this.attrListRef}
onPageChange={(page) => this.setState({ attrPage: page })}
renderItem={(a) => (
<ArtikelMerkmalAuswahl
key={a.id}
attribute={a}
isSelected={selectedAttr?.id === a.id}
onSelect={() => this.handleSelectAttribute(a.id)}
/>
)}
/>
)}
</Stack>
</PanelTile>
{filteredAttributes.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{attrFilterTrimmed
? `Keine Treffer für „${attrFilterTrimmed}“.`
: 'Noch keine Auswahl-Merkmale angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredAttributes}
totalCount={attributes.length}
minPageSize={8}
maxPageSize={8}
listRef={this.attrListRef}
onPageChange={(page) => this.setState({ attrPage: page })}
renderItem={(a) => (
<ArtikelMerkmalAuswahl
key={a.id}
attribute={a}
isSelected={selectedAttr?.id === a.id}
onSelect={() => this.handleSelectAttribute(a.id)}
/>
)}
/>
)}
</Stack>
</PanelTile>
{/* Tile 2: Werte für das gewählte Auswahl-Merkmal */}
<PanelTile wide={2} minHeight={0} color={PURPLE_PANEL}>
<Stack spacing={1.5}>
<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 Auswahl-Merkmal „${selectedAttr.name}“` : 'Werte'}
</Typography>
</Box>
{/* Tile 2: Werte für das gewählte Auswahl-Merkmal */}
<PanelTile wide={2} minHeight={0} color={PURPLE_PANEL}>
<Stack spacing={1.5}>
<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 Auswahl-Merkmal „${selectedAttr.name}“` : 'Werte'}
</Typography>
</Box>
{!selectedAttr ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
Kein Attribut ausgewählt.
</Typography>
) : (
<>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={valFilter}
disabled={valSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ valFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateValue();
}
}}
/>
<Button
variant="contained"
disabled={!valFilterTrimmed || valSubmitting}
onClick={() => void this.handleCreateValue()}
>
Hinzufügen
</Button>
</Box>
{!selectedAttr ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
Kein Attribut ausgewählt.
</Typography>
) : (
<>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={valFilter}
disabled={valSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ valFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateValue();
}
}}
/>
<Button
variant="contained"
disabled={!valFilterTrimmed || valSubmitting}
onClick={() => void this.handleCreateValue()}
>
Hinzufügen
</Button>
</Box>
{filteredValues.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{valFilterTrimmed
? `Keine Treffer für „${valFilterTrimmed}“.`
: 'Noch keine Werte angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredValues}
totalCount={selectedValues.length}
minPageSize={8}
maxPageSize={8}
listRef={this.valListRef}
onPageChange={(page) => this.setState({ valPage: page })}
renderItem={(v) => (
<ArtikelMerkmalWert
key={v.id}
value={v}
/>
)}
/>
)}
</>
)}
</Stack>
</PanelTile>
{filteredValues.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{valFilterTrimmed
? `Keine Treffer für „${valFilterTrimmed}“.`
: 'Noch keine Werte angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredValues}
totalCount={selectedValues.length}
minPageSize={8}
maxPageSize={8}
listRef={this.valListRef}
onPageChange={(page) => this.setState({ valPage: page })}
renderItem={(v) => (
<ArtikelMerkmalWert
key={v.id}
value={v}
/>
)}
/>
)}
</>
)}
</Stack>
</PanelTile>
</PanelTileGrid>
{/* Tile 3: Text-Merkmale */}
<PanelTile wide={2} minHeight={0} color={ORANGE_PANEL}>
<Stack spacing={1.5}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<TuneIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Text-Merkmale
</Typography>
</Box>
{/* Tab 2: Text-Merkmale */}
<PanelTileGrid icon={<TuneIcon />} text="Text-Merkmale">
{/* Tile 3: Text-Merkmale */}
<PanelTile wide={2} minHeight={0} color={ORANGE_PANEL}>
<Stack spacing={1.5}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<TuneIcon sx={{ color: 'text.secondary', fontSize: 20 }} />
<Typography variant="subtitle1" sx={{ fontWeight: 600 }}>
Text-Merkmale
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={propFilter}
disabled={propSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ propFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateProperty();
}
}}
/>
<Button
variant="contained"
disabled={!propFilterTrimmed || propSubmitting}
onClick={() => void this.handleCreateProperty()}
>
Hinzufügen
</Button>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<SingleLineTextEdit
sx={{ flex: 1 }}
size="small"
search
clear
value={propFilter}
disabled={propSubmitting}
placeholder="Suchen oder anlegen…"
onChange={(next) => this.setState({ propFilter: next })}
onKeyDown={(event) => {
if (event.key === 'Enter') {
event.preventDefault();
void this.handleCreateProperty();
}
}}
/>
<Button
variant="contained"
disabled={!propFilterTrimmed || propSubmitting}
onClick={() => void this.handleCreateProperty()}
>
Hinzufügen
</Button>
</Box>
{filteredProperties.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{propFilterTrimmed
? `Keine Treffer für „${propFilterTrimmed}“.`
: 'Noch keine Text-Merkmale angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredProperties}
totalCount={properties.length}
minPageSize={8}
maxPageSize={8}
listRef={this.propListRef}
onPageChange={(page) => this.setState({ propPage: page })}
renderItem={(p) => (
<ArtikelMerkmalText
key={p.id}
property={p}
/>
)}
/>
)}
</Stack>
</PanelTile>
</PanelTileGrid>
{filteredProperties.length === 0 ? (
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
{propFilterTrimmed
? `Keine Treffer für „${propFilterTrimmed}“.`
: 'Noch keine Text-Merkmale angelegt.'}
</Typography>
) : (
<FillSpace
items={filteredProperties}
totalCount={properties.length}
minPageSize={8}
maxPageSize={8}
listRef={this.propListRef}
onPageChange={(page) => this.setState({ propPage: page })}
renderItem={(p) => (
<ArtikelMerkmalText
key={p.id}
property={p}
/>
)}
/>
)}
</Stack>
</PanelTile>
</PanelTileGrid>
</TabbedPanelTileGrid>
);
}
}