@configuratorware/configurator-admingui 1.31.1 → 1.31.4

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 (46) hide show
  1. package/App/Reducers/Api/Actions.js +12 -1
  2. package/App/Reducers/Api/Reducer.js +6 -0
  3. package/App/Reducers/Entity/Actions.js +6 -2
  4. package/Components/DefaultForm.js +3 -2
  5. package/Components/DefaultList.js +1 -1
  6. package/Screens/Client/Containers/Edit.js +15 -4
  7. package/Screens/Client/Translations.js +3 -1
  8. package/Screens/ColorPalettes/Components/DefaultColorSwitch.js +21 -11
  9. package/Screens/Creator/Components/VisualizationAndMediaData/Components/ImageEditTools.js +78 -28
  10. package/Screens/Creator/Components/VisualizationAndMediaData/Translations.js +4 -2
  11. package/Screens/CurrentClient/Containers/Edit.js +15 -4
  12. package/Screens/CurrentClient/Reducers/Reducer.js +1 -4
  13. package/Screens/DefaultClient/Containers/Edit.js +15 -4
  14. package/Screens/DefaultClient/Reducers/Reducer.js +1 -4
  15. package/Screens/Designer/Containers/List.js +1 -1
  16. package/Screens/Designer/SubScreens/Visualization/Containers/List.js +1 -1
  17. package/Screens/Group/Reducers/Actions.js +4 -2
  18. package/Screens/Item/Components/Variants/VariantsEditor.js +4 -2
  19. package/Screens/Item/Components/Variants/VariantsEditorPopup.js +34 -0
  20. package/Screens/Login/Reducers/Actions.js +4 -5
  21. package/package.json +2 -2
  22. package/src/App/Reducers/Api/Actions.js +6 -0
  23. package/src/App/Reducers/Api/Reducer.js +7 -0
  24. package/src/App/Reducers/Entity/Actions.js +5 -2
  25. package/src/Components/DefaultForm.js +2 -1
  26. package/src/Components/DefaultList.js +1 -1
  27. package/src/Components/TranslationFinder.js +1 -0
  28. package/src/Screens/Client/Containers/Edit.js +15 -2
  29. package/src/Screens/Client/Translations.js +3 -1
  30. package/src/Screens/ColorPalettes/Components/DefaultColorSwitch.js +18 -11
  31. package/src/Screens/Creator/Components/VisualizationAndMediaData/Components/ImageEditTools.js +65 -40
  32. package/src/Screens/Creator/Components/VisualizationAndMediaData/Translations.js +2 -0
  33. package/src/Screens/Creator/Components/VisualizationAndMediaData/VisualizationAndMediaData.js +1 -0
  34. package/src/Screens/Creator/Components/VisualizationAndMediaData/VisualizationAndMediaData.test.js +1 -0
  35. package/src/Screens/CurrentClient/Containers/Edit.js +15 -2
  36. package/src/Screens/CurrentClient/Reducers/Reducer.js +1 -1
  37. package/src/Screens/DefaultClient/Containers/Edit.js +15 -2
  38. package/src/Screens/DefaultClient/Reducers/Reducer.js +1 -1
  39. package/src/Screens/Designer/Containers/List.js +1 -1
  40. package/src/Screens/Designer/SubScreens/Visualization/Containers/List.js +1 -1
  41. package/src/Screens/Group/Reducers/Actions.js +1 -1
  42. package/src/Screens/Item/Components/Variants/VariantsEditor.js +3 -2
  43. package/src/Screens/Item/Components/Variants/VariantsEditorPopup.js +11 -1
  44. package/src/Screens/Login/Reducers/Actions.js +1 -5
  45. package/Screens/Client/Components/TextFields.js +0 -163
  46. package/src/Screens/Client/Components/TextFields.js +0 -77
