u
This commit is contained in:
@@ -71,8 +71,9 @@ export default class TextInput extends Component {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const isSelect = Boolean(rest.select);
|
const isSelect = Boolean(rest.select);
|
||||||
const effectiveReadOnly = isSelect
|
const isAutocomplete = Boolean(inputProps?.['aria-autocomplete'] || rest['aria-autocomplete']);
|
||||||
? (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly)
|
const effectiveReadOnly = (isSelect || isAutocomplete)
|
||||||
|
? (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? false)
|
||||||
: (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? this.state.readOnly);
|
: (propReadOnly ?? slotProps?.htmlInput?.readOnly ?? inputProps?.readOnly ?? this.state.readOnly);
|
||||||
|
|
||||||
const htmlInput = isSelect
|
const htmlInput = isSelect
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -13,17 +13,54 @@ import { enqueueSnackbar } from 'notistack';
|
|||||||
import { StorageManagerContext } from '../../../api/storageManagerContext';
|
import { StorageManagerContext } from '../../../api/storageManagerContext';
|
||||||
import attributeAutomat, { selectAttributes } from '../../../automats/attributeAutomat';
|
import attributeAutomat, { selectAttributes } from '../../../automats/attributeAutomat';
|
||||||
import propertyAutomat, { selectProperties } from '../../../automats/propertyAutomat';
|
import propertyAutomat, { selectProperties } from '../../../automats/propertyAutomat';
|
||||||
|
import artikelAutomat, { getArticle } from '../../../automats/artikelAutomat';
|
||||||
import { article as articleApi } from '../../../api';
|
import { article as articleApi } from '../../../api';
|
||||||
|
|
||||||
export default class ArticleAttributesTab extends PureComponent {
|
export default class ArticleAttributesTab extends PureComponent {
|
||||||
static contextType = StorageManagerContext;
|
static contextType = StorageManagerContext;
|
||||||
|
|
||||||
|
getArticle() {
|
||||||
|
return this.props.article || getArticle(this.props.sku);
|
||||||
|
}
|
||||||
|
|
||||||
|
isFatherVariantAttribute(attr) {
|
||||||
|
const article = this.getArticle();
|
||||||
|
const isFather = Boolean(article?.isFather ?? article?.is_father);
|
||||||
|
if (!isFather || !attr) return false;
|
||||||
|
const fatherAttrId = article?.fatherAttributeNameId ?? article?.attributeNameId ?? null;
|
||||||
|
if (fatherAttrId != null && Number(attr.id) === Number(fatherAttrId)) return true;
|
||||||
|
if (
|
||||||
|
article?.fatherAttributeName &&
|
||||||
|
String(attr.name).trim().toLowerCase() === String(article.fatherAttributeName).trim().toLowerCase()
|
||||||
|
) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
const initialAttributes = selectAttributes() || [];
|
const initialAttributes = selectAttributes() || [];
|
||||||
const initialProperties = selectProperties() || [];
|
const initialProperties = selectProperties() || [];
|
||||||
|
|
||||||
const firstAttr = initialAttributes[0];
|
const article = props.article || getArticle(props.sku);
|
||||||
|
const isFather = Boolean(article?.isFather ?? article?.is_father);
|
||||||
|
const fatherAttrId = article?.fatherAttributeNameId ?? article?.attributeNameId ?? null;
|
||||||
|
const fatherAttrName = article?.fatherAttributeName;
|
||||||
|
|
||||||
|
const selectableAttrs = initialAttributes.filter((a) => {
|
||||||
|
if (!isFather) return true;
|
||||||
|
if (fatherAttrId != null && Number(a.id) === Number(fatherAttrId)) return false;
|
||||||
|
if (
|
||||||
|
fatherAttrName &&
|
||||||
|
String(a.name).trim().toLowerCase() === String(fatherAttrName).trim().toLowerCase()
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
|
const firstAttr = selectableAttrs[0];
|
||||||
const initialKey = firstAttr
|
const initialKey = firstAttr
|
||||||
? `attr-${firstAttr.id}`
|
? `attr-${firstAttr.id}`
|
||||||
: initialProperties[0]
|
: initialProperties[0]
|
||||||
@@ -53,11 +90,24 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
let nextKey = prev.selectedMerkmalKey;
|
let nextKey = prev.selectedMerkmalKey;
|
||||||
let nextItemId = prev.selectedItemId;
|
let nextItemId = prev.selectedItemId;
|
||||||
|
|
||||||
if (!nextKey) {
|
const selectableAttrs = state.attributes;
|
||||||
const first = state.attributes[0];
|
|
||||||
|
const isCurrentKeyValid =
|
||||||
|
nextKey &&
|
||||||
|
(selectableAttrs.some((a) => `attr-${a.id}` === nextKey) ||
|
||||||
|
prev.globalProperties.some((p) => `prop-${p.id}` === nextKey));
|
||||||
|
|
||||||
|
if (!isCurrentKeyValid) {
|
||||||
|
const first = selectableAttrs[0];
|
||||||
if (first) {
|
if (first) {
|
||||||
nextKey = `attr-${first.id}`;
|
nextKey = `attr-${first.id}`;
|
||||||
nextItemId = first.values?.[0]?.id ?? '';
|
nextItemId = first.values?.[0]?.id ?? '';
|
||||||
|
} else if (prev.globalProperties[0]) {
|
||||||
|
nextKey = `prop-${prev.globalProperties[0].id}`;
|
||||||
|
nextItemId = '';
|
||||||
|
} else {
|
||||||
|
nextKey = '';
|
||||||
|
nextItemId = '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -71,8 +121,9 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
this.unsubProp = propertyAutomat.subscribe((state) => {
|
this.unsubProp = propertyAutomat.subscribe((state) => {
|
||||||
if (this._cancelled) return;
|
if (this._cancelled) return;
|
||||||
this.setState((prev) => {
|
this.setState((prev) => {
|
||||||
|
const selectableAttrs = prev.globalAttributes;
|
||||||
let nextKey = prev.selectedMerkmalKey;
|
let nextKey = prev.selectedMerkmalKey;
|
||||||
if (!nextKey && !prev.globalAttributes.length && state.properties[0]) {
|
if (!nextKey && !selectableAttrs.length && state.properties[0]) {
|
||||||
nextKey = `prop-${state.properties[0].id}`;
|
nextKey = `prop-${state.properties[0].id}`;
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
@@ -82,6 +133,11 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
this.unsubArtikel = artikelAutomat.subscribe(() => {
|
||||||
|
if (this._cancelled) return;
|
||||||
|
this.forceUpdate();
|
||||||
|
});
|
||||||
|
|
||||||
if (attributeAutomat.state.status === 'idle') {
|
if (attributeAutomat.state.status === 'idle') {
|
||||||
void attributeAutomat.actions.load();
|
void attributeAutomat.actions.load();
|
||||||
}
|
}
|
||||||
@@ -102,6 +158,7 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
this._cancelled = true;
|
this._cancelled = true;
|
||||||
this.unsubAttr?.();
|
this.unsubAttr?.();
|
||||||
this.unsubProp?.();
|
this.unsubProp?.();
|
||||||
|
this.unsubArtikel?.();
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadArticleData(sku) {
|
async loadArticleData(sku) {
|
||||||
@@ -164,16 +221,30 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
saving,
|
saving,
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
if (saving || !sku || !selectedMerkmalKey) return;
|
const selectableAttributes = globalAttributes;
|
||||||
|
const merkmalOptions = [
|
||||||
|
...selectableAttributes.map((a) => ({ key: `attr-${a.id}`, id: a.id, type: 'attr' })),
|
||||||
|
...globalProperties.map((p) => ({ key: `prop-${p.id}`, id: p.id, type: 'prop' })),
|
||||||
|
];
|
||||||
|
const selectedOption =
|
||||||
|
merkmalOptions.find((m) => m.key === selectedMerkmalKey) ?? merkmalOptions[0] ?? null;
|
||||||
|
const effectiveKey = selectedOption?.key || selectedMerkmalKey;
|
||||||
|
|
||||||
|
if (saving || !sku || !effectiveKey) return;
|
||||||
const api = this.context?.article || articleApi;
|
const api = this.context?.article || articleApi;
|
||||||
|
|
||||||
if (selectedMerkmalKey.startsWith('attr-')) {
|
if (effectiveKey.startsWith('attr-')) {
|
||||||
const attrId = Number(selectedMerkmalKey.replace('attr-', ''));
|
const attrId = Number(effectiveKey.replace('attr-', ''));
|
||||||
const targetAttr = globalAttributes.find((a) => a.id === attrId);
|
const targetAttr = selectableAttributes.find((a) => a.id === attrId);
|
||||||
if (!targetAttr || !selectedItemId) return;
|
if (!targetAttr || this.isFatherVariantAttribute(targetAttr)) return;
|
||||||
|
|
||||||
|
const effectiveItemId = targetAttr.values?.some((v) => v.id === selectedItemId)
|
||||||
|
? selectedItemId
|
||||||
|
: (targetAttr.values?.[0]?.id ?? '');
|
||||||
|
if (!effectiveItemId) return;
|
||||||
|
|
||||||
const isExclusive = Boolean(targetAttr.exclusive);
|
const isExclusive = Boolean(targetAttr.exclusive);
|
||||||
const targetValId = Number(selectedItemId);
|
const targetValId = Number(effectiveItemId);
|
||||||
const isAlreadyAssigned = assignedAttributes.some((a) => a.attributeValueId === targetValId);
|
const isAlreadyAssigned = assignedAttributes.some((a) => a.attributeValueId === targetValId);
|
||||||
|
|
||||||
if (isAlreadyAssigned) {
|
if (isAlreadyAssigned) {
|
||||||
@@ -320,8 +391,9 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unified list of selectable Merkmale
|
// Unified list of selectable Merkmale
|
||||||
|
const selectableAttributes = globalAttributes;
|
||||||
const merkmalOptions = [
|
const merkmalOptions = [
|
||||||
...globalAttributes.map((a) => ({
|
...selectableAttributes.map((a) => ({
|
||||||
key: `attr-${a.id}`,
|
key: `attr-${a.id}`,
|
||||||
id: a.id,
|
id: a.id,
|
||||||
type: 'attr',
|
type: 'attr',
|
||||||
@@ -339,9 +411,17 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
|
|
||||||
const selectedOption = merkmalOptions.find((m) => m.key === selectedMerkmalKey) ?? null;
|
const selectedOption =
|
||||||
|
merkmalOptions.find((m) => m.key === selectedMerkmalKey) ?? merkmalOptions[0] ?? null;
|
||||||
|
const currentMerkmalKey = selectedOption?.key ?? '';
|
||||||
const isAttr = selectedOption?.type === 'attr';
|
const isAttr = selectedOption?.type === 'attr';
|
||||||
|
const isVariantBlocked = isAttr && this.isFatherVariantAttribute(selectedOption);
|
||||||
const availableItems = selectedOption?.values || [];
|
const availableItems = selectedOption?.values || [];
|
||||||
|
const currentItemId = isAttr
|
||||||
|
? (availableItems.some((v) => v.id === selectedItemId)
|
||||||
|
? selectedItemId
|
||||||
|
: (availableItems[0]?.id ?? ''))
|
||||||
|
: '';
|
||||||
|
|
||||||
// Unified list of assigned groups sorted by definition order
|
// Unified list of assigned groups sorted by definition order
|
||||||
const assignedGroups = [];
|
const assignedGroups = [];
|
||||||
@@ -397,7 +477,7 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
<TextInput
|
<TextInput
|
||||||
select
|
select
|
||||||
label="Merkmal"
|
label="Merkmal"
|
||||||
value={selectedMerkmalKey}
|
value={currentMerkmalKey}
|
||||||
disabled={saving || merkmalOptions.length === 0}
|
disabled={saving || merkmalOptions.length === 0}
|
||||||
onChange={this.handleMerkmalChange}
|
onChange={this.handleMerkmalChange}
|
||||||
sx={{ minWidth: 170 }}
|
sx={{ minWidth: 170 }}
|
||||||
@@ -409,11 +489,15 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
))}
|
))}
|
||||||
</TextInput>
|
</TextInput>
|
||||||
|
|
||||||
{isAttr ? (
|
{isVariantBlocked ? (
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ minWidth: 170, flex: 1 }}>
|
||||||
|
Ein Vaterartikel kann keine Werte für sein Varianten-Merkmal haben.
|
||||||
|
</Typography>
|
||||||
|
) : isAttr ? (
|
||||||
<TextInput
|
<TextInput
|
||||||
select
|
select
|
||||||
label="Wert"
|
label="Wert"
|
||||||
value={selectedItemId}
|
value={currentItemId}
|
||||||
disabled={saving || availableItems.length === 0}
|
disabled={saving || availableItems.length === 0}
|
||||||
onChange={this.handleItemChange}
|
onChange={this.handleItemChange}
|
||||||
sx={{ minWidth: 170 }}
|
sx={{ minWidth: 170 }}
|
||||||
@@ -453,6 +537,7 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
color="primary"
|
color="primary"
|
||||||
disabled={
|
disabled={
|
||||||
saving ||
|
saving ||
|
||||||
|
isVariantBlocked ||
|
||||||
!selectedMerkmalKey ||
|
!selectedMerkmalKey ||
|
||||||
(isAttr ? !selectedItemId || availableItems.length === 0 : !customValueText.trim())
|
(isAttr ? !selectedItemId || availableItems.length === 0 : !customValueText.trim())
|
||||||
}
|
}
|
||||||
@@ -475,14 +560,20 @@ export default class ArticleAttributesTab extends PureComponent {
|
|||||||
{assignedGroups.length > 0 && (
|
{assignedGroups.length > 0 && (
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
|
||||||
{assignedGroups.map((group) => (
|
{assignedGroups.map((group) => (
|
||||||
<Box key={group.key} sx={{ display: 'flex', alignItems: 'center', gap: 1.5 }}>
|
<Box key={group.key} sx={{ display: 'flex', alignItems: 'flex-start', gap: 1.5 }}>
|
||||||
<Typography
|
<Typography
|
||||||
variant="body2"
|
variant="body2"
|
||||||
sx={{ fontWeight: 600, minWidth: 100, color: 'text.secondary' }}
|
sx={{ fontWeight: 600, minWidth: 100, color: 'text.secondary', pt: 0.25 }}
|
||||||
>
|
>
|
||||||
{group.name}:
|
{group.name}:
|
||||||
</Typography>
|
</Typography>
|
||||||
<Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.75 }}>
|
<Box
|
||||||
|
sx={
|
||||||
|
group.key.startsWith('prop-')
|
||||||
|
? { display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 0.75, minWidth: 0 }
|
||||||
|
: { display: 'flex', flexWrap: 'wrap', gap: 0.75 }
|
||||||
|
}
|
||||||
|
>
|
||||||
{group.items.map((item) => (
|
{group.items.map((item) => (
|
||||||
<Chip
|
<Chip
|
||||||
key={item.id}
|
key={item.id}
|
||||||
|
|||||||
@@ -33,6 +33,18 @@ export function createDefaultTheme() {
|
|||||||
transitionDuration: 0,
|
transitionDuration: 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
MuiButton: {
|
||||||
|
defaultProps: {
|
||||||
|
size: 'small',
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
textTransform: 'none',
|
||||||
|
fontWeight: 600,
|
||||||
|
borderRadius: 6,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
MuiPopover: {
|
MuiPopover: {
|
||||||
defaultProps: {
|
defaultProps: {
|
||||||
transitionDuration: 0,
|
transitionDuration: 0,
|
||||||
|
|||||||
Reference in New Issue
Block a user