@configuratorware/configurator-admingui 1.40.5 → 1.41.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Components/FormFragments/Checkbox.js +6 -2
- package/Screens/Channel/Containers/Edit.js +8 -0
- package/Screens/Channel/Reducers/Reducer.js +3 -0
- package/Screens/Channel/Translations.js +2 -1
- package/Screens/Client/Components/ColorTextField.js +35 -7
- package/Screens/Client/Components/PdfMarkdownField.js +0 -2
- package/Screens/Client/Translations.js +2 -0
- package/Screens/Creator/Components/OptionclassificationEditor.js +89 -12
- package/Screens/Creator/Containers/Edit.js +9 -5
- package/Screens/Creator/Reducers/ConfigurationActions.js +82 -3
- package/Screens/Creator/Reducers/ConfigurationReducer.js +53 -1
- package/Screens/Creator/Translations.js +6 -2
- package/Screens/CurrentClient/Containers/Edit.js +3 -1
- package/Screens/DefaultClient/Containers/Edit.js +3 -1
- package/Screens/DesignProductionMethods/Containers/Edit.js +5 -1
- package/Screens/DesignProductionMethods/Reducers/DesignProductionMethodsReducer.js +3 -0
- package/Screens/DesignProductionMethods/Translations.js +4 -2
- package/Screens/Item/Components/Styles.scss +11 -10
- package/package.json +2 -2
- package/src/Components/Form.js +1 -1
- package/src/Components/FormFragments/Checkbox.js +16 -12
- package/src/Components/FormFragments/index.js +1 -1
- package/src/Screens/Channel/Containers/Edit.js +11 -0
- package/src/Screens/Channel/Reducers/Reducer.js +1 -0
- package/src/Screens/Channel/Translations.js +1 -0
- package/src/Screens/Client/Components/ColorTextField.js +72 -47
- package/src/Screens/Client/Components/PdfMarkdownField.js +1 -2
- package/src/Screens/Client/Translations.js +4 -0
- package/src/Screens/Creator/Components/OptionclassificationEditor.js +93 -17
- package/src/Screens/Creator/Containers/Edit.js +7 -3
- package/src/Screens/Creator/Reducers/ConfigurationActions.js +40 -0
- package/src/Screens/Creator/Reducers/ConfigurationReducer.js +57 -0
- package/src/Screens/Creator/Translations.js +4 -0
- package/src/Screens/CurrentClient/Containers/Edit.js +2 -1
- package/src/Screens/DefaultClient/Containers/Edit.js +2 -1
- package/src/Screens/DesignProductionMethods/Containers/Edit.js +17 -8
- package/src/Screens/DesignProductionMethods/Reducers/DesignProductionMethodsReducer.js +1 -0
- package/src/Screens/DesignProductionMethods/Translations.js +4 -2
- package/src/Screens/Item/Components/Styles.scss +11 -10
|
@@ -21,7 +21,8 @@ require("../../App/i18n").use({
|
|
|
21
21
|
embroidery: 'embroidery',
|
|
22
22
|
doming: 'doming'
|
|
23
23
|
},
|
|
24
|
-
|
|
24
|
+
vectorsRequired: 'Colorize step is mandatory',
|
|
25
|
+
vectorizedLogoMandatory: 'Force using vectorized logo',
|
|
25
26
|
'Maximum Color Amount': 'Maximum Color Amount',
|
|
26
27
|
'Minimum Font Size': 'Minimum Font Size',
|
|
27
28
|
'Has Engraving Background Colors': 'Has Engraving Background Colors',
|
|
@@ -56,7 +57,8 @@ require("../../App/i18n").use({
|
|
|
56
57
|
embroidery: 'Stick',
|
|
57
58
|
doming: 'Doming'
|
|
58
59
|
},
|
|
59
|
-
|
|
60
|
+
vectorsRequired: 'Umfärben erzwingen',
|
|
61
|
+
vectorizedLogoMandatory: 'Vektorisierte Bilddaten müssen verwendet werden',
|
|
60
62
|
'Maximum Color Amount': 'Maximale Farbanzahl',
|
|
61
63
|
'Minimum Font Size': 'Minimale Schriftgröße',
|
|
62
64
|
'Has Engraving Background Colors': 'Hat Gravur-Hintergrundfarben',
|
|
@@ -63,19 +63,20 @@
|
|
|
63
63
|
overflow-y: hidden;
|
|
64
64
|
height: 250px;
|
|
65
65
|
white-space: nowrap;
|
|
66
|
-
|
|
67
|
-
li {
|
|
68
|
-
white-space: normal;
|
|
69
|
-
display: inline-block;
|
|
70
|
-
width: calc(25% - 10px);
|
|
71
|
-
margin-right: 10px;
|
|
72
|
-
height: 100%;
|
|
73
|
-
vertical-align: top;
|
|
74
|
-
padding: 0 10px;
|
|
75
|
-
}
|
|
76
66
|
}
|
|
77
67
|
}
|
|
78
68
|
|
|
69
|
+
.draggable-option-item {
|
|
70
|
+
list-style-type: none;
|
|
71
|
+
white-space: normal;
|
|
72
|
+
display: inline-block;
|
|
73
|
+
width: calc(25% - 10px);
|
|
74
|
+
margin-right: 10px;
|
|
75
|
+
height: 100%;
|
|
76
|
+
vertical-align: top;
|
|
77
|
+
padding: 0 10px;
|
|
78
|
+
}
|
|
79
|
+
|
|
79
80
|
.optionclassificationEditor-item {
|
|
80
81
|
|
|
81
82
|
.headline {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@configuratorware/configurator-admingui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.41.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"private": false,
|
|
6
6
|
"dependencies": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"react-redux-i18n": "^1.9.3",
|
|
30
30
|
"react-router": "^3.2.6",
|
|
31
31
|
"react-sortable-hoc": "^1.11.0",
|
|
32
|
-
"redhotmagma-visualization": "1.
|
|
32
|
+
"redhotmagma-visualization": "1.41.0",
|
|
33
33
|
"redux": "^4.1.0",
|
|
34
34
|
"redux-logger": "^3.0.6",
|
|
35
35
|
"redux-persist": "^5.10.0",
|
package/src/Components/Form.js
CHANGED
|
@@ -192,7 +192,7 @@ class ConfigurableForm extends Component {
|
|
|
192
192
|
onChange: this.onChange,
|
|
193
193
|
onAction: this.onAction,
|
|
194
194
|
renderWrappedInput: this.renderWrappedInput,
|
|
195
|
-
helperText: input.helperText && T(input.helperText)
|
|
195
|
+
helperText: input.helperText && T(input.helperText),
|
|
196
196
|
};
|
|
197
197
|
|
|
198
198
|
if (input.name) {
|
|
@@ -3,20 +3,24 @@ import React from 'react';
|
|
|
3
3
|
import Checkbox from '../../UIComponents/Checkbox';
|
|
4
4
|
import FormControlLabel from '../../UIComponents/FormControlLabel';
|
|
5
5
|
import ErrorWrapper from './ErrorWrapper';
|
|
6
|
+
import Typography from '@material-ui/core/Typography';
|
|
6
7
|
|
|
7
8
|
const checkbox = props => (
|
|
8
|
-
|
|
9
|
-
<
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
<>
|
|
10
|
+
<ErrorWrapper error={props.error}>
|
|
11
|
+
<FormControlLabel
|
|
12
|
+
control={
|
|
13
|
+
<Checkbox
|
|
14
|
+
checked={!!props.value}
|
|
15
|
+
onChange={evt => props.onChange(props.name, evt.target.checked)}
|
|
16
|
+
name={props.name}
|
|
17
|
+
/>
|
|
18
|
+
}
|
|
19
|
+
label={props.label}
|
|
20
|
+
/>
|
|
21
|
+
</ErrorWrapper>
|
|
22
|
+
{props.helperText && <Typography variant={'caption'}>{props.helperText}</Typography>}
|
|
23
|
+
</>
|
|
20
24
|
);
|
|
21
25
|
|
|
22
26
|
export default checkbox;
|
|
@@ -26,7 +26,7 @@ import Value from './Value';
|
|
|
26
26
|
export { Value };
|
|
27
27
|
import HintText from './HintText';
|
|
28
28
|
export { HintText };
|
|
29
|
-
import AttributeValueChip from
|
|
29
|
+
import AttributeValueChip from './AttributeValueChip';
|
|
30
30
|
export { AttributeValueChip };
|
|
31
31
|
|
|
32
32
|
export default {
|
|
@@ -31,6 +31,17 @@ const formFields = [
|
|
|
31
31
|
},
|
|
32
32
|
],
|
|
33
33
|
},
|
|
34
|
+
{
|
|
35
|
+
name: 'settings',
|
|
36
|
+
type: SimpleNestedData,
|
|
37
|
+
fields: [
|
|
38
|
+
{
|
|
39
|
+
name: 'vatrate',
|
|
40
|
+
label: 'VAT rate',
|
|
41
|
+
type: 'number',
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
34
45
|
{
|
|
35
46
|
name: 'globalDiscountPercentage',
|
|
36
47
|
label: 'discountPercentage',
|
|
@@ -1,49 +1,74 @@
|
|
|
1
|
-
import { TextField } from
|
|
2
|
-
import React, { useEffect, useState } from
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
import { TextField } from '@material-ui/core';
|
|
2
|
+
import React, { useEffect, useState } from 'react';
|
|
3
|
+
import { t } from '../../../App/i18n';
|
|
4
|
+
import PropTypes from 'prop-types';
|
|
5
|
+
|
|
6
|
+
const ColorTextField = ({ label, name, value, onChange, error }) => {
|
|
7
|
+
const [customError, setCustomError] = useState(null);
|
|
8
|
+
const [customHelperText, setCustomHelperText] = useState(null);
|
|
9
|
+
|
|
10
|
+
useEffect(() => {
|
|
11
|
+
checkIfColorIsEqualToBackgroundColor(value);
|
|
12
|
+
}, [value]);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
if (error) {
|
|
16
|
+
if (value === '' || value === undefined) {
|
|
17
|
+
setCustomError(error);
|
|
18
|
+
} else {
|
|
19
|
+
validateColor(value);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}, [error]);
|
|
23
|
+
|
|
24
|
+
const validateColor = color => {
|
|
25
|
+
if (color !== '') {
|
|
26
|
+
const s = new Option().style;
|
|
27
|
+
s.color = color;
|
|
28
|
+
const isValidColorText = s.color == color;
|
|
29
|
+
if (isValidColorText || /^#[0-9a-fA-F]{6}$/i.test(color)) {
|
|
30
|
+
setCustomError(null);
|
|
31
|
+
} else if (/^[0-9a-fA-F]{6}$/i.test(color)) {
|
|
32
|
+
onChange(name, '#' + color);
|
|
33
|
+
setCustomError(null);
|
|
34
|
+
} else {
|
|
35
|
+
setCustomError(t('themeColorError'));
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const checkIfColorIsEqualToBackgroundColor = color => {
|
|
41
|
+
if (color.toUpperCase() === '#FFF' || color.toUpperCase() === '#FFFFFF') {
|
|
42
|
+
setCustomHelperText(t('highlightColorInfo'));
|
|
43
|
+
} else {
|
|
44
|
+
setCustomHelperText(null);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<TextField
|
|
50
|
+
label={label || t('Theme - Highlight color')}
|
|
51
|
+
onChange={evt => {
|
|
52
|
+
onChange(name, evt.target.value);
|
|
53
|
+
}}
|
|
54
|
+
onBlur={evt => {
|
|
55
|
+
validateColor(evt.target.value);
|
|
56
|
+
}}
|
|
57
|
+
style={{ width: '100%' }}
|
|
58
|
+
error={customError}
|
|
59
|
+
helperText={customError || customHelperText}
|
|
60
|
+
value={value || ''}
|
|
61
|
+
type={'text'}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
ColorTextField.propTypes = {
|
|
67
|
+
value: PropTypes.string,
|
|
68
|
+
error: PropTypes.node,
|
|
69
|
+
onChange: PropTypes.func.isRequired,
|
|
70
|
+
label: PropTypes.node,
|
|
71
|
+
name: PropTypes.string,
|
|
48
72
|
};
|
|
73
|
+
|
|
49
74
|
export default ColorTextField;
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import PropTypes from 'prop-types';
|
|
3
3
|
import ReactMde from 'react-mde';
|
|
4
4
|
import * as Showdown from 'showdown';
|
|
5
5
|
import 'react-mde/lib/styles/css/react-mde-all.css';
|
|
6
6
|
import FormControlLabel from '../../../UIComponents/FormControlLabel';
|
|
7
7
|
import { withStyles } from '@material-ui/core/styles';
|
|
8
|
-
import debounce from 'lodash/debounce';
|
|
9
8
|
|
|
10
9
|
const MARK_DOWN_FIELD_TRANSLATION_KEY = 'markDownField';
|
|
11
10
|
|
|
@@ -16,6 +16,8 @@ require('../../App/i18n').use(
|
|
|
16
16
|
headerInfo: 'Please check your changes in a generated pdf to make sure it looks as expected',
|
|
17
17
|
footerInfo: 'Please check your changes in a generated pdf to make sure it looks as expected',
|
|
18
18
|
emailHint: 'separate multiple addresses with a semicolon',
|
|
19
|
+
highlightColorInfo:
|
|
20
|
+
'Highlight color is used for highlighting elements in the frontend. Make sure you use a color value that is visible, depending on the background color in your frontend.',
|
|
19
21
|
clientFontMessage:
|
|
20
22
|
'For the font to be rendered correctly in the configuratorware frontend, make sure the filename does not contain any spaces and matches the fonts internal name.',
|
|
21
23
|
},
|
|
@@ -49,6 +51,8 @@ require('../../App/i18n').use(
|
|
|
49
51
|
headerInfo: 'Bitte überprüfe Deine Änderungen in einem generierten PDF',
|
|
50
52
|
footerInfo: 'Bitte überprüfe Deine Änderungen in einem generierten PDF',
|
|
51
53
|
emailHint: 'trenne mehrere Adressen mit einem Semikolon',
|
|
54
|
+
highlightColorInfo:
|
|
55
|
+
'Die Highlight Farbe wird genutzt um Elemente im Frontend zu unterstreichen. Benutze einen Farbwert, der auf der Hintgrundfarbe des Frontends sichtbar ist.',
|
|
52
56
|
clientFontMessage:
|
|
53
57
|
'Für eine korrekte Darstellung der Schrift im configuratorware Fronted darf der Dateiname keine Leerzeichen enthalten und muss dem internen Namen der Schriftart entsprechen.',
|
|
54
58
|
},
|
|
@@ -33,6 +33,12 @@ import { connect } from 'react-redux';
|
|
|
33
33
|
import OptionPriceEditor from './OptionPriceEditor';
|
|
34
34
|
|
|
35
35
|
import '../../Item/Components/Styles.scss';
|
|
36
|
+
import Toggle from "../../../Components/FormFragments/Toggle";
|
|
37
|
+
import {
|
|
38
|
+
arrayMove,
|
|
39
|
+
SortableContainer,
|
|
40
|
+
SortableElement
|
|
41
|
+
} from "react-sortable-hoc";
|
|
36
42
|
|
|
37
43
|
const OptionActionButton = ({ onClick, IconComponent }) => (
|
|
38
44
|
<div className="optionActionButton-wrapper">
|
|
@@ -81,6 +87,36 @@ class OptionclassificationEditorOptionTile extends React.Component {
|
|
|
81
87
|
}
|
|
82
88
|
}
|
|
83
89
|
|
|
90
|
+
const SortableList = SortableContainer(props => {
|
|
91
|
+
return <ul>{props.children}</ul>;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const SortableOptionTile = SortableElement( props => {
|
|
95
|
+
return (
|
|
96
|
+
<OptionTile
|
|
97
|
+
option={props.value}
|
|
98
|
+
onRemoveOption={props.onRemoveOption}
|
|
99
|
+
onEditOption={props.onEditOption}
|
|
100
|
+
onEditPrice={props.onEditPrice}
|
|
101
|
+
priceEditEnabled={props.priceEditEnabled}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const OptionTile = ({option, priceEditEnabled, onRemoveOption, onEditOption, onEditPrice}) => {
|
|
107
|
+
return (
|
|
108
|
+
<li key={option.identifier} className='draggable-option-item'>
|
|
109
|
+
<OptionclassificationEditorOptionTile
|
|
110
|
+
option={option}
|
|
111
|
+
onRemoveOption={onRemoveOption}
|
|
112
|
+
onEditOption={onEditOption}
|
|
113
|
+
onEditPrice={onEditPrice}
|
|
114
|
+
priceEditEnabled={priceEditEnabled}
|
|
115
|
+
/>
|
|
116
|
+
</li>
|
|
117
|
+
);
|
|
118
|
+
};
|
|
119
|
+
|
|
84
120
|
class OptionclassificationEditorItem extends React.Component {
|
|
85
121
|
constructor(props) {
|
|
86
122
|
super(props);
|
|
@@ -118,8 +154,18 @@ class OptionclassificationEditorItem extends React.Component {
|
|
|
118
154
|
this.props.onEditOptionPrice(this.props.optionclassification, option);
|
|
119
155
|
};
|
|
120
156
|
|
|
157
|
+
onSortEnd = ({ oldIndex, newIndex }) => {
|
|
158
|
+
let component = this.props.optionclassification;
|
|
159
|
+
const items = arrayMove(component.selectableOptions, oldIndex, newIndex);
|
|
160
|
+
component.selectableOptions = items.map((item, index) => {
|
|
161
|
+
item.sequenceNumber = index + 1;
|
|
162
|
+
return item;
|
|
163
|
+
});
|
|
164
|
+
this.props.onChange(component);
|
|
165
|
+
};
|
|
166
|
+
|
|
121
167
|
render() {
|
|
122
|
-
const { optionclassification, number, priceEditEnabled } = this.props;
|
|
168
|
+
const { optionclassification, number, sortable, priceEditEnabled } = this.props;
|
|
123
169
|
const { selectableOptions } = optionclassification;
|
|
124
170
|
const { open } = this.state;
|
|
125
171
|
|
|
@@ -158,25 +204,47 @@ class OptionclassificationEditorItem extends React.Component {
|
|
|
158
204
|
icon={<IconAdd />}
|
|
159
205
|
/>
|
|
160
206
|
|
|
161
|
-
{selectableOptions &&
|
|
162
|
-
|
|
163
|
-
<
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
207
|
+
{selectableOptions &&
|
|
208
|
+
(sortable ?
|
|
209
|
+
<div className="options-wrapper">
|
|
210
|
+
<SortableList
|
|
211
|
+
onSortEnd={this.onSortEnd}
|
|
212
|
+
axis="x"
|
|
213
|
+
lockAxis="x"
|
|
214
|
+
pressDelay="150"
|
|
215
|
+
>
|
|
216
|
+
{selectableOptions.map((option, index) => {
|
|
217
|
+
return (
|
|
218
|
+
<SortableOptionTile
|
|
219
|
+
key={option.id}
|
|
220
|
+
index={index}
|
|
221
|
+
value={option}
|
|
222
|
+
onRemoveOption={this.removeOption}
|
|
223
|
+
onEditOption={this.editOption}
|
|
224
|
+
onEditPrice={this.editOptionPrice}
|
|
225
|
+
priceEditEnabled={priceEditEnabled}
|
|
226
|
+
/>
|
|
227
|
+
);
|
|
228
|
+
})}
|
|
229
|
+
</SortableList>
|
|
230
|
+
</div>
|
|
231
|
+
:
|
|
232
|
+
<div className="options-wrapper">
|
|
233
|
+
<ul>
|
|
234
|
+
{selectableOptions.map(option => {
|
|
235
|
+
return (
|
|
236
|
+
<OptionTile
|
|
168
237
|
option={option}
|
|
169
238
|
onRemoveOption={this.removeOption}
|
|
170
239
|
onEditOption={this.editOption}
|
|
171
240
|
onEditPrice={this.editOptionPrice}
|
|
172
241
|
priceEditEnabled={priceEditEnabled}
|
|
173
242
|
/>
|
|
174
|
-
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
</
|
|
178
|
-
|
|
179
|
-
)}
|
|
243
|
+
);
|
|
244
|
+
})}
|
|
245
|
+
</ul>
|
|
246
|
+
</div>
|
|
247
|
+
)}
|
|
180
248
|
</div>
|
|
181
249
|
</div>
|
|
182
250
|
);
|
|
@@ -207,18 +275,24 @@ class OptionclassificationEditor extends React.Component {
|
|
|
207
275
|
};
|
|
208
276
|
|
|
209
277
|
render() {
|
|
210
|
-
const { value, priceEditEnabled, ...otherProps } = this.props;
|
|
278
|
+
const { value, priceEditEnabled, overrideOptionOrder, setOverrideOptionOrder, showOverrideOptionOrderSaveHint, ...otherProps } = this.props;
|
|
211
279
|
|
|
212
280
|
return (
|
|
213
281
|
<div className="optionclassificationEditor-wrapper">
|
|
214
282
|
<div className="optionclassificationEditor-info">
|
|
215
283
|
{T(
|
|
216
284
|
value.length > 0
|
|
217
|
-
? 'constructionPatterns.
|
|
285
|
+
? 'constructionPatterns.option.sourceHeadline'
|
|
218
286
|
: 'constructionPatterns.components.noComponentsSelected'
|
|
219
287
|
)}
|
|
220
288
|
</div>
|
|
221
|
-
|
|
289
|
+
<Toggle
|
|
290
|
+
label={T('constructionPatterns.overrideOptionOrder')}
|
|
291
|
+
value={overrideOptionOrder}
|
|
292
|
+
onChange={(name, checked) => setOverrideOptionOrder(checked)}
|
|
293
|
+
fullWidth={false}
|
|
294
|
+
error={showOverrideOptionOrderSaveHint ? T('constructionPatterns.overrideOptionOrderHint') : null}
|
|
295
|
+
/>
|
|
222
296
|
{value.map((oc, idx) => {
|
|
223
297
|
return (
|
|
224
298
|
<OptionclassificationEditorItem
|
|
@@ -230,6 +304,7 @@ class OptionclassificationEditor extends React.Component {
|
|
|
230
304
|
onEditOption={this.editOption}
|
|
231
305
|
onEditOptionPrice={this.editOptionPrice}
|
|
232
306
|
priceEditEnabled={priceEditEnabled}
|
|
307
|
+
sortable={overrideOptionOrder}
|
|
233
308
|
/>
|
|
234
309
|
);
|
|
235
310
|
})}
|
|
@@ -385,6 +460,7 @@ function mapStateToProps({ baseConfigurationData, creatorData }) {
|
|
|
385
460
|
selectedOption: baseConfigurationData.selectedOption,
|
|
386
461
|
options: baseConfigurationData.selectableOptions,
|
|
387
462
|
product: creatorData.source,
|
|
463
|
+
showOverrideOptionOrderSaveHint: baseConfigurationData.showOverrideOptionOrderSaveHint,
|
|
388
464
|
priceEditEnabled:
|
|
389
465
|
_.get(baseConfigurationData, 'dataSources.settings.calculationMethod') === 'deltaprices',
|
|
390
466
|
};
|
|
@@ -11,6 +11,7 @@ import configurationActions, {
|
|
|
11
11
|
CONFIGURATION_REDUCER_NAME,
|
|
12
12
|
getAdminModeHash,
|
|
13
13
|
setOverwriteComponentOrder,
|
|
14
|
+
setOverrideOptionOrder,
|
|
14
15
|
} from '../Reducers/ConfigurationActions';
|
|
15
16
|
import EditorPopup from '../../Item/Components/AttributeEditorPopup';
|
|
16
17
|
import '../../Item/Containers/Styles.scss';
|
|
@@ -262,7 +263,7 @@ class ConfigurationDataFormBase extends Component {
|
|
|
262
263
|
};
|
|
263
264
|
|
|
264
265
|
renderTabContent(tab) {
|
|
265
|
-
const { entityState, setOverwriteComponentOrder } = this.props;
|
|
266
|
+
const { entityState, setOverwriteComponentOrder, setOverrideOptionOrder } = this.props;
|
|
266
267
|
const source = _.get(entityState, 'dataSources.selectableComponents.data');
|
|
267
268
|
const value = _.get(entityState, 'data.selectableComponents.value');
|
|
268
269
|
const headLine = (
|
|
@@ -330,6 +331,8 @@ class ConfigurationDataFormBase extends Component {
|
|
|
330
331
|
name="selectableComponents"
|
|
331
332
|
value={value}
|
|
332
333
|
onChange={this.onChange}
|
|
334
|
+
overrideOptionOrder={Boolean(_.get(entityState, 'data.item.value.overrideOptionOrder'))}
|
|
335
|
+
setOverrideOptionOrder={setOverrideOptionOrder}
|
|
333
336
|
/>
|
|
334
337
|
</div>
|
|
335
338
|
);
|
|
@@ -374,14 +377,15 @@ class ConfigurationDataFormBase extends Component {
|
|
|
374
377
|
}
|
|
375
378
|
}
|
|
376
379
|
|
|
377
|
-
function
|
|
380
|
+
function withOverwriteOrder(Component) {
|
|
378
381
|
return connect(() => ({}), {
|
|
379
382
|
setOverwriteComponentOrder,
|
|
383
|
+
setOverrideOptionOrder
|
|
380
384
|
})(Component);
|
|
381
385
|
}
|
|
382
386
|
|
|
383
387
|
const ConfigurationDataForm = connectDefault(
|
|
384
|
-
|
|
388
|
+
withOverwriteOrder(ConfigurationDataFormBase),
|
|
385
389
|
CONFIGURATION_REDUCER_NAME,
|
|
386
390
|
configurationActions.setFieldData,
|
|
387
391
|
configurationActions.postData
|
|
@@ -43,6 +43,7 @@ export const CONFIGURATION_HIDE_DESIGNER_PREVIEW_ERROR = 'CONFIGURATION_HIDE_DES
|
|
|
43
43
|
export const CONFIGURATION_HIDE_CREATOR_PREVIEW_ERROR = 'CONFIGURATION_HIDE_CREATOR_PREVIEW_ERROR';
|
|
44
44
|
export const CONFIGURATION_SHOW_CONFIGURATOR_ADMIN_MODE = 'CONFIGURATION_SHOW_CONFIGURATOR_ADMIN_MODE';
|
|
45
45
|
export const CONFIGURATION_SET_OVERWRITE_COMPONENT_ORDER = 'CONFIGURATION_SET_OVERWRITE_COMPONENT_ORDER';
|
|
46
|
+
export const CONFIGURATION_SET_OVERRIDE_OPTION_ORDER = 'CONFIGURATION_SET_OVERRIDE_OPTION_ORDER';
|
|
46
47
|
|
|
47
48
|
export const showOptionSelection = (show = false, component = null) => ({
|
|
48
49
|
type: CONFIGURATION_SHOW_OPTION_SELECTION,
|
|
@@ -212,3 +213,42 @@ export const setOverwriteComponentOrder = value => async (dispatch, getState) =>
|
|
|
212
213
|
});
|
|
213
214
|
}
|
|
214
215
|
};
|
|
216
|
+
|
|
217
|
+
export const setOverrideOptionOrder = value => async (dispatch, getState) => {
|
|
218
|
+
const entityState = getState()[CONFIGURATION_REDUCER_NAME];
|
|
219
|
+
await dispatch({
|
|
220
|
+
type: CONFIGURATION_SET_OVERRIDE_OPTION_ORDER,
|
|
221
|
+
value,
|
|
222
|
+
});
|
|
223
|
+
const item = get(entityState, 'data.item.value');
|
|
224
|
+
if (item) {
|
|
225
|
+
const { stock, ...itemData } = item;
|
|
226
|
+
const data = { ...itemData, overrideOptionOrder: value };
|
|
227
|
+
const url = `items`;
|
|
228
|
+
return dispatch(postData({ url }, data, CONFIGURATION_SAVE_ITEM)).then(apiAction => {
|
|
229
|
+
switch (apiAction.type) {
|
|
230
|
+
case RECEIVE_DATA: {
|
|
231
|
+
dispatch(showInfoMessage(t('entity.saveOk')));
|
|
232
|
+
dispatch(saveConfigurationSuccess(apiAction.data));
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
case NETWORK_ERROR: {
|
|
237
|
+
const state = getState();
|
|
238
|
+
const source = state.dataBySource[apiAction.key];
|
|
239
|
+
if (source.isPosted) {
|
|
240
|
+
// data is saved
|
|
241
|
+
dispatch(showErrorMessage(t('entity.saveError'), apiAction.error));
|
|
242
|
+
} else {
|
|
243
|
+
dispatch(showErrorMessage(t('entity.loadError'), apiAction.error));
|
|
244
|
+
}
|
|
245
|
+
dispatch({
|
|
246
|
+
type: CONFIGURATION_SET_OVERRIDE_OPTION_ORDER,
|
|
247
|
+
value: !value,
|
|
248
|
+
});
|
|
249
|
+
return false;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
};
|