@configuratorware/configurator-admingui 1.37.3 → 1.38.1

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.
Files changed (25) hide show
  1. package/App/Data.js +4 -4
  2. package/Screens/Designer/SubScreens/Visualization/Components/AreaEditor.js +89 -67
  3. package/Screens/Designer/SubScreens/Visualization/Components/AreaEditorForm.js +203 -0
  4. package/Screens/Designer/SubScreens/Visualization/Containers/Edit/DataLinks.js +15 -11
  5. package/Screens/Designer/SubScreens/Visualization/Containers/Edit/Edit.js +66 -10
  6. package/Screens/Designer/SubScreens/Visualization/Screen.js +1 -1
  7. package/Screens/Item/Components/CopyCreatorItemDialog.js +246 -0
  8. package/Screens/Item/Containers/Edit.js +1 -1
  9. package/Screens/Item/Containers/List.js +3 -1
  10. package/Screens/Item/Reducers/Actions.js +18 -2
  11. package/Screens/Item/Translations.js +10 -3
  12. package/Screens/Itemclassification/Reducers/Actions.js +5 -1
  13. package/package.json +2 -2
  14. package/src/App/Data.js +4 -3
  15. package/src/Screens/Designer/SubScreens/Visualization/Components/AreaEditor.js +30 -5
  16. package/src/Screens/Designer/SubScreens/Visualization/Components/AreaEditorForm.js +139 -0
  17. package/src/Screens/Designer/SubScreens/Visualization/Containers/Edit/DataLinks.js +14 -8
  18. package/src/Screens/Designer/SubScreens/Visualization/Containers/Edit/Edit.js +68 -10
  19. package/src/Screens/Designer/SubScreens/Visualization/Screen.js +1 -1
  20. package/src/Screens/Item/Components/CopyCreatorItemDialog.js +130 -0
  21. package/src/Screens/Item/Containers/Edit.js +1 -1
  22. package/src/Screens/Item/Containers/List.js +8 -4
  23. package/src/Screens/Item/Reducers/Actions.js +10 -1
  24. package/src/Screens/Item/Translations.js +9 -2
  25. package/src/Screens/Itemclassification/Reducers/Actions.js +5 -1
@@ -57,7 +57,11 @@ export const multiplyToPixel = m => v => Math.round(v * m);
57
57
  const sizeMin = 5;
58
58
 
59
59
  export const applyLinks = (prevData, nextData, options = {}) => {
60
- const { positionRatio, baseShapePadding = DEFAULT_BASE_SHAPE_PADDING } = options;
60
+ const {
61
+ positionRatio,
62
+ baseShapePadding = DEFAULT_BASE_SHAPE_PADDING,
63
+ recalculatePosition = true,
64
+ } = options;
61
65
 
62
66
  // set min value for area size
63
67
  if (get(nextData, 'position.width') <= sizeMin) {
@@ -105,11 +109,13 @@ export const applyLinks = (prevData, nextData, options = {}) => {
105
109
  set(nextData, 'baseShape.r', minRadius);
106
110
  }
107
111
  }
108
- // recalculating the position with the baseShape size change ratio
109
- withRatio(prevData, nextData, 'baseShape.width', r => {
110
- isFinite(r) && calcPathValue(nextData, 'position.x', v => v * r);
111
- });
112
- withRatio(prevData, nextData, 'baseShape.height', r => {
113
- isFinite(r) && calcPathValue(nextData, 'position.y', v => v * r);
114
- });
112
+ if (recalculatePosition) {
113
+ // recalculating the position with the baseShape size change ratio
114
+ withRatio(prevData, nextData, 'baseShape.width', r => {
115
+ isFinite(r) && calcPathValue(nextData, 'position.x', v => v * r);
116
+ });
117
+ withRatio(prevData, nextData, 'baseShape.height', r => {
118
+ isFinite(r) && calcPathValue(nextData, 'position.y', v => v * r);
119
+ });
120
+ }
115
121
  };
@@ -48,6 +48,7 @@ import Checkbox from '../../../../../../UIComponents/Checkbox';
48
48
  import FormControlLabel from '../../../../../../UIComponents/FormControlLabel';
49
49
  import { sendMessage } from '../../../../../../Utils/Function/sendMessage';
50
50
  import CircularProgress from '@material-ui/core/CircularProgress';
51
+ import AreaEditorForm from '../../Components/AreaEditorForm';
51
52
 
