This commit is contained in:
seb
2026-10-04 22:47:26 +02:00
parent 8b401fa50b
commit a90069d340
44 changed files with 994 additions and 268 deletions

View File

@@ -61,10 +61,10 @@ export default class ArticleViewLists extends PureComponent {
// Active sub-tab for compound article: 'stueckliste' | 'options'
activeListTab,
// Creator mode for standard article: null | 'father' | 'stueckliste' | 'options'
// Creator mode for standard article: null | 'parent' | 'stueckliste' | 'options'
activeCreatorMode: null,
// Father mode state
// Parent mode state
selectedAttributeId: '',
selectedChildValueId: '',
newChildText: '',
@@ -117,8 +117,8 @@ export default class ArticleViewLists extends PureComponent {
void this.loadAssignedAttributes(this.props.article.sku);
}
if (this.props.article?.fatherSku) {
void artikelAutomat.actions.loadArticle(this.props.article.fatherSku);
if (this.props.article?.parentSku) {
void artikelAutomat.actions.loadArticle(this.props.article.parentSku);
}
void this.loadAllArticles();
@@ -129,8 +129,8 @@ export default class ArticleViewLists extends PureComponent {
if (this.props.article?.sku) {
void this.loadAssignedAttributes(this.props.article.sku);
}
if (this.props.article?.fatherSku) {
void artikelAutomat.actions.loadArticle(this.props.article.fatherSku);
if (this.props.article?.parentSku) {
void artikelAutomat.actions.loadArticle(this.props.article.parentSku);
}
const nextArticle = this.props.article;
const hasStueckliste = Boolean(
@@ -205,9 +205,9 @@ export default class ArticleViewLists extends PureComponent {
}
}
// ── Father Actions ──────────────────────────────────────────────────────────
// ── Parent Actions ──────────────────────────────────────────────────────────
handleEnableFather = async () => {
handleEnableParent = async () => {
const { article } = this.props;
const { selectedAttributeId, assignedAttributes, busy } = this.state;
if (busy || !article?.sku || !selectedAttributeId) return;
@@ -232,8 +232,8 @@ export default class ArticleViewLists extends PureComponent {
this.setState({ busy: true });
try {
const api = this.getApi();
const res = await api.toggleFather(article.sku, {
isFather: true,
const res = await api.toggleParent(article.sku, {
isParent: true,
attributeNameId: Number(selectedAttributeId),
});
if (this._cancelled) return;
@@ -253,7 +253,7 @@ export default class ArticleViewLists extends PureComponent {
}
};
handleDisableFather = async () => {
handleDisableParent = async () => {
const { article } = this.props;
const { busy } = this.state;
if (busy || !article?.sku) return;
@@ -261,7 +261,7 @@ export default class ArticleViewLists extends PureComponent {
this.setState({ busy: true });
try {
const api = this.getApi();
const res = await api.toggleFather(article.sku, { isFather: false });
const res = await api.toggleParent(article.sku, { isParent: false });
if (this._cancelled) return;
if (res?.article) {
@@ -365,12 +365,12 @@ export default class ArticleViewLists extends PureComponent {
return;
}
const isTargetFather = Boolean(
stuecklisteTargetArticle.isFather ||
stuecklisteTargetArticle.is_father ||
getArticle(stuecklisteTargetArticle.sku)?.isFather
const isTargetParent = Boolean(
stuecklisteTargetArticle.isParent ||
stuecklisteTargetArticle.is_parent ||
getArticle(stuecklisteTargetArticle.sku)?.isParent
);
if (isTargetFather) {
if (isTargetParent) {
enqueueSnackbar('Ein Vaterartikel kann nicht als Komponente einer Stückliste hinzugefügt werden.', { variant: 'error' });
return;
}
@@ -489,12 +489,12 @@ export default class ArticleViewLists extends PureComponent {
return;
}
const isTargetFather = Boolean(
optionTargetArticle.isFather ||
optionTargetArticle.is_father ||
getArticle(optionTargetArticle.sku)?.isFather
const isTargetParent = Boolean(
optionTargetArticle.isParent ||
optionTargetArticle.is_parent ||
getArticle(optionTargetArticle.sku)?.isParent
);
if (isTargetFather) {
if (isTargetParent) {
enqueueSnackbar('Ein Vaterartikel kann nicht als Option hinzugefügt werden.', { variant: 'error' });
return;
}
@@ -1081,33 +1081,33 @@ export default class ArticleViewLists extends PureComponent {
);
}
renderChildView(fatherSku, fatherArticle, attributes, assignedAttributes) {
renderChildView(parentSku, parentArticle, attributes, assignedAttributes) {
const { article: articleData } = this.props;
const fatherAttrName =
fatherArticle?.fatherAttributeName ||
(fatherArticle?.fatherAttributeNameId
? attributes.find((a) => a.id === fatherArticle.fatherAttributeNameId)?.name
const parentAttrName =
parentArticle?.parentAttributeName ||
(parentArticle?.parentAttributeNameId
? attributes.find((a) => a.id === parentArticle.parentAttributeNameId)?.name
: null) ||
'Varianten';
const selfInFather = (fatherArticle?.children || []).find(
const selfInParent = (parentArticle?.children || []).find(
(c) => c.sku === articleData.sku || c.id === articleData.id
);
const assignedMatch = (assignedAttributes || []).find(
(a) =>
(fatherArticle?.fatherAttributeNameId != null &&
Number(a.attributeId) === Number(fatherArticle.fatherAttributeNameId)) ||
(parentArticle?.parentAttributeNameId != null &&
Number(a.attributeId) === Number(parentArticle.parentAttributeNameId)) ||
(a.attributeName &&
fatherAttrName &&
String(a.attributeName).toLowerCase() === String(fatherAttrName).toLowerCase())
parentAttrName &&
String(a.attributeName).toLowerCase() === String(parentAttrName).toLowerCase())
);
const variationValue =
selfInFather?.attributeValue ||
selfInParent?.attributeValue ||
assignedMatch?.attributeValue ||
null;
const siblings = (fatherArticle?.children || []).filter(
const siblings = (parentArticle?.children || []).filter(
(c) => c.sku !== articleData.sku && c.id !== articleData.id
);
@@ -1130,7 +1130,7 @@ export default class ArticleViewLists extends PureComponent {
für
</Typography>
<Chip
label={fatherAttrName}
label={parentAttrName}
size="small"
variant="outlined"
color="primary"
@@ -1163,7 +1163,7 @@ export default class ArticleViewLists extends PureComponent {
>
<Link
component={RouterLink}
to={artikelDetailPath(fatherSku)}
to={artikelDetailPath(parentSku)}
sx={{
minWidth: 0,
flex: 1,
@@ -1196,7 +1196,7 @@ export default class ArticleViewLists extends PureComponent {
transition: 'color 0.15s ease',
}}
>
{fatherArticle?.name || fatherSku}
{parentArticle?.name || parentSku}
</Typography>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.4, mt: 0.2 }}>
<Typography
@@ -1210,7 +1210,7 @@ export default class ArticleViewLists extends PureComponent {
letterSpacing: '0.02em',
}}
>
{fatherSku}
{parentSku}
</Typography>
<OpenInNewIcon sx={{ fontSize: 13, color: 'primary.main', opacity: 0.8 }} />
</Box>
@@ -1316,7 +1316,7 @@ export default class ArticleViewLists extends PureComponent {
const { busy, activeCreatorMode, selectedAttributeId } = this.state;
return (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
{activeCreatorMode === 'father' ? (
{activeCreatorMode === 'parent' ? (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75 }}>
<Box sx={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
@@ -1367,7 +1367,7 @@ export default class ArticleViewLists extends PureComponent {
assignedAttributeIds.has(Number(selectedAttributeId)) ||
selectableAttributes.length === 0
}
onClick={this.handleEnableFather}
onClick={this.handleEnableParent}
sx={{ height: 38, flexShrink: 0 }}
>
{busy ? <CircularProgress size={14} color="inherit" /> : 'Aktivieren'}
@@ -1381,7 +1381,7 @@ export default class ArticleViewLists extends PureComponent {
variant="outlined"
color="primary"
startIcon={<AccountTreeIcon sx={{ fontSize: 16 }} />}
onClick={() => this.setState({ activeCreatorMode: 'father' })}
onClick={() => this.setState({ activeCreatorMode: 'parent' })}
>
Als Vaterartikel aktivieren
</Button>
@@ -1428,8 +1428,8 @@ export default class ArticleViewLists extends PureComponent {
if (!articleData) return null;
const isChild = articleData.fatherArticleId != null || Boolean(articleData.fatherSku);
const isFather = Boolean(articleData.isFather);
const isChild = articleData.parentArticleId != null || Boolean(articleData.parentSku);
const isParent = Boolean(articleData.isParent);
const children = Array.isArray(articleData.children) ? articleData.children : [];
const stuecklisteItems = Array.isArray(articleData.articlelist) ? articleData.articlelist : [];
@@ -1438,7 +1438,7 @@ export default class ArticleViewLists extends PureComponent {
const optionsItems = Array.isArray(articleData.options) ? articleData.options : [];
const hasOptions = articleData.optionslistId != null || optionsItems.length > 0;
// Attributes assigned to this article that cannot be selected for father mode
// Attributes assigned to this article that cannot be selected for parent mode
const assignedAttributeIds = new Set(
(assignedAttributes || []).map((a) => a.attributeId)
);
@@ -1446,31 +1446,31 @@ export default class ArticleViewLists extends PureComponent {
(a) => !assignedAttributeIds.has(a.id)
);
// Filter available articles for Autocomplete (cannot select self, and cannot select father articles)
// Filter available articles for Autocomplete (cannot select self, and cannot select parent articles)
const selectableOtherArticles = allArticles.filter(
(a) => a.sku !== articleData.sku && !a.isFather && !a.is_father
(a) => a.sku !== articleData.sku && !a.isParent && !a.is_parent
);
// Find father attribute definition & values
const fatherAttrDef = isFather
? attributes.find((a) => a.id === articleData.fatherAttributeNameId)
// Find parent attribute definition & values
const parentAttrDef = isParent
? attributes.find((a) => a.id === articleData.parentAttributeNameId)
: null;
const allFatherValues = fatherAttrDef?.values || [];
const allParentValues = parentAttrDef?.values || [];
const assignedValueIds = new Set(
children.map((c) => c.attributeValueId).filter((id) => id != null)
);
const availableFatherValues = allFatherValues.filter((v) => !assignedValueIds.has(v.id));
const availableParentValues = allParentValues.filter((v) => !assignedValueIds.has(v.id));
const fatherSku = articleData.fatherSku;
const fatherArticle = fatherSku ? getArticle(fatherSku) : null;
const parentSku = articleData.parentSku;
const parentArticle = parentSku ? getArticle(parentSku) : null;
return (
<PanelTile wide={1} minHeight={200} color="#fbcfe8">
<Box sx={{ display: 'flex', flexDirection: 'column', height: '100%', gap: 1 }}>
{/* ── Mode 1: Father Article ──────────────────────────────────────── */}
{isFather ? (
{/* ── Mode 1: Parent Article ──────────────────────────────────────── */}
{isParent ? (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 0.75, flex: 1, minHeight: 0 }}>
{isChild && fatherSku ? (
{isChild && parentSku ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mb: 0.5 }}>
<AccountTreeIcon sx={{ color: 'text.secondary', fontSize: 16 }} />
<Typography variant="caption" color="text.secondary">
@@ -1478,7 +1478,7 @@ export default class ArticleViewLists extends PureComponent {
</Typography>
<Link
component={RouterLink}
to={artikelDetailPath(fatherSku)}
to={artikelDetailPath(parentSku)}
sx={{
fontWeight: 600,
fontSize: '0.82rem',
@@ -1488,7 +1488,7 @@ export default class ArticleViewLists extends PureComponent {
'&:hover': { textDecoration: 'underline' },
}}
>
{fatherSku}
{parentSku}
</Link>
</Box>
) : null}
@@ -1499,7 +1499,7 @@ export default class ArticleViewLists extends PureComponent {
Variantsvater für
</Typography>
<Chip
label={articleData.fatherAttributeName || fatherAttrDef?.name || 'Varianten'}
label={articleData.parentAttributeName || parentAttrDef?.name || 'Varianten'}
size="small"
color="primary"
sx={{ height: 20, fontSize: '0.72rem', fontWeight: 600 }}
@@ -1511,7 +1511,7 @@ export default class ArticleViewLists extends PureComponent {
<span>
<IconButton
size="small"
onClick={this.handleDisableFather}
onClick={this.handleDisableParent}
disabled={busy}
sx={{ p: 0.4, color: 'text.secondary', '&:hover': { color: 'error.main' } }}
>
@@ -1524,7 +1524,7 @@ export default class ArticleViewLists extends PureComponent {
{/* Add Child Variant Row */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, mt: 0.5 }}>
{isCustomChildValue || availableFatherValues.length === 0 ? (
{isCustomChildValue || availableParentValues.length === 0 ? (
<TextInput
label="Neuer Wert"
placeholder="Neuer Wert..."
@@ -1551,7 +1551,7 @@ export default class ArticleViewLists extends PureComponent {
<MenuItem value="" disabled>
<em>Variante wählen...</em>
</MenuItem>
{availableFatherValues.map((v) => (
{availableParentValues.map((v) => (
<MenuItem key={v.id} value={v.id}>
{v.value}
</MenuItem>
@@ -1566,7 +1566,7 @@ export default class ArticleViewLists extends PureComponent {
color="primary"
disabled={
busy ||
(isCustomChildValue || availableFatherValues.length === 0
(isCustomChildValue || availableParentValues.length === 0
? !newChildText.trim()
: !selectedChildValueId)
}
@@ -1590,7 +1590,7 @@ export default class ArticleViewLists extends PureComponent {
</Box>
{/* Toggle custom value / predefined value if values exist */}
{allFatherValues.length > 0 && availableFatherValues.length > 0 ? (
{allParentValues.length > 0 && availableParentValues.length > 0 ? (
<Box sx={{ display: 'flex', justifyContent: 'flex-end', mt: -0.25 }}>
<Link
component="button"
@@ -1721,12 +1721,12 @@ export default class ArticleViewLists extends PureComponent {
)}
</Box>
</Box>
) : isChild && fatherSku ? (
) : isChild && parentSku ? (
/* ── Mode 2: Child Variant Article ───────────────────────────────── */
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1, flex: 1, minHeight: 0 }}>
{this.renderChildView(
fatherSku,
fatherArticle,
parentSku,
parentArticle,
attributes,
assignedAttributes
)}