@@ -88,11 +88,10 @@ var errorHandler = function errorHandler(dispatch) {
88
88
 
89
89
  var postLoginData = function postLoginData(username, password) {
90
90
  return function (dispatch) {
91
- var data = new _Api.ObjectFormData({
92
- _username: username,
93
- _password: password
94
- });
95
- return (0, _Helpers.dispatchPostData)(dispatch)(LOGIN_DATA_KEY, 'login_check', data).then(function (apiAction) {
91
+ return (0, _Helpers.dispatchPostData)(dispatch)(LOGIN_DATA_KEY, 'login_check', {
92
+ username: username,
93
+ password: password
94
+ }).then(function (apiAction) {
96
95
  return dispatch((0, _Helpers.apiActionHandler)(apiAction, receiveHandler, errorHandler));
97
96
  });
98
97
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@configuratorware/configurator-admingui",
3
- "version": "1.31.1",
3
+ "version": "1.31.4",
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.31.1",
32
+ "redhotmagma-visualization": "1.31.4",
33
33
  "redux": "^4.1.0",
34
34
  "redux-logger": "^3.0.6",
35
35
  "redux-persist": "^5.10.0",
@@ -10,12 +10,18 @@ export const POST_DATA = 'POST_DATA';
10
10
  export const DELETE_DATA = 'DELETE_DATA';
11
11
  export const CANCEL_REQUEST = 'CANCEL_REQUEST';
12
12
  export const EXPIRED_RESPONSE = 'EXPIRED_RESPONSE';
13
+ export const RESET_DID_INVALIDATE = 'RESET_DID_INVALIDATE';
13
14
 
14
15
  export const invalidateSource = key => ({
15
16
  type: INVALIDATE_SOURCE,
16
17
  key,
17
18
  });
18
19
 
20
+ export const resetDidInvalidate = key => ({
21
+ type: RESET_DID_INVALIDATE,
22
+ key,
23
+ });
24
+
19
25
  export const requestData = (source, key) => ({
20
26
  type: REQUEST_DATA,
21
27
  source,
@@ -7,6 +7,7 @@ import {
7
7
  DELETE_DATA,
8
8
  CANCEL_REQUEST,
9
9
  EXPIRED_RESPONSE,
10
+ RESET_DID_INVALIDATE,
10
11
  } from './Actions';
11
12
 
12
13
  const data = (
@@ -27,6 +28,11 @@ const data = (
27
28
  didInvalidate: true,
28
29
  data: null,
29
30
  };
31
+ case RESET_DID_INVALIDATE:
32
+ return {
33
+ ...state,
34
+ didInvalidate: false,
35
+ };
30
36
  case REQUEST_DATA:
31
37
  return {
32
38
  ...state,
@@ -90,6 +96,7 @@ export const dataBySource = (state = {}, action) => {
90
96
  case DELETE_DATA:
91
97
  case CANCEL_REQUEST:
92
98
  case EXPIRED_RESPONSE:
99
+ case RESET_DID_INVALIDATE:
93
100
  return {
94
101
  ...state,
95
102
  [action.key]: data(state[action.key], action),
@@ -15,10 +15,13 @@ export const reportUnsavedEntity = (reducerName, entityState, saveAndHide, hide,
15
15
  cancel,
16
16
  });
17
17
 
18
- export const releaseUnsavedEntity = () => (dispatch, getState) => {
18
+ export const releaseUnsavedEntity = targetReducerName => (dispatch, getState) => {
19
19
  const state = getState();
20
20
  const { entity } = state;
21
21
  const { reducerName } = entity.unsavedEntity;
22
+ if (targetReducerName && targetReducerName !== reducerName) {
23
+ return;
24
+ }
22
25
  if (reducerName) {
23
26
  if (state[reducerName].showDetails) {
24
27
  dispatch({
@@ -422,7 +425,7 @@ export const generateDefaultActions = (listKey, dataKey, reducerName, url, ctx =
422
425
  // onDelete callback
423
426
  dispatchDeleteData(dispatch)(listKey, url, ids).then(() => {
424
427
  dispatch(hideDetails(false));
425
- dispatch(releaseUnsavedEntity());
428
+ dispatch(releaseUnsavedEntity(reducerName));
426
429
  dispatch(invalidateSource(listKey));
427
430
  dispatch(cancelDeleteEntitiesDialog());
428
431
  ctx.afterDelete && dispatch(ctx.afterDelete(ids));
@@ -13,10 +13,11 @@ export default class DefaultForm extends Component {
13
13
  onAction,
14
14
  config,
15
15
  closeAfterSave,
16
+ customPostData
16
17
  } = this.props;
17
18
  const { actionButtons } = config;
18
19
 
19
- const onSubmit = !closeAfterSave ? postData : () => postData(true);
20
+ const onSubmit = customPostData ? customPostData : !closeAfterSave ? postData : () => postData(true);
20
21
 
21
22
  return entityState.isReady ? (
22
23
  <Form
@@ -33,7 +33,7 @@ export default class DefaultList extends Component {
33
33
  }
34
34
 
35
35
  componentDidUpdate(prevProps) {
36
- if (prevProps.selected !== this.props.selected) {
36
+ if (this.props.selected && !_.isEqual(prevProps.selected, this.props.selected)) {
37
37
  this.setState({
38
38
  selectedRows: this.props.selected,
39
39
  });
@@ -20,6 +20,7 @@ let TranslationFinder = props => {
20
20
  const { language, data, property, fallback } = props;
21
21
  return <span>{get(find(data, { language }), property, fallback)}</span>;
22
22
  };
23
+
23
24
  const mapStateToProps = ({ i18n }) => ({
24
25
  language: i18n.locale.replace('-', '_'),
25
26
  });
@@ -1,7 +1,6 @@
1
1
  import Actions, { REDUCER_NAME } from '../Reducers/Actions';
2
2
  import SimpleNestedData from '../../../Components/FormFragments/SimpleNestedData';
3
3
  import generateConnectedEdit from '../../../Components/DefaultConnectedForm';
4
- import TextFields from '../Components/TextFields';
5
4
  import ColorTextField from '../Components/ColorTextField';
6
5
  import { t } from '../../../App/i18n';
7
6
  import React from 'react';
@@ -139,8 +138,22 @@ const formFields = [
139
138
  dynamicDefaultLabel: false
140
139
  },
141
140
  {
142
- type: TextFields,
143
141
  name: 'texts',
142
+ label: 'termsAndConditionsLink',
143
+ type: 'intl',
144
+ intl: {
145
+ name: 'termsAndConditionsLink',
146
+ type: 'text',
147
+ },
148
+ },
149
+ {
150
+ name: 'texts',
151
+ label: 'dataPrivacyLink',
152
+ type: 'intl',
153
+ intl: {
154
+ name: 'dataPrivacyLink',
155
+ type: 'text',
156
+ },
144
157
  },
145
158
  {
146
159
  name: 'channels',
@@ -5,6 +5,7 @@ require('../../App/i18n').use(
5
5
  Users: 'Users',
6
6
  'Theme - Highlight color': 'Theme - Highlight color',
7
7
  termsAndConditionsLink: 'Terms and conditions link',
8
+ dataPrivacyLink: 'Data privacy Link',
8
9
  themeColorError: 'Please enter a valid hex color (e.g. #0000ff)"',
9
10
  client: {
10
11
  addButtonLabel: 'Add Client',
@@ -21,7 +22,8 @@ require('../../App/i18n').use(
21
22
  Users: 'Benutzer',
22
23
  'Theme - Highlight color': 'Theme - Highlightfarbe',
23
24
  'Theme - Font': 'Theme - Font',
24
- termsAndConditionsLink: 'Terms and conditions link',
25
+ termsAndConditionsLink: 'Link zu den Nutzungsbedingungen',
26
+ dataPrivacyLink: 'Link zu den Datenschutzbestimmungen',
25
27
  'Clientname / Shopname': 'Bezeichner/ Shopname',
26
28
  'E-mail (contact)': 'E-Mail (Kontakt)',
27
29
  'Street / no': 'Straße / Hausnummer',
@@ -20,20 +20,27 @@ export class DefaultColorSwitch extends Component {
20
20
  };
21
21
 
22
22
  onToggle = e => {
23
- this.props.setDefaultColor(this.props.isDefaultColor ? null : this.props.color);
23
+ const colorId = get(this.props.color, 'id', null);
24
+ if (colorId) {
25
+ this.props.setDefaultColor(this.props.isDefaultColor ? null : this.props.color);
26
+ }
24
27
  };
25
28
 
26
29
  render() {
27
30
  const { classes } = this.props;
28
- return (
29
- <Switch
30
- classes={{ switchBase: classes.switchBase }}
31
- checked={this.props.isDefaultColor}
32
- onClick={this.onToggle}
33
- name="isDefaultColor"
34
- color="primary"
35
- />
36
- );
31
+ const colorId = get(this.props.color, 'id', null);
32
+ if (colorId) {
33
+ return (
34
+ <Switch
35
+ classes={{ switchBase: classes.switchBase }}
36
+ checked={this.props.isDefaultColor}
37
+ onClick={this.onToggle}
38
+ name="isDefaultColor"
39
+ color="primary"
40
+ />
41
+ );
42
+ }
43
+ return null;
37
44
  }
38
45
  }
39
46
 
@@ -42,7 +49,7 @@ const mapStateToProps = (state, ownProps) => {
42
49
  const defaultColorId = get(state, `${COLORPALETTES_REDUCER_NAME}.data.defaultColor.value.id`, null);
43
50
 
44
51
  return {
45
- isDefaultColor: colorId === defaultColorId,
52
+ isDefaultColor: colorId === defaultColorId && colorId !== null && defaultColorId !== null,
46
53
  };
47
54
  };
48
55
 
@@ -7,8 +7,10 @@ import { t } from '../../../../../App/i18n';
7
7
  import ConfirmDelete from './ConfirmDelete';
8
8
  import Api from '../../../../../App/Api';
9
9
  import { networkError } from '../../../../../App/Reducers/Api/Actions';
10
+ import { showErrorMessage, cancelErrorMessage } from '../../../../../App/Reducers/Frame/Actions';
10
11
  import { useDispatch } from 'react-redux';
11
12
  import { Tooltip } from '@material-ui/core';
13
+ import FileDrop from 'react-file-drop';
12
14
 
13
15
  const ImageEditTools = withStyles({
14
16
  root: {
@@ -17,18 +19,32 @@ const ImageEditTools = withStyles({
17
19
  fileInput: {
18
20
  display: 'none',
19
21
  },
22
+ fileDrop: {
23
+ position: 'absolute',
24
+ top: 0,
25
+ left: '50%',
26
+ width: 150,
27
+ marginLeft: -75,
28
+ height: 100,
29
+ },
20
30
  })(({ classes, className, deleteEnabled, uploadUrl, removeUrl, onUpdate }) => {
21
31
  const fileInputRef = useRef(null);
22
32
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
23
33
  const dispatch = useDispatch();
24
34
 
25
35
  const uploadImage = async image => {
36
+ const allowedFormats = ['image/jpeg', 'image/jpg', 'image/png'];
37
+
26
38
  if (!uploadUrl) {
27
39
  throw new Error('uploadUrl is not provided!');
28
40
  }
41
+ if (allowedFormats.indexOf(image.type) === -1) {
42
+ return dispatch(showErrorMessage(t('visualizationAndMediaData.unsupportedFileTypeError')));
43
+ }
29
44
  try {
30
45
  const data = new FormData();
31
46
  data.append('file', image);
47
+ dispatch(cancelErrorMessage());
32
48
  return await Api.request({ method: 'post', url: uploadUrl, data });
33
49
  } catch (e) {
34
50
  dispatch(networkError('image', 'image', e));
@@ -46,47 +62,56 @@ const ImageEditTools = withStyles({
46
62
  };
47
63
 
48
64
  return (
49
- <div className={`${classes.root} ${className}`}>
50
- <input
51
- ref={fileInputRef}
52
- type="file"
53
- accept="image/jpeg, image/png"
54
- className={classes.fileInput}
55
- onChange={async evt => {
56
- if (evt.target.files.length) {
57
- await uploadImage(evt.target.files[0]);
58
- onUpdate && onUpdate();
59
- }
60
- }}
61
- />
62
- <label>
63
- <Tooltip title={t('visualizationAndMediaData.imageAddTooltip')}>
64
- <IconButton onClick={() => fileInputRef.current.click()}>
65
- <ImageSearchIcon />
66
- </IconButton>
67
- </Tooltip>
68
- </label>
69
- {deleteEnabled && (
70
- <Tooltip title={t('visualizationAndMediaData.imageDeleteTooltip')}>
71
- <IconButton onClick={() => setShowDeleteConfirm(true)}>
72
- <DeleteIcon />
73
- </IconButton>
74
- </Tooltip>
75
- )}
65
+ <FileDrop
66
+ className={classes.fileDrop}
67
+ onDrop={async files => {
68
+ await uploadImage(files[0]);
69
+ onUpdate && onUpdate();
70
+ }}
71
+ multiple={false}
72
+ >
73
+ <div className={`${classes.root} ${className}`}>
74
+ <input
75
+ ref={fileInputRef}
76
+ type="file"
77
+ accept="image/jpeg, image/png"
78
+ className={classes.fileInput}
79
+ onChange={async evt => {
80
+ if (evt.target.files.length) {
81
+ await uploadImage(evt.target.files[0]);
82
+ onUpdate && onUpdate();
83
+ }
84
+ }}
85
+ />
86
+ <label>
87
+ <Tooltip title={t('visualizationAndMediaData.imageAddTooltip')}>
88
+ <IconButton onClick={() => fileInputRef.current.click()}>
89
+ <ImageSearchIcon />
90
+ </IconButton>
91
+ </Tooltip>
92
+ </label>
93
+ {deleteEnabled && (
94
+ <Tooltip title={t('visualizationAndMediaData.imageDeleteTooltip')}>
95
+ <IconButton onClick={() => setShowDeleteConfirm(true)}>
96
+ <DeleteIcon />
97
+ </IconButton>
98
+ </Tooltip>
99
+ )}
76
100
 
77
- <ConfirmDelete
78
- open={showDeleteConfirm}
79
- onCancel={() => setShowDeleteConfirm(false)}
80
- onConfirm={async () => {
81
- await removeImage();
82
- setShowDeleteConfirm(false);
83
- onUpdate && onUpdate();
84
- }}
85
- title={t('visualizationAndMediaData.imageDeleteConfirm.title')}
86
- message={t('visualizationAndMediaData.imageDeleteConfirm.message')}
87
- />
88
- </div>
101
+ <ConfirmDelete
102
+ open={showDeleteConfirm}
103
+ onCancel={() => setShowDeleteConfirm(false)}
104
+ onConfirm={async () => {
105
+ await removeImage();
106
+ setShowDeleteConfirm(false);
107
+ onUpdate && onUpdate();
108
+ }}
109
+ title={t('visualizationAndMediaData.imageDeleteConfirm.title')}
110
+ message={t('visualizationAndMediaData.imageDeleteConfirm.message')}
111
+ />
112
+ </div>
113
+ </FileDrop>
89
114
  );
90
115
  });
91
116
 
92
- export default ImageEditTools;
117
+ export default ImageEditTools;
@@ -44,6 +44,7 @@ require('../../../../App/i18n').use(
44
44
  title: 'Delete image',
45
45
  message: 'Do you really want to delete this image?',
46
46
  },
47
+ unsupportedFileTypeError: 'Unsupported file format',
47
48
  },
48
49
  },
49
50
  de: {
@@ -92,6 +93,7 @@ require('../../../../App/i18n').use(
92
93
  title: 'Bild löschen',
93
94
  message: 'Möchtest du dieses Bild wirklich löschen?',
94
95
  },
96
+ unsupportedFileTypeError: 'Nicht unterstütztes Dateiformat',
95
97
  },
96
98
  },
97
99
  },
@@ -70,6 +70,7 @@ const ImageTile = withStyles(theme => ({
70
70
  return (
71
71
  <div className={classes.imageTile}>
72
72
  {expectedSrc ? <PathStructure path={expectedSrc} /> : <Image src={src} />}
73
+
73
74
  {uploadUrl && removeUrl && (
74
75
  <ImageEditTools
75
76
  className={classes.imageEditTools}
@@ -3,6 +3,7 @@ import { act } from 'react-dom/test-utils';
3
3
  import { VisualizationAndMediaData } from './VisualizationAndMediaData';
4
4
  import { mount, render } from 'enzyme';
5
5
  import FormControl from '@material-ui/core/FormControl';
6
+ jest.mock('react-file-drop', () => props => <div>{props.children}</div>);
6
7
 
7
8
  const mockDispatch = jest.fn();
8
9
  jest.mock('react-redux', () => ({
@@ -1,7 +1,6 @@
1
1
  import Actions, { REDUCER_NAME } from '../Reducers/Actions';
2
2
  import SimpleNestedData from '../../../Components/FormFragments/SimpleNestedData';
3
3
  import generateConnectedEdit from '../../../Components/DefaultConnectedForm';
4
- import TextFields from '../../Client/Components/TextFields';
5
4
  import { withLoadAction } from '../../../Components/withLoadAction';
6
5
  import { t } from '../../../App/i18n';
7
6
  import { CallToActionField } from '../../../Components/CallToActionField';
@@ -108,8 +107,22 @@ const formFields = [
108
107
  dynamicDefaultLabel: false
109
108
  },
110
109
  {
111
- type: TextFields,
112
110
  name: 'texts',
111
+ label: 'termsAndConditionsLink',
112
+ type: 'intl',
113
+ intl: {
114
+ name: 'termsAndConditionsLink',
115
+ type: 'text',
116
+ },
117
+ },
118
+ {
119
+ name: 'texts',
120
+ label: 'Title',
121
+ type: 'dataPrivacyLink',
122
+ intl: {
123
+ name: 'dataPrivacyLink',
124
+ type: 'text',
125
+ },
113
126
  },
114
127
  {
115
128
  name: 'customCss',
@@ -7,7 +7,7 @@ const initialState = {
7
7
  ...getDefaultEntityState(
8
8
  {
9
9
  id: null,
10
- texts: { value: [], constraints: { presence: true } },
10
+ texts: { value: [] },
11
11
  theme: {
12
12
  value: {},
13
13
  schema: {
@@ -3,7 +3,6 @@ import generateConnectedEdit from '../../../Components/DefaultConnectedForm';
3
3
  import { withLoadAction } from '../../../Components/withLoadAction';
4
4
  import SimpleNestedData from '../../../Components/FormFragments/SimpleNestedData';
5
5
  import { t, T } from '../../../App/i18n';
6
- import TextFields from '../../Client/Components/TextFields';
7
6
  import PdfMarkDownField from '../../Client/Components/PdfMarkdownField';
8
7
 
9
8
  const formFields = [
@@ -124,8 +123,22 @@ const formFields = [
124
123
  ],
125
124
  },
126
125
  {
127
- type: TextFields,
128
126
  name: 'texts',
127
+ label: 'termsAndConditionsLink',
128
+ type: 'intl',
129
+ intl: {
130
+ name: 'termsAndConditionsLink',
131
+ type: 'text',
132
+ },
133
+ },
134
+ {
135
+ name: 'texts',
136
+ label: 'dataPrivacyLink',
137
+ type: 'intl',
138
+ intl: {
139
+ name: 'dataPrivacyLink',
140
+ type: 'text',
141
+ },
129
142
  },
130
143
  {
131
144
  name: 'customCss',
@@ -12,7 +12,7 @@ const initialState = {
12
12
  theme: {
13
13
  value: {},
14
14
  schema: {
15
- highlightColor: { value: '', constraints: { presence: true } },
15
+ highlightColor: { value: '' },
16
16
  logo: { value: '' },
17
17
  font: { value: '' },
18
18
  },
@@ -37,6 +37,6 @@ export default generateConnectedList(
37
37
  false, // skip remove entity
38
38
  {
39
39
  reducerName: DESIGNER_REDUCER_NAME,
40
- entityIdKey: 'item.value.id',
40
+ entityIdKey: 'id.value',
41
41
  }
42
42
  );
@@ -16,7 +16,7 @@ import { DESIGNER_REDUCER_NAME } from '../../../Reducers/Actions';
16
16
  class VisualizationSettings extends React.Component {
17
17
  static propTypes = {
18
18
  loadDetails: PropTypes.func.isRequired,
19
- steState: PropTypes.func.isRequired,
19
+ setState: PropTypes.func.isRequired,
20
20
  onDeleteItems: PropTypes.func.isRequired,
21
21
  };
22
22
 
@@ -7,6 +7,6 @@ import { generateDefaultActions } from '../../../App/Reducers/Entity/Actions';
7
7
 
8
8
  const actions = generateDefaultActions(LIST_KEY, DATA_KEY, REDUCER_NAME, GROUPS_URL);
9
9
 
10
- export const { hideDetails, loadEntity } = actions;
10
+ export const { hideDetails, loadEntity, postData } = actions;
11
11
 
12
12
  export default actions;
@@ -8,6 +8,7 @@ import TableHead from '@material-ui/core/TableHead';
8
8
  import TableRow from '@material-ui/core/TableRow';
9
9
  import Checkbox from '@material-ui/core/Checkbox/Checkbox';
10
10
  import TablePagination from '@material-ui/core/TablePagination';
11
+ import uniq from 'lodash/uniq';
11
12
  import { addReduxListener, removeReduxListener } from '../../../../App/ReduxListener';
12
13
  import { t } from '../../../../App/i18n';
13
14
  import Actions, { ITEM_REDUCER_NAME } from '../../Reducers/Actions';
@@ -64,7 +65,7 @@ const getGroupsFromChildren = children => {
64
65
  }
65
66
  }
66
67
 
67
- return { groups, groupIdsInOrder };
68
+ return { groups, groupIdsInOrder: uniq(groupIdsInOrder) };
68
69
  };
69
70
 
70
71
  const prepareDataForRendering = children => {
@@ -222,7 +223,7 @@ class VariantsEditor extends Component {
222
223
 
223
224
  this.setState({
224
225
  groups,
225
- groupIdsInOrder,
226
+ groupIdsInOrder: uniq(groupIdsInOrder),
226
227
  });
227
228
  };
228
229
 
@@ -8,6 +8,7 @@ import * as GroupActions from '../../../Group/Reducers/Actions';
8
8
  import * as GroupEntryActions from '../../../GroupEntry/Reducers/Actions';
9
9
 
10
10
  import { T } from '../../../../App/i18n';
11
+ import { resetDidInvalidate } from '../../../../App/Reducers/Api/Actions';
11
12
 
12
13
  export class VariantsEditorPopup extends React.Component {
13
14
  render() {
@@ -21,7 +22,11 @@ export class VariantsEditorPopup extends React.Component {
21
22
  open={showGroupEditor}
22
23
  scroll={'paper'}
23
24
  >
24
- <GroupEdit onCancel={this.props.cancelGroup} closeAfterSave />
25
+ <GroupEdit
26
+ customPostData={this.props.customPostData}
27
+ onCancel={this.props.cancelGroup}
28
+ closeAfterSave
29
+ />
25
30
  </Dialog>
26
31
 
27
32
  <Dialog
@@ -51,6 +56,11 @@ const renderDispatchToProps = dispatch => ({
51
56
  cancelGroup: () => {
52
57
  dispatch(GroupActions.hideDetails(false));
53
58
  },
59
+ customPostData: async () => {
60
+ await dispatch(GroupActions.postData(true));
61
+ // need to reset didInvalidate flag as it is set on save of group which is blocking to reloadList
62
+ dispatch(resetDidInvalidate(GroupActions.GROUPS_URL));
63
+ },
54
64
  cancelGroupEntry: () => {
55
65
  dispatch(GroupEntryActions.hideDetails(false));
56
66
  },
@@ -49,11 +49,7 @@ const errorHandler = dispatch => error => {
49
49
  };
50
50
 
51
51
  export const postLoginData = (username, password) => dispatch => {
52
- const data = new ObjectFormData({
53
- _username: username,
54
- _password: password,
55
- });
56
- return dispatchPostData(dispatch)(LOGIN_DATA_KEY, 'login_check', data).then(apiAction => {
52
+ return dispatchPostData(dispatch)(LOGIN_DATA_KEY, 'login_check', {username, password}).then(apiAction => {
57
53
  return dispatch(apiActionHandler(apiAction, receiveHandler, errorHandler));
58
54
  });
59
55
  };