52
53
  const styles = theme => {
53
54
  const breakPoint = {
@@ -57,22 +58,37 @@ const styles = theme => {
57
58
  editorRoot: {
58
59
  display: 'flex',
59
60
  flexDirection: 'row',
61
+ height: 'calc(100vh - 180px)',
60
62
  '& > $header': {
61
63
  display: 'none',
62
64
  [breakPoint.max]: {
63
65
  display: 'block',
64
66
  },
65
67
  },
68
+ '& > $header3DContainer': {
69
+ display: 'none',
70
+ [breakPoint.max]: {
71
+ display: 'flex',
72
+ },
73
+ },
66
74
  [breakPoint.max]: {
67
75
  flexDirection: 'column',
68
76
  },
69
77
  },
70
78
  leftSide: {
71
79
  marginRight: 2 * theme.spacing.unit,
80
+ flex: 1,
72
81
  },
73
82
  header: {
74
83
  flex: 1,
75
84
  },
85
+ header3D: {
86
+ flex: 1,
87
+ },
88
+ header3DContainer: {
89
+ display: 'flex',
90
+ flexDirection: 'row',
91
+ },
76
92
  rightSide: {
77
93
  flex: 1,
78
94
  '& > $header': {
@@ -81,6 +97,27 @@ const styles = theme => {
81
97
  },
82
98
  },
83
99
  },
100
+ rightSide3D: {
101
+ flex: 1,
102
+ '& > $header3DContainer': {
103
+ [breakPoint.max]: {
104
+ display: 'none',
105
+ },
106
+ },
107
+ display: 'flex',
108
+ flexDirection: 'column',
109
+ maxWidth: '50%',
110
+ marginLeft: 'auto',
111
+ overflow: 'hidden',
112
+ [breakPoint.max]: {
113
+ maxWidth: '100%',
114
+ },
115
+ },
116
+ areaEditorForm: {
117
+ marginLeft: 4 * theme.spacing.unit,
118
+ marginBottom: 2 * theme.spacing.unit,
119
+ flex: 1,
120
+ },
84
121
  title: {
85
122
  fontSize: 20,
86
123
  marginBottom: theme.spacing.unit,
@@ -120,6 +157,12 @@ const styles = theme => {
120
157
  width: '100%',
121
158
  height: '39vw',
122
159
  },
160
+ visualizationContainer3D: {
161
+ width: 'auto',
162
+ flex: 1,
163
+ maxHeight: '100%',
164
+ overflow: 'hidden',
165
+ },
123
166
  loaderContainer: {
124
167
  minHeight: 400,
125
168
  display: 'flex',
@@ -475,10 +518,10 @@ export class VisualizationSettingsEdit extends Component {
475
518
  designArea: { width, height },
476
519
  } = this.props;
477
520
 
478
- this.baseShapeEditor &&
479
- applyLinks(previousData, nextData, {
480
- positionRatio: (width || 1) / (height || 1),
481
- });
521
+ applyLinks(previousData, nextData, {
522
+ positionRatio: (width || 1) / (height || 1),
523
+ recalculatePosition: isKindOf2D(this.props.mode),
524
+ });
482
525
 
483
526
  data.updateValues(nextData);
484
527
  if (data.validate()) {
@@ -520,7 +563,7 @@ export class VisualizationSettingsEdit extends Component {
520
563
  .map(dpm => dpm.title)
521
564
  .join(', ');
522
565
  return (
523
- <div className={classes.header}>
566
+ <div className={isKindOf3D(mode) ? classes.header3D : classes.header}>
524
567
  <Typography variant="h3" className={classes.title}>
525
568
  <TranslationFinder
526
569
  data={designArea.texts}
@@ -572,6 +615,20 @@ export class VisualizationSettingsEdit extends Component {
572
615
  );
573
616
  }
574
617
 
618
+ renderHeader3D(designAreaData) {
619
+ const { classes } = this.props;
620
+ return (
621
+ <div className={classes.header3DContainer}>
622
+ {this.renderHeader()}
623
+ <AreaEditorForm
624
+ designAreaData={designAreaData}
625
+ onChange={this.onChangeData}
626
+ className={classes.areaEditorForm}
627
+ />
628
+ </div>
629
+ );
630
+ }
631
+
575
632
  renderVisualization(designAreaData) {
576
633
  const { mode, classes } = this.props;
577
634
  const { three } = this;
@@ -598,8 +655,8 @@ export class VisualizationSettingsEdit extends Component {
598
655
  if (isKindOf3D(mode)) {
599
656
  return (
600
657
  <React.Fragment>
601
- {this.renderHeader()}
602
- <VisualizationContainer three={three} />
658
+ {this.renderHeader3D(designAreaData)}
659
+ <VisualizationContainer three={three} className={classes.visualizationContainer3D} />
603
660
  </React.Fragment>
604
661
  );
605
662
  }
@@ -607,7 +664,7 @@ export class VisualizationSettingsEdit extends Component {
607
664
  }
608
665
 
609
666
  render() {
610
- const { designArea, classes } = this.props;
667
+ const { designArea, classes, mode } = this.props;
611
668
  const { three } = this;
612
669
  if (!designArea) {
613
670
  return null;
@@ -634,7 +691,7 @@ export class VisualizationSettingsEdit extends Component {
634
691
  <div className={classes.editorRoot}>
635
692
  {loading && <LoadingComponent fullSize />}
636
693
 
637
- {this.renderHeader()}
694
+ {isKindOf3D(mode) ? this.renderHeader3D(data) : this.renderHeader()}
638
695
 
639
696
  {imageElement && (
640
697
  <div className={classes.leftSide}>
@@ -645,11 +702,12 @@ export class VisualizationSettingsEdit extends Component {
645
702
  areaData={data && data.position}
646
703
  areaRatio={areaRatio}
647
704
  ref={this.areaEditorRef}
705
+ onChange={this.updateData}
648
706
  />
649
707
  </div>
650
708
  )}
651
709
 
652
- <div className={classes.rightSide}>
710
+ <div className={isKindOf3D(mode) ? classes.rightSide3D : classes.rightSide}>
653
711
  {this.renderVisualization(data)}
654
712
 
655
713
  {errors && (
@@ -13,7 +13,7 @@ import { provideLocalState, withLocalState } from '../../../../Utils/HOCs/LocalS
13
13
  const styles = () => ({
14
14
  paperRoot: {
15
15
  maxWidth: '90vw',
16
- maxHeight: '90vw',
16
+ maxHeight: '90vh',
17
17
  marginTop: '16px',
18
18
  marginBottom: '16px',
19
19
  },
@@ -0,0 +1,130 @@
1
+ import React, { useState } from "react";
2
+ import { connect } from "react-redux";
3
+ import { t } from '../../../App/i18n';
4
+ import Dialog from '../../../UIComponents/Dialog';
5
+ import FlatButton from '../../../UIComponents/FlatButton';
6
+ import TextField from '../../../UIComponents/TextField';
7
+ import IconButton from '../../../UIComponents/IconButton';
8
+ import Tooltip from '../../../UIComponents/Tooltip';
9
+ import CopyIcon from '@material-ui/icons/FileCopy';
10
+ import Actions from '../Reducers/Actions';
11
+ import IntlInputHoc from "../../../Components/FormFragments/IntlInput";
12
+ import { InputSwitch } from "../../../Components/Form";
13
+ import {identifierPattern} from "../../../App/Data";
14
+ import { showErrorMessage } from "../../../App/Reducers/Frame/Actions";
15
+
16
+ const IntlInput = IntlInputHoc(InputSwitch);
17
+
18
+ const CopyCreatorItemDialog = ({ data, selected, copyCreatorItem, clearSelection, reloadList, showErrorMessage }) => {
19
+
20
+ const [showEditor, setShowEditor] = useState(false);
21
+ const [identifier, setIdentifier] = useState('');
22
+ const [identifierError, setIdentifierError] = useState(null);
23
+ const [titles, setTitles] = useState([]);
24
+ const hasSelection = selected && selected.length === 1;
25
+ let hasOnlyCreatorItemsSelected = hasSelection;
26
+ if (hasSelection) {
27
+ selected.map(selectedIndex => hasOnlyCreatorItemsSelected = data[selectedIndex].configurationMode === 'creator');
28
+ }
29
+
30
+ const onTitleChange = (name, values) => {
31
+ setTitles(values);
32
+ }
33
+
34
+ const onIdentifierChange = (name, identifier) => {
35
+ setIdentifier(identifier);
36
+ }
37
+
38
+ const validateIdentifier = () => {
39
+ const isSameIdentifier = identifier === data[selected[0]].identifier;
40
+ if ( identifier.match(identifierPattern) &&
41
+ identifier.match(identifierPattern)[0] === identifier &&
42
+ !isSameIdentifier
43
+ ) {
44
+ setIdentifierError(null);
45
+ } else {
46
+ setIdentifierError(t('customValidators.identifier'));
47
+ }
48
+ }
49
+
50
+ const onSave = async () => {
51
+ const saveResult = await copyCreatorItem(data[selected[0]].id, identifier, titles);
52
+ if (saveResult.error) {
53
+ showErrorMessage(t('entity.saveError'))
54
+ }
55
+ else {
56
+ clearSelection();
57
+ reloadList();
58
+ }
59
+ }
60
+
61
+ if (!hasOnlyCreatorItemsSelected) {
62
+ return null;
63
+ }
64
+
65
+ return (
66
+ <>
67
+ <Tooltip title={t('item.copyCreatorItemDialogTitle')}>
68
+ <IconButton color={hasSelection ? 'primary' : ''}
69
+ onClick={() => setShowEditor(true)}
70
+ >
71
+ <CopyIcon />
72
+ </IconButton>
73
+ </Tooltip>
74
+ <Dialog
75
+ title={t('item.copyCreatorItemDialogTitle')}
76
+ actions={
77
+ <React.Fragment>
78
+ <FlatButton
79
+ label={t('cancel')}
80
+ onClick={() => setShowEditor(false)}
81
+ />
82
+ <FlatButton
83
+ label={t('duplicate')}
84
+ primary={true}
85
+ onClick={onSave}
86
+ disabled={identifier === '' || identifierError !== null}
87
+ />
88
+ </React.Fragment>
89
+ }
90
+ open={showEditor && hasOnlyCreatorItemsSelected}
91
+ >
92
+ <div style={{ display: 'flex', mindWidth: '620px', flexDirection: 'column' }}>
93
+ <TextField
94
+ label={t('item.copyItemIdentifier')}
95
+ onChange={(evt) => {
96
+ onIdentifierChange(name, evt.target.value);
97
+ }}
98
+ onBlur={validateIdentifier}
99
+ style={{ width: '100%' }}
100
+ error={identifierError !== null}
101
+ helperText={identifierError}
102
+ value={identifier}
103
+ type={'text'}
104
+ />
105
+ <IntlInput
106
+ intl={{
107
+ name: 'title',
108
+ type: 'text',
109
+ }}
110
+ label={t('item.copyItemTitle')}
111
+ onChange={onTitleChange}
112
+ style={{ width: 200 }}
113
+ value={titles}
114
+ />
115
+ </div>
116
+ </Dialog>
117
+ </>
118
+ )
119
+ };
120
+
121
+ const mapDispatchToProps = (dispatch) => ({
122
+ copyCreatorItem: async (sourceItemId, newItemIdentifier, itemTexts) =>
123
+ await dispatch(Actions.copyCreatorItem(sourceItemId, newItemIdentifier, itemTexts)),
124
+ showErrorMessage: (errorMessage) => dispatch(showErrorMessage(errorMessage))
125
+ });
126
+
127
+ export default connect(
128
+ () => null,
129
+ mapDispatchToProps
130
+ )((CopyCreatorItemDialog));
@@ -237,7 +237,7 @@ const formFields = [
237
237
  },
238
238
  {
239
239
  name: 'itemclassifications',
240
- label: 'itemclassifications',
240
+ label: 'ItemClassifications',
241
241
  type: 'dynSource',
242
242
  sourceConfig: {
243
243
  value: 'id',
@@ -1,6 +1,9 @@
1
1
  import React from 'react';
2
2
  import Actions, { ITEM_LIST_KEY, ITEM_REDUCER_NAME } from '../Reducers/Actions';
3
3
  import { T } from '../../../App/i18n';
4
+ import generateConnectedList from '../../../Components/DefaultConnectedList';
5
+ import ListItemStatusEditor from '../Components/ListItemStatusEditor';
6
+ import CopyCreatorItemDialog from "../Components/CopyCreatorItemDialog";
4
7
 
5
8
  const columns = [
6
9
  {
@@ -52,9 +55,6 @@ const listParams = {
52
55
  orderdir: 'asc',
53
56
  };
54
57
 
55
- import generateConnectedList from '../../../Components/DefaultConnectedList';
56
- import ListItemStatusEditor from '../Components/ListItemStatusEditor';
57
-
58
58
  export const ConnectedItemList = generateConnectedList(
59
59
  columns,
60
60
  listParams,
@@ -74,7 +74,11 @@ const ItemList = ({ listProps, ...props }) => (
74
74
  <ConnectedItemList
75
75
  {...props}
76
76
  listProps={{
77
- renderAction: props => <ListItemStatusEditor {...props} />,
77
+ renderAction: props =>
78
+ <>
79
+ <ListItemStatusEditor {...props} />
80
+ <CopyCreatorItemDialog {...props} />
81
+ </>,
78
82
  ...listProps,
79
83
  }}
80
84
  />
@@ -1,5 +1,6 @@
1
1
  export const ITEM_LIST_KEY = 'items';
2
2
  export const ITEM_LIST_URL = 'items';
3
+ export const ITEM_COPY_URL = (id) => `items/${id}/duplicate`;
3
4
  export const ITEM_STATUS_URL = 'items/status';
4
5
  export const ITEM_DATA_KEY = 'item';
5
6
  export const ITEM_REDUCER_NAME = 'itemData';
@@ -43,6 +44,13 @@ const setItemStatusForMultipleItems = (selectedItems, itemStatus) => (dispatch,
43
44
  }))
44
45
  }
45
46
 
47
+ const copyCreatorItem = (sourceItemId, newItemIdentifier, itemTexts) => (dispatch, getState) => {
48
+ return dispatch(apiActions.postData(ITEM_COPY_URL(sourceItemId), {
49
+ "identifier": newItemIdentifier,
50
+ itemTexts
51
+ }))
52
+ };
53
+
46
54
  const setFieldData = (field, value) => (dispatch, getState) => {
47
55
  const entityState = getState()[ITEM_REDUCER_NAME];
48
56
  let configurationMode = entityState.data.configurationMode.value;
@@ -69,5 +77,6 @@ export default {
69
77
  ...actions,
70
78
  setFieldData,
71
79
  setItemStatusForMultipleItems,
72
- loadItemStatus
80
+ loadItemStatus,
81
+ copyCreatorItem
73
82
  };
@@ -18,7 +18,7 @@ require('../../App/i18n').use(
18
18
  editDialogTitle: 'Edit selected entries',
19
19
  editDialogHint:
20
20
  'Use the checkboxes to select one or more entries in the list</br>and then click this icon to set the item status for them.',
21
- itemclassifications: 'Product categories',
21
+ ItemClassifications: 'Product categories',
22
22
  visualizationData: {
23
23
  '2d': "2D",
24
24
  '3d': "3D",
@@ -60,6 +60,9 @@ require('../../App/i18n').use(
60
60
  dialogTitle: 'Edit core data',
61
61
  },
62
62
  },
63
+ copyCreatorItemDialogTitle: 'Copy selected product',
64
+ copyItemIdentifier: 'New identifier',
65
+ copyItemTitle: 'New title',
63
66
  },
64
67
  configuratorQuickView: {
65
68
  buttonText: 'Preview',
@@ -98,9 +101,10 @@ require('../../App/i18n').use(
98
101
  cancel: 'Abbrechen',
99
102
  save: 'Speichern',
100
103
  editDialogTitle: 'Gewählte Einträge bearbeiten',
104
+ duplicate: 'Duplizieren',
101
105
  editDialogHint:
102
106
  'Nutze die Checkboxen in der Liste und klicke dann dieses Icon,</br>um den Status für einen oder mehrere Einträge zu setzen.',
103
- itemclassifications: 'Produktkategorien',
107
+ ItemClassifications: 'Produktkategorien',
104
108
  visualizationData: {
105
109
  '2d': "2D",
106
110
  '3d': "3D",
@@ -123,6 +127,9 @@ require('../../App/i18n').use(
123
127
  searchOrAddAttributeValue: 'Suche einen Attributwert oder lege einen neuen mit dem + an',
124
128
  },
125
129
  headline: 'Dieses Produkt ist folgendermaßen definiert:',
130
+ copyCreatorItemDialogTitle: 'Gewähltes Produkt kopieren',
131
+ copyItemIdentifier: 'Neuer Identifier',
132
+ copyItemTitle: 'Neuer Titel',
126
133
  editorPopup: {
127
134
  attribute: {
128
135
  dialogTitle: 'Attribut hinzufügen',
@@ -4,12 +4,16 @@ export const ITEMCLASSIFICATION_REDUCER_NAME = 'itemclassificationData';
4
4
  export const ITEMCLASSIFICATIONS_URL = 'itemclassifications';
5
5
 
6
6
  import { generateDefaultActions } from '../../../App/Reducers/Entity/Actions';
7
+ import { customDispatchHandler } from '../../Item/DataStructures/Attributes/Actions';
7
8
 
8
9
  const actions = generateDefaultActions(
9
10
  ITEMCLASSIFICATION_LIST_KEY,
10
11
  ITEMCLASSIFICATION_DATA_KEY,
11
12
  ITEMCLASSIFICATION_REDUCER_NAME,
12
- ITEMCLASSIFICATIONS_URL
13
+ ITEMCLASSIFICATIONS_URL,
14
+ {
15
+ customDispatchHandler,
16
+ }
13
17
  );
14
18
 
15
19
  export const { hideDetails, loadEntity, postData } = actions;