@configuratorware/configurator-admingui 1.37.3 → 1.38.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/App/Data.js CHANGED
@@ -3,7 +3,7 @@
3
3
  Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
- exports.getErrorsFromValidatedDataObject = exports.validateDataObject = exports.DataValidator = exports["default"] = exports.whiteSpacePattern = void 0;
6
+ exports.getErrorsFromValidatedDataObject = exports.validateDataObject = exports.DataValidator = exports["default"] = exports.identifierPattern = exports.whiteSpacePattern = void 0;
7
7
 
8
8
  var _lodash = _interopRequireDefault(require("lodash"));
9
9
 
@@ -29,6 +29,8 @@ function _defineProperty(obj, key, value) { if (key in obj) { Object.definePrope
29
29
 
30
30
  var whiteSpacePattern = /^[\S]+$/;
31
31
  exports.whiteSpacePattern = whiteSpacePattern;
32
+ var identifierPattern = /^[A-za-z0-9_.-]+$/;
33
+ exports.identifierPattern = identifierPattern;
32
34
 
33
35
  _validate.validate.validators.nonEmptyArray = function (value, options) {
34
36
  if (options && options === true && (0, _isArray["default"])(value) && value.length !== 0) {
@@ -55,9 +57,7 @@ _validate.validate.validators.numericOrEmpty = function (value, options) {
55
57
  };
56
58
 
57
59
  _validate.validate.validators.identifier = function (value, options) {
58
- var pattern = /^[A-za-z0-9_.-]+$/;
59
-
60
- if (options && options === true && typeof value === 'string' && value.match(pattern) && value.match(pattern)[0] === value) {
60
+ if (options && options === true && typeof value === 'string' && value.match(identifierPattern) && value.match(identifierPattern)[0] === value) {
61
61
  return null;
62
62
  }
63
63
 
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+
3
+ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
+
5
+ Object.defineProperty(exports, "__esModule", {
6
+ value: true
7
+ });
8
+ exports["default"] = void 0;
9
+
10
+ var _react = _interopRequireWildcard(require("react"));
11
+
12
+ var _reactRedux = require("react-redux");
13
+
14
+ var _i18n = require("../../../App/i18n");
15
+
16
+ var _Dialog = _interopRequireDefault(require("../../../UIComponents/Dialog"));
17
+
18
+ var _FlatButton = _interopRequireDefault(require("../../../UIComponents/FlatButton"));
19
+
20
+ var _TextField = _interopRequireDefault(require("../../../UIComponents/TextField"));
21
+
22
+ var _IconButton = _interopRequireDefault(require("../../../UIComponents/IconButton"));
23
+
24
+ var _Tooltip = _interopRequireDefault(require("../../../UIComponents/Tooltip"));
25
+
26
+ var _FileCopy = _interopRequireDefault(require("@material-ui/icons/FileCopy"));
27
+
28
+ var _Actions = _interopRequireDefault(require("../Reducers/Actions"));
29
+
30
+ var _IntlInput = _interopRequireDefault(require("../../../Components/FormFragments/IntlInput"));
31
+
32
+ var _Form = require("../../../Components/Form");
33
+
34
+ var _Data = require("../../../App/Data");
35
+
36
+ var _Actions2 = require("../../../App/Reducers/Frame/Actions");
37
+
38
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
39
+
40
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
41
+
42
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof(obj) !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
43
+
44
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
45
+
46
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
47
+
48
+ function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }
49
+
50
+ function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }
51
+
52
+ function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }
53
+
54
+ function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }
55
+
56
+ function _iterableToArrayLimit(arr, i) { var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]; if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }
57
+
58
+ function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
59
+
60
+ var IntlInput = (0, _IntlInput["default"])(_Form.InputSwitch);
61
+
62
+ var CopyCreatorItemDialog = function CopyCreatorItemDialog(_ref) {
63
+ var data = _ref.data,
64
+ selected = _ref.selected,
65
+ copyCreatorItem = _ref.copyCreatorItem,
66
+ clearSelection = _ref.clearSelection,
67
+ reloadList = _ref.reloadList,
68
+ showErrorMessage = _ref.showErrorMessage;
69
+
70
+ var _useState = (0, _react.useState)(false),
71
+ _useState2 = _slicedToArray(_useState, 2),
72
+ showEditor = _useState2[0],
73
+ setShowEditor = _useState2[1];
74
+
75
+ var _useState3 = (0, _react.useState)(''),
76
+ _useState4 = _slicedToArray(_useState3, 2),
77
+ identifier = _useState4[0],
78
+ setIdentifier = _useState4[1];
79
+
80
+ var _useState5 = (0, _react.useState)(null),
81
+ _useState6 = _slicedToArray(_useState5, 2),
82
+ identifierError = _useState6[0],
83
+ setIdentifierError = _useState6[1];
84
+
85
+ var _useState7 = (0, _react.useState)([]),
86
+ _useState8 = _slicedToArray(_useState7, 2),
87
+ titles = _useState8[0],
88
+ setTitles = _useState8[1];
89
+
90
+ var hasSelection = selected && selected.length === 1;
91
+ var hasOnlyCreatorItemsSelected = hasSelection;
92
+
93
+ if (hasSelection) {
94
+ selected.map(function (selectedIndex) {
95
+ return hasOnlyCreatorItemsSelected = data[selectedIndex].configurationMode === 'creator';
96
+ });
97
+ }
98
+
99
+ var onTitleChange = function onTitleChange(name, values) {
100
+ setTitles(values);
101
+ };
102
+
103
+ var onIdentifierChange = function onIdentifierChange(name, identifier) {
104
+ setIdentifier(identifier);
105
+ };
106
+
107
+ var validateIdentifier = function validateIdentifier() {
108
+ var isSameIdentifier = identifier === data[selected[0]].identifier;
109
+
110
+ if (identifier.match(_Data.identifierPattern) && identifier.match(_Data.identifierPattern)[0] === identifier && !isSameIdentifier) {
111
+ setIdentifierError(null);
112
+ } else {
113
+ setIdentifierError((0, _i18n.t)('customValidators.identifier'));
114
+ }
115
+ };
116
+
117
+ var onSave = /*#__PURE__*/function () {
118
+ var _ref2 = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee() {
119
+ var saveResult;
120
+ return regeneratorRuntime.wrap(function _callee$(_context) {
121
+ while (1) {
122
+ switch (_context.prev = _context.next) {
123
+ case 0:
124
+ _context.next = 2;
125
+ return copyCreatorItem(data[selected[0]].id, identifier, titles);
126
+
127
+ case 2:
128
+ saveResult = _context.sent;
129
+
130
+ if (saveResult.error) {
131
+ showErrorMessage((0, _i18n.t)('entity.saveError'));
132
+ } else {
133
+ clearSelection();
134
+ reloadList();
135
+ }
136
+
137
+ case 4:
138
+ case "end":
139
+ return _context.stop();
140
+ }
141
+ }
142
+ }, _callee);
143
+ }));
144
+
145
+ return function onSave() {
146
+ return _ref2.apply(this, arguments);
147
+ };
148
+ }();
149
+
150
+ if (!hasOnlyCreatorItemsSelected) {
151
+ return null;
152
+ }
153
+
154
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_Tooltip["default"], {
155
+ title: (0, _i18n.t)('item.copyCreatorItemDialogTitle')
156
+ }, /*#__PURE__*/_react["default"].createElement(_IconButton["default"], {
157
+ color: hasSelection ? 'primary' : '',
158
+ onClick: function onClick() {
159
+ return setShowEditor(true);
160
+ }
161
+ }, /*#__PURE__*/_react["default"].createElement(_FileCopy["default"], null))), /*#__PURE__*/_react["default"].createElement(_Dialog["default"], {
162
+ title: (0, _i18n.t)('item.copyCreatorItemDialogTitle'),
163
+ actions: /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_FlatButton["default"], {
164
+ label: (0, _i18n.t)('cancel'),
165
+ onClick: function onClick() {
166
+ return setShowEditor(false);
167
+ }
168
+ }), /*#__PURE__*/_react["default"].createElement(_FlatButton["default"], {
169
+ label: (0, _i18n.t)('duplicate'),
170
+ primary: true,
171
+ onClick: onSave,
172
+ disabled: identifier === '' || identifierError !== null
173
+ })),
174
+ open: showEditor && hasOnlyCreatorItemsSelected
175
+ }, /*#__PURE__*/_react["default"].createElement("div", {
176
+ style: {
177
+ display: 'flex',
178
+ mindWidth: '620px',
179
+ flexDirection: 'column'
180
+ }
181
+ }, /*#__PURE__*/_react["default"].createElement(_TextField["default"], {
182
+ label: (0, _i18n.t)('item.copyItemIdentifier'),
183
+ onChange: function onChange(evt) {
184
+ onIdentifierChange(name, evt.target.value);
185
+ },
186
+ onBlur: validateIdentifier,
187
+ style: {
188
+ width: '100%'
189
+ },
190
+ error: identifierError !== null,
191
+ helperText: identifierError,
192
+ value: identifier,
193
+ type: 'text'
194
+ }), /*#__PURE__*/_react["default"].createElement(IntlInput, {
195
+ intl: {
196
+ name: 'title',
197
+ type: 'text'
198
+ },
199
+ label: (0, _i18n.t)('item.copyItemTitle'),
200
+ onChange: onTitleChange,
201
+ style: {
202
+ width: 200
203
+ },
204
+ value: titles
205
+ }))));
206
+ };
207
+
208
+ var mapDispatchToProps = function mapDispatchToProps(dispatch) {
209
+ return {
210
+ copyCreatorItem: function () {
211
+ var _copyCreatorItem = _asyncToGenerator( /*#__PURE__*/regeneratorRuntime.mark(function _callee2(sourceItemId, newItemIdentifier, itemTexts) {
212
+ return regeneratorRuntime.wrap(function _callee2$(_context2) {
213
+ while (1) {
214
+ switch (_context2.prev = _context2.next) {
215
+ case 0:
216
+ _context2.next = 2;
217
+ return dispatch(_Actions["default"].copyCreatorItem(sourceItemId, newItemIdentifier, itemTexts));
218
+
219
+ case 2:
220
+ return _context2.abrupt("return", _context2.sent);
221
+
222
+ case 3:
223
+ case "end":
224
+ return _context2.stop();
225
+ }
226
+ }
227
+ }, _callee2);
228
+ }));
229
+
230
+ function copyCreatorItem(_x, _x2, _x3) {
231
+ return _copyCreatorItem.apply(this, arguments);
232
+ }
233
+
234
+ return copyCreatorItem;
235
+ }(),
236
+ showErrorMessage: function showErrorMessage(errorMessage) {
237
+ return dispatch((0, _Actions2.showErrorMessage)(errorMessage));
238
+ }
239
+ };
240
+ };
241
+
242
+ var _default = (0, _reactRedux.connect)(function () {
243
+ return null;
244
+ }, mapDispatchToProps)(CopyCreatorItemDialog);
245
+
246
+ exports["default"] = _default;
@@ -371,7 +371,7 @@ var formFields = [{
371
371
  }
372
372
  }, {
373
373
  name: 'itemclassifications',
374
- label: 'itemclassifications',
374
+ label: 'ItemClassifications',
375
375
  type: 'dynSource',
376
376
  sourceConfig: {
377
377
  value: 'id',
@@ -17,6 +17,8 @@ var _DefaultConnectedList = _interopRequireDefault(require("../../../Components/
17
17
 
18
18
  var _ListItemStatusEditor = _interopRequireDefault(require("../Components/ListItemStatusEditor"));
19
19
 
20
+ var _CopyCreatorItemDialog = _interopRequireDefault(require("../Components/CopyCreatorItemDialog"));
21
+
20
22
  var _excluded = ["listProps"];
21
23
 
22
24
  function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
@@ -108,7 +110,7 @@ var ItemList = function ItemList(_ref) {
108
110
  return /*#__PURE__*/_react["default"].createElement(ConnectedItemList, _extends({}, props, {
109
111
  listProps: _objectSpread({
110
112
  renderAction: function renderAction(props) {
111
- return /*#__PURE__*/_react["default"].createElement(_ListItemStatusEditor["default"], props);
113
+ return /*#__PURE__*/_react["default"].createElement(_react["default"].Fragment, null, /*#__PURE__*/_react["default"].createElement(_ListItemStatusEditor["default"], props), /*#__PURE__*/_react["default"].createElement(_CopyCreatorItemDialog["default"], props));
112
114
  }
113
115
  }, listProps)
114
116
  }));
@@ -5,7 +5,7 @@ function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "functi
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports["default"] = exports.ITEM_REDUCER_NAME = exports.ITEM_DATA_KEY = exports.ITEM_STATUS_URL = exports.ITEM_LIST_URL = exports.ITEM_LIST_KEY = void 0;
8
+ exports["default"] = exports.ITEM_REDUCER_NAME = exports.ITEM_DATA_KEY = exports.ITEM_STATUS_URL = exports.ITEM_COPY_URL = exports.ITEM_LIST_URL = exports.ITEM_LIST_KEY = void 0;
9
9
 
10
10
  var apiActions = _interopRequireWildcard(require("../../../App/Reducers/Api/Actions"));
11
11
 
@@ -27,6 +27,12 @@ var ITEM_LIST_KEY = 'items';
27
27
  exports.ITEM_LIST_KEY = ITEM_LIST_KEY;
28
28
  var ITEM_LIST_URL = 'items';
29
29
  exports.ITEM_LIST_URL = ITEM_LIST_URL;
30
+
31
+ var ITEM_COPY_URL = function ITEM_COPY_URL(id) {
32
+ return "items/".concat(id, "/duplicate");
33
+ };
34
+
35
+ exports.ITEM_COPY_URL = ITEM_COPY_URL;
30
36
  var ITEM_STATUS_URL = 'items/status';
31
37
  exports.ITEM_STATUS_URL = ITEM_STATUS_URL;
32
38
  var ITEM_DATA_KEY = 'item';
@@ -73,6 +79,15 @@ var setItemStatusForMultipleItems = function setItemStatusForMultipleItems(selec
73
79
  };
74
80
  };
75
81
 
82
+ var copyCreatorItem = function copyCreatorItem(sourceItemId, newItemIdentifier, itemTexts) {
83
+ return function (dispatch, getState) {
84
+ return dispatch(apiActions.postData(ITEM_COPY_URL(sourceItemId), {
85
+ "identifier": newItemIdentifier,
86
+ itemTexts: itemTexts
87
+ }));
88
+ };
89
+ };
90
+
76
91
  var setFieldData = function setFieldData(field, value) {
77
92
  return function (dispatch, getState) {
78
93
  var entityState = getState()[ITEM_REDUCER_NAME];
@@ -91,7 +106,8 @@ var setFieldData = function setFieldData(field, value) {
91
106
  var _default = _objectSpread(_objectSpread({}, actions), {}, {
92
107
  setFieldData: setFieldData,
93
108
  setItemStatusForMultipleItems: setItemStatusForMultipleItems,
94
- loadItemStatus: loadItemStatus
109
+ loadItemStatus: loadItemStatus,
110
+ copyCreatorItem: copyCreatorItem
95
111
  });
96
112
 
97
113
  exports["default"] = _default;
@@ -17,7 +17,7 @@ require("../../App/i18n").use({
17
17
  save: 'Save',
18
18
  editDialogTitle: 'Edit selected entries',
19
19
  editDialogHint: '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.',
20
- itemclassifications: 'Product categories',
20
+ ItemClassifications: 'Product categories',
21
21
  visualizationData: {
22
22
  '2d': "2D",
23
23
  '3d': "3D",
@@ -56,7 +56,10 @@ require("../../App/i18n").use({
56
56
  coreData: {
57
57
  dialogTitle: 'Edit core data'
58
58
  }
59
- }
59
+ },
60
+ copyCreatorItemDialogTitle: 'Copy selected product',
61
+ copyItemIdentifier: 'New identifier',
62
+ copyItemTitle: 'New title'
60
63
  },
61
64
  configuratorQuickView: {
62
65
  buttonText: 'Preview'
@@ -93,8 +96,9 @@ require("../../App/i18n").use({
93
96
  cancel: 'Abbrechen',
94
97
  save: 'Speichern',
95
98
  editDialogTitle: 'Gewählte Einträge bearbeiten',
99
+ duplicate: 'Duplizieren',
96
100
  editDialogHint: 'Nutze die Checkboxen in der Liste und klicke dann dieses Icon,</br>um den Status für einen oder mehrere Einträge zu setzen.',
97
- itemclassifications: 'Produktkategorien',
101
+ ItemClassifications: 'Produktkategorien',
98
102
  visualizationData: {
99
103
  '2d': "2D",
100
104
  '3d': "3D",
@@ -117,6 +121,9 @@ require("../../App/i18n").use({
117
121
  searchOrAddAttributeValue: 'Suche einen Attributwert oder lege einen neuen mit dem + an'
118
122
  },
119
123
  headline: 'Dieses Produkt ist folgendermaßen definiert:',
124
+ copyCreatorItemDialogTitle: 'Gewähltes Produkt kopieren',
125
+ copyItemIdentifier: 'Neuer Identifier',
126
+ copyItemTitle: 'Neuer Titel',
120
127
  editorPopup: {
121
128
  attribute: {
122
129
  dialogTitle: 'Attribut hinzufügen'
@@ -7,6 +7,8 @@ exports["default"] = exports.postData = exports.loadEntity = exports.hideDetails
7
7
 
8
8
  var _Actions = require("../../../App/Reducers/Entity/Actions");
9
9
 
10
+ var _Actions2 = require("../../Item/DataStructures/Attributes/Actions");
11
+
10
12
  var ITEMCLASSIFICATION_LIST_KEY = 'itemclassifications';
11
13
  exports.ITEMCLASSIFICATION_LIST_KEY = ITEMCLASSIFICATION_LIST_KEY;
12
14
  var ITEMCLASSIFICATION_DATA_KEY = 'itemclassification';
@@ -15,7 +17,9 @@ var ITEMCLASSIFICATION_REDUCER_NAME = 'itemclassificationData';
15
17
  exports.ITEMCLASSIFICATION_REDUCER_NAME = ITEMCLASSIFICATION_REDUCER_NAME;
16
18
  var ITEMCLASSIFICATIONS_URL = 'itemclassifications';
17
19
  exports.ITEMCLASSIFICATIONS_URL = ITEMCLASSIFICATIONS_URL;
18
- var actions = (0, _Actions.generateDefaultActions)(ITEMCLASSIFICATION_LIST_KEY, ITEMCLASSIFICATION_DATA_KEY, ITEMCLASSIFICATION_REDUCER_NAME, ITEMCLASSIFICATIONS_URL);
20
+ var actions = (0, _Actions.generateDefaultActions)(ITEMCLASSIFICATION_LIST_KEY, ITEMCLASSIFICATION_DATA_KEY, ITEMCLASSIFICATION_REDUCER_NAME, ITEMCLASSIFICATIONS_URL, {
21
+ customDispatchHandler: _Actions2.customDispatchHandler
22
+ });
19
23
  var hideDetails = actions.hideDetails,
20
24
  loadEntity = actions.loadEntity,
21
25
  postData = actions.postData;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-admingui",
3
- "version": "1.37.3",
3
+ "version": "1.38.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.37.3",
32
+ "redhotmagma-visualization": "1.38.0",
33
33
  "redux": "^4.1.0",
34
34
  "redux-logger": "^3.0.6",
35
35
  "redux-persist": "^5.10.0",
package/src/App/Data.js CHANGED
@@ -5,6 +5,8 @@ import { t } from './i18n';
5
5
 
6
6
  export const whiteSpacePattern = /^[\S]+$/;
7
7
 
8
+ export const identifierPattern = /^[A-za-z0-9_.-]+$/;
9
+
8
10
  validate.validators.nonEmptyArray = function(value, options) {
9
11
  if (options && options === true && isArray(value) && value.length !== 0) {
10
12
  return null;
@@ -30,13 +32,12 @@ validate.validators.numericOrEmpty = function (value, options) {
30
32
  }
31
33
 
32
34
  validate.validators.identifier = function(value, options) {
33
- const pattern = /^[A-za-z0-9_.-]+$/;
34
35
  if (
35
36
  options &&
36
37
  options === true &&
37
38
  typeof value === 'string' &&
38
- value.match(pattern) &&
39
- value.match(pattern)[0] === value
39
+ value.match(identifierPattern) &&
40
+ value.match(identifierPattern)[0] === value
40
41
  ) {
41
42
  return null;
42
43
  }
@@ -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;