@configuratorware/configurator-admingui 1.31.1 → 1.31.2

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 (38) hide show
  1. package/App/Reducers/Api/Actions.js +12 -1
  2. package/App/Reducers/Api/Reducer.js +6 -0
  3. package/Components/DefaultForm.js +3 -2
  4. package/Screens/Client/Containers/Edit.js +15 -4
  5. package/Screens/Client/Translations.js +3 -1
  6. package/Screens/ColorPalettes/Components/DefaultColorSwitch.js +21 -11
  7. package/Screens/Creator/Components/VisualizationAndMediaData/Components/ImageEditTools.js +78 -28
  8. package/Screens/Creator/Components/VisualizationAndMediaData/Translations.js +4 -2
  9. package/Screens/CurrentClient/Containers/Edit.js +15 -4
  10. package/Screens/CurrentClient/Reducers/Reducer.js +1 -4
  11. package/Screens/DefaultClient/Containers/Edit.js +15 -4
  12. package/Screens/DefaultClient/Reducers/Reducer.js +1 -4
  13. package/Screens/Group/Reducers/Actions.js +4 -2
  14. package/Screens/Item/Components/Variants/VariantsEditor.js +4 -2
  15. package/Screens/Item/Components/Variants/VariantsEditorPopup.js +34 -0
  16. package/Screens/Login/Reducers/Actions.js +4 -5
  17. package/package.json +2 -2
  18. package/src/App/Reducers/Api/Actions.js +6 -0
  19. package/src/App/Reducers/Api/Reducer.js +7 -0
  20. package/src/Components/DefaultForm.js +2 -1
  21. package/src/Components/TranslationFinder.js +1 -0
  22. package/src/Screens/Client/Containers/Edit.js +15 -2
  23. package/src/Screens/Client/Translations.js +3 -1
  24. package/src/Screens/ColorPalettes/Components/DefaultColorSwitch.js +18 -11
  25. package/src/Screens/Creator/Components/VisualizationAndMediaData/Components/ImageEditTools.js +65 -40
  26. package/src/Screens/Creator/Components/VisualizationAndMediaData/Translations.js +2 -0
  27. package/src/Screens/Creator/Components/VisualizationAndMediaData/VisualizationAndMediaData.js +1 -0
  28. package/src/Screens/Creator/Components/VisualizationAndMediaData/VisualizationAndMediaData.test.js +1 -0
  29. package/src/Screens/CurrentClient/Containers/Edit.js +15 -2
  30. package/src/Screens/CurrentClient/Reducers/Reducer.js +1 -1
  31. package/src/Screens/DefaultClient/Containers/Edit.js +15 -2
  32. package/src/Screens/DefaultClient/Reducers/Reducer.js +1 -1
  33. package/src/Screens/Group/Reducers/Actions.js +1 -1
  34. package/src/Screens/Item/Components/Variants/VariantsEditor.js +3 -2
  35. package/src/Screens/Item/Components/Variants/VariantsEditorPopup.js +11 -1
  36. package/src/Screens/Login/Reducers/Actions.js +1 -5
  37. package/Screens/Client/Components/TextFields.js +0 -163
  38. package/src/Screens/Client/Components/TextFields.js +0 -77
@@ -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),
@@ -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
@@ -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
  },
@@ -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
  };