@griddo/ax 1.55.14 → 1.56.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 (33) hide show
  1. package/package.json +2 -2
  2. package/src/GlobalStore.tsx +3 -0
  3. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +6 -0
  4. package/src/components/Fields/ArrayFieldGroup/ArrayFieldInline/index.tsx +16 -12
  5. package/src/components/Fields/ArrayFieldGroup/ArrayFieldItem/index.tsx +17 -15
  6. package/src/components/Fields/ConditionalField/index.tsx +1 -3
  7. package/src/components/Fields/FileField/index.tsx +1 -1
  8. package/src/components/Fields/Wysiwyg/config.tsx +0 -1
  9. package/src/components/Fields/Wysiwyg/index.tsx +16 -5
  10. package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +110 -99
  11. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +75 -55
  12. package/src/components/Gallery/GalleryPanel/index.tsx +14 -8
  13. package/src/components/Gallery/index.tsx +113 -151
  14. package/src/components/Gallery/style.tsx +40 -10
  15. package/src/components/MainWrapper/AppBar/index.tsx +1 -0
  16. package/src/components/Toast/index.tsx +15 -9
  17. package/src/components/Toast/style.tsx +2 -2
  18. package/src/containers/Gallery/actions.tsx +171 -0
  19. package/src/containers/Gallery/constants.tsx +18 -0
  20. package/src/containers/Gallery/index.tsx +7 -0
  21. package/src/containers/Gallery/interfaces.tsx +41 -0
  22. package/src/containers/Gallery/reducer.tsx +78 -0
  23. package/src/containers/PageEditor/actions.tsx +15 -5
  24. package/src/containers/StructuredData/actions.tsx +4 -21
  25. package/src/forms/fields.tsx +19 -6
  26. package/src/guards/error/index.tsx +3 -1
  27. package/src/modules/Content/HeaderMenus/Live/index.tsx +6 -5
  28. package/src/modules/Sites/SitesList/index.tsx +1 -1
  29. package/src/modules/StructuredData/Form/ConnectedField/index.tsx +2 -4
  30. package/src/modules/StructuredData/Form/index.tsx +10 -6
  31. package/src/modules/StructuredData/StructuredDataList/HeaderMenus/Live/index.tsx +1 -1
  32. package/src/types/index.tsx +20 -0
  33. package/src/components/Gallery/store.tsx +0 -186
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "1.55.14",
4
+ "version": "1.56.2",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -233,5 +233,5 @@
233
233
  "publishConfig": {
234
234
  "access": "public"
235
235
  },
236
- "gitHead": "74a33fa1c0a01633e2c999a7166a175be820b09f"
236
+ "gitHead": "76dddc292b1037223a780b2093399c3c6e12ce02"
237
237
  }
@@ -16,6 +16,7 @@ import { languagesReducer, languagesInitialState } from "./containers/Settings/L
16
16
  import { dataPacksReducer, dataPacksInitialState } from "./containers/Settings/DataPacks/reducer";
17
17
  import { socialReducer, socialInitialState } from "./containers/Settings/Social/reducer";
18
18
  import { usersReducer, usersInitialState } from "./containers/Users/reducer";
19
+ import { galleryReducer, galleryInitialState } from "./containers/Gallery/reducer";
19
20
 
20
21
  import { IRootState } from "@ax/types";
21
22
 
@@ -44,6 +45,7 @@ export class GlobalStore {
44
45
  dataPacks: dataPacksReducer as Reducer<any, Action<any>>,
45
46
  social: socialReducer as Reducer<any, Action<any>>,
46
47
  users: usersReducer as Reducer<any, Action<any>>,
48
+ gallery: galleryReducer as Reducer<any, Action<any>>,
47
49
  });
48
50
 
49
51
  const rootReducer = (state: IRootState | undefined, action: any) => {
@@ -60,6 +62,7 @@ export class GlobalStore {
60
62
  dataPacks: dataPacksInitialState,
61
63
  social: socialInitialState,
62
64
  users: usersInitialState,
65
+ gallery: galleryInitialState,
63
66
  };
64
67
  }
65
68
 
@@ -111,6 +111,12 @@ const PageConnectedField = (props: any) => {
111
111
  updateEditorContent(selectedEditorID, "metaTitle", value);
112
112
  }
113
113
  }
114
+
115
+ const isComponentImage = ["LinkableImage", "Image"].includes(selectedContent.component) && key === "file";
116
+ if (isComponentImage) {
117
+ updateEditorContent(selectedEditorID, "title", value.title);
118
+ updateEditorContent(selectedEditorID, "alt", value.alt);
119
+ }
114
120
  };
115
121
 
116
122
  if (isOverride) {
@@ -7,8 +7,21 @@ import * as S from "./style";
7
7
  const ArrayFieldInline = (props: IProps): JSX.Element => {
8
8
  const { fields, item, index, onChange, handleDelete } = props;
9
9
 
10
- const deleteItem = () => {
11
- handleDelete(index);
10
+ const deleteItem = () => handleDelete(index);
11
+
12
+ const getFields = (fields: any[]): any[] => {
13
+ return fields.map((field: any) => {
14
+ const key = field.props.objKey;
15
+ const handleChange = (newValue: any) => onChange({ [key]: newValue });
16
+
17
+ const innerFields = field.props.innerFields ? getFields(field.props.innerFields) : undefined;
18
+
19
+ return {
20
+ ...field,
21
+ props: { ...field.props, value: item[key], objKey: key, onChange: handleChange, innerFields },
22
+ key,
23
+ };
24
+ });
12
25
  };
13
26
 
14
27
  return (
@@ -17,16 +30,7 @@ const ArrayFieldInline = (props: IProps): JSX.Element => {
17
30
  <S.IconWrapper>
18
31
  <IconAction icon="delete" onClick={deleteItem} size="s" />
19
32
  </S.IconWrapper>
20
- {fields.map((field: any) => {
21
- const key = field.props.objKey;
22
- const handleChange = (newValue: any) => onChange({ [key]: newValue });
23
-
24
- return {
25
- ...field,
26
- props: { ...field.props, value: item[key], objKey: key, onChange: handleChange },
27
- key,
28
- };
29
- })}
33
+ {getFields(fields)}
30
34
  </S.Content>
31
35
  </S.Wrapper>
32
36
  );
@@ -9,9 +9,7 @@ const ArrayFieldItem = (props: IProps): JSX.Element => {
9
9
 
10
10
  const handleClick = () => setIsOpen(!isOpen);
11
11
 
12
- const deleteItem = () => {
13
- handleDelete(index);
14
- };
12
+ const deleteItem = () => handleDelete(index);
15
13
 
16
14
  const menuOptions = [
17
15
  {
@@ -21,24 +19,28 @@ const ArrayFieldItem = (props: IProps): JSX.Element => {
21
19
  },
22
20
  ];
23
21
 
22
+ const getFields = (fields: any[]): any[] => {
23
+ return fields.map((field: any) => {
24
+ const key = field.props.objKey;
25
+ const handleChange = (newValue: any) => onChange({ [key]: newValue });
26
+
27
+ const innerFields = field.props.innerFields ? getFields(field.props.innerFields) : undefined;
28
+
29
+ return {
30
+ ...field,
31
+ props: { ...field.props, value: item[key], objKey: key, onChange: handleChange, innerFields },
32
+ key,
33
+ };
34
+ });
35
+ };
36
+
24
37
  return (
25
38
  <S.Wrapper isOpen={isOpen}>
26
39
  <S.Title onClick={handleClick} isOpen={isOpen}>
27
40
  {`${name} ${index + 1}`}
28
41
  <S.StyledActionMenu icon="more" options={menuOptions} />
29
42
  </S.Title>
30
- <S.Content isOpen={isOpen}>
31
- {fields.map((field: any) => {
32
- const key = field.props.objKey;
33
- const handleChange = (newValue: any) => onChange({ [key]: newValue });
34
-
35
- return {
36
- ...field,
37
- props: { ...field.props, value: item[key], objKey: key, onChange: handleChange },
38
- key,
39
- };
40
- })}
41
- </S.Content>
43
+ <S.Content isOpen={isOpen}>{getFields(fields)}</S.Content>
42
44
  </S.Wrapper>
43
45
  );
44
46
  };
@@ -6,9 +6,7 @@ import * as S from "./style";
6
6
  const ConditionalField = (props: IConditionalFieldProps) => {
7
7
  const { value, options, innerFields, onChange } = props;
8
8
 
9
- const handleChange = (newValue: any) => {
10
- onChange(newValue);
11
- };
9
+ const handleChange = (newValue: any) => onChange(newValue);
12
10
 
13
11
  return (
14
12
  <S.Wrapper>
@@ -80,7 +80,7 @@ const FileField = (props: IProps) => {
80
80
  <TextField
81
81
  name="url"
82
82
  value={value.url}
83
- onChange={() => {}}
83
+ onChange={() => null}
84
84
  readonly={true}
85
85
  icon="openOutside"
86
86
  onClickIcon={handleOnClickUrl}
@@ -1,4 +1,3 @@
1
-
2
1
  const buttonsFull = {
3
2
  moreText: {
4
3
  buttons: ["bold", "italic", "paragraphFormat", "formatUL", "quote", "alignCenter"],
@@ -1,15 +1,16 @@
1
1
  import React from "react";
2
+ import { connect } from "react-redux";
2
3
  import FroalaEditor from "react-froala-wysiwyg";
3
4
  import { decodeEntities } from "@ax/helpers";
4
- import { uploadImage } from "@ax/components/Gallery/store";
5
- import { ISite } from "@ax/types";
5
+ import { IImage, ISite } from "@ax/types";
6
+ import { galleryActions } from "@ax/containers/Gallery";
6
7
 
7
8
  import { wysiwygConfig, buttons, buttonsFull } from "./config";
8
9
  import "./vendors";
9
10
  import * as S from "./style";
10
11
 
11
- const Wysiwyg = (props: IWysiwygProps): JSX.Element => {
12
- const { value, error, onChange, placeholder, full = true, disabled, handleValidation, site } = props;
12
+ const Wysiwyg = (props: IProps): JSX.Element => {
13
+ const { value, error, onChange, placeholder, full = true, disabled, handleValidation, site, uploadImage } = props;
13
14
 
14
15
  const imageSite = site ? site.id : "global"
15
16
 
@@ -71,4 +72,14 @@ interface IWysiwygProps {
71
72
  site: ISite;
72
73
  }
73
74
 
74
- export default Wysiwyg;
75
+ interface IDispatchProps {
76
+ uploadImage: (imageFile: File, site: number | string) => Promise<IImage>;
77
+ }
78
+
79
+ const mapDispatchToProps = {
80
+ uploadImage: galleryActions.uploadImage,
81
+ };
82
+
83
+ type IProps = IWysiwygProps & IDispatchProps;
84
+
85
+ export default connect(null, mapDispatchToProps)(Wysiwyg);
@@ -1,114 +1,106 @@
1
- import React, { memo, useReducer, useEffect, useState } from "react";
2
- import { IImage } from "@ax/types";
3
- import { Button, CheckField, FieldsBehavior } from "@ax/components";
1
+ import React, { memo, useEffect, useState } from "react";
2
+ import { connect } from "react-redux";
3
+ import { IImage, IRootState, IImageForm } from "@ax/types";
4
+ import { Button, CheckField, FieldsBehavior, Toast } from "@ax/components";
4
5
  import { formatBytes, getFormattedDateWithTimezone } from "@ax/helpers";
5
- import {
6
- reducer,
7
- initialState,
8
- setInitForm,
9
- setFormField,
10
- saveImageData,
11
- deleteImageData,
12
- uploadImage,
13
- } from "../../store";
14
6
 
7
+ import { galleryActions } from "@ax/containers/Gallery";
8
+ import { IIsSaving } from "@ax/containers/Gallery/reducer";
15
9
  import * as S from "./style";
16
10
 
17
11
  const GalleryDetailPanel = (props: IProps) => {
18
- const { setImage, imageSelected, isImageSelected, updateImage, deleteImage, isGlobalTab } = props;
12
+ const {
13
+ imageSelected,
14
+ isImageSelected,
15
+ deleteImage,
16
+ isGlobalTab,
17
+ updateImage,
18
+ refreshImages,
19
+ isSaving,
20
+ setImage,
21
+ uploadImage,
22
+ } = props;
23
+
24
+ const initialState: IImageForm = {
25
+ id: undefined,
26
+ alt: "",
27
+ title: "",
28
+ description: "",
29
+ tags: [],
30
+ };
19
31
 
20
- const [state, dispatch] = useReducer(reducer, initialState);
21
- const [isSaving, setIsSaving] = useState({ save: false, saveAdd: false, delete: false });
32
+ const [imageForm, setImageForm] = useState(initialState);
22
33
  const [addToGlobal, setAddToGlobal] = useState({ value: "addToGlobal", isChecked: false });
34
+ const [deletedToast, setDeletedToast] = useState(false);
35
+
36
+ const setInitForm = (imageSelected: IImage) => {
37
+ const form = {
38
+ id: imageSelected.id,
39
+ title: imageSelected.title ? imageSelected.title : "",
40
+ alt: imageSelected.alt ? imageSelected.alt : "",
41
+ description: imageSelected.description ? imageSelected.description : "",
42
+ tags: imageSelected.tags ? imageSelected.tags : [],
43
+ };
44
+ setImageForm(form);
45
+ };
46
+
47
+ const updateFormField = (field: string, value: string) => setImageForm((state) => ({ ...state, [field]: value }));
23
48
 
24
49
  useEffect(() => {
25
- if (imageSelected && imageSelected.id !== state.imageForm.id) {
26
- dispatch(
27
- setInitForm({
28
- id: imageSelected.id,
29
- title: imageSelected.title,
30
- alt: imageSelected.alt,
31
- description: imageSelected.description,
32
- tags: imageSelected.tags,
33
- })
34
- );
50
+ if (imageSelected && imageSelected.id !== imageForm.id) {
51
+ setInitForm(imageSelected);
35
52
  }
36
53
  // eslint-disable-next-line react-hooks/exhaustive-deps
37
54
  }, [imageSelected]);
38
55
 
39
- const _handleTitle = (newValue: string) => {
40
- dispatch(setFormField("title", newValue));
56
+ const handleTitle = (newValue: string) => {
57
+ updateFormField("title", newValue);
41
58
  };
42
59
 
43
- const _handleAlt = (newValue: string) => {
44
- dispatch(setFormField("alt", newValue));
60
+ const handleAlt = (newValue: string) => {
61
+ updateFormField("alt", newValue);
45
62
  };
46
63
 
47
- const _handleDescription = (newValue: string) => {
48
- dispatch(setFormField("description", newValue));
64
+ const handleDescription = (newValue: string) => {
65
+ updateFormField("description", newValue);
49
66
  };
50
67
 
51
- const _handleTags = (newValue: string) => {
52
- dispatch(setFormField("tags", newValue));
68
+ const handleTags = (newValue: string) => {
69
+ updateFormField("tags", newValue);
53
70
  };
54
71
 
55
72
  const handleSetAsGlobal = async () => {
56
73
  const saveAsGlobal = !isGlobalTab && addToGlobal.isChecked && imageSelected?.file;
57
- const image = saveAsGlobal && imageSelected && await uploadImage(imageSelected.file, "global");
58
- if (image) await saveImageData(image.id, state.imageForm)
74
+ const image = saveAsGlobal && imageSelected && (await uploadImage(imageSelected.file, "global"));
75
+ if (image && image.id) await updateImage(image.id, imageForm);
59
76
  setAddToGlobal({ value: "addToGlobal", isChecked: false });
60
77
  };
61
78
 
62
- const _handleSaveAndAdd = async () => {
63
- if (state.imageForm.id) {
64
- try {
65
- setIsSaving({ ...isSaving, saveAdd: true });
66
- await handleSetAsGlobal();
67
- const value = await saveImageData(state.imageForm.id, state.imageForm);
68
- if (value) {
69
- updateImage(value);
70
- }
71
- setIsSaving({ ...isSaving, saveAdd: false });
72
- setImage();
73
- } catch (error) {
74
- console.log(error);
75
- }
79
+ const handleSave = async () => {
80
+ if (imageForm.id) {
81
+ await updateImage(imageForm.id, imageForm);
82
+ await handleSetAsGlobal();
83
+ refreshImages();
76
84
  }
77
85
  };
78
86
 
79
- const _handleSave = async () => {
80
- if (state.imageForm.id) {
81
- try {
82
- setIsSaving({ ...isSaving, save: true });
83
- await handleSetAsGlobal();
84
- const value = await saveImageData(state.imageForm.id, state.imageForm);
85
- if (value) {
86
- updateImage(value);
87
- }
88
- setIsSaving({ ...isSaving, save: false });
89
- } catch (error) {
90
- console.log(error);
91
- }
87
+ const handleSaveAndAdd = async () => {
88
+ if (imageForm.id) {
89
+ await updateImage(imageForm.id, imageForm, true);
90
+ handleSetAsGlobal();
91
+ setImage(imageForm);
92
92
  }
93
93
  };
94
94
 
95
- const _handleDelete = () => {
96
- if (state.imageForm.id) {
97
- setIsSaving({ ...isSaving, delete: true });
98
- deleteImageData(state.imageForm.id)
99
- .then((value) => {
100
- if (value && state.imageForm.id) {
101
- deleteImage(state.imageForm.id);
102
- }
103
- setIsSaving({ ...isSaving, delete: false });
104
- })
105
- .catch((error) => {
106
- console.log(error);
107
- });
95
+ const handleDelete = async () => {
96
+ if (imageForm.id) {
97
+ const deleted = await deleteImage(imageForm.id);
98
+ if (deleted) setDeletedToast(true);
99
+ refreshImages();
108
100
  }
109
101
  };
110
102
 
111
- const _handleOnClickUrl = () => {
103
+ const handleOnClickUrl = () => {
112
104
  if (imageSelected) {
113
105
  const win = window.open(imageSelected.url, "_blank");
114
106
  if (win) {
@@ -137,14 +129,14 @@ const GalleryDetailPanel = (props: IProps) => {
137
129
 
138
130
  const canSetAsGlobal = !isGlobalTab && imageSelected?.file;
139
131
 
140
- const imageForm = !imageSelected ? null : (
132
+ const renderImageForm = !imageSelected ? null : (
141
133
  <S.PanelForm>
142
134
  {canSetAsGlobal && <AddToGlobal />}
143
135
  <S.DateWrapper>Uploaded: {getFormattedDateWithTimezone(imageSelected.published, "d MMM Y")}</S.DateWrapper>
144
136
  <S.ImageName>{imageSelected.name}</S.ImageName>
145
137
  <S.ImageInfoWrapper>
146
138
  <S.ImageWrapper>
147
- <img src={imageSelected.url} alt={state.imageForm.alt} />
139
+ <img src={imageSelected.url} alt={imageForm.alt} />
148
140
  </S.ImageWrapper>
149
141
  <S.ImageData>
150
142
  <S.ImageSize>{formatBytes(imageSelected.size)}</S.ImageSize>
@@ -159,42 +151,42 @@ const GalleryDetailPanel = (props: IProps) => {
159
151
  fieldType="TextField"
160
152
  readonly={true}
161
153
  icon="openOutside"
162
- onClickIcon={_handleOnClickUrl}
154
+ onClickIcon={handleOnClickUrl}
163
155
  />
164
156
  <FieldsBehavior
165
- title="Default aux text"
166
- name="alt"
167
- value={state.imageForm.alt}
157
+ title="Title"
158
+ name="title"
159
+ value={imageForm.title}
168
160
  fieldType="TextField"
169
- onChange={_handleAlt}
161
+ onChange={handleTitle}
170
162
  />
171
163
  <FieldsBehavior
172
- title="Title"
173
- name="title"
174
- value={state.imageForm.title}
164
+ title="Alternative text"
165
+ name="alt"
166
+ value={imageForm.alt}
175
167
  fieldType="TextField"
176
- onChange={_handleTitle}
168
+ onChange={handleAlt}
177
169
  />
178
170
  <FieldsBehavior
179
171
  title="Description"
180
172
  name="description"
181
- value={state.imageForm.description}
173
+ value={imageForm.description}
182
174
  fieldType="TextArea"
183
- onChange={_handleDescription}
175
+ onChange={handleDescription}
184
176
  />
185
- <FieldsBehavior title="Tags" value={state.imageForm.tags} fieldType="TagField" onChange={_handleTags} />
177
+ <FieldsBehavior title="Tags" value={imageForm.tags} fieldType="TagField" onChange={handleTags} />
186
178
  </S.FormWrapper>
187
179
  </S.PanelForm>
188
180
  );
189
181
 
190
182
  return (
191
183
  <S.DetailPanelWrapper hidden={!isImageSelected}>
192
- {imageForm}
184
+ {renderImageForm}
193
185
  <S.PanelActions>
194
186
  <Button
195
187
  type="button"
196
188
  buttonStyle="text"
197
- onClick={_handleDelete}
189
+ onClick={handleDelete}
198
190
  disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
199
191
  >
200
192
  {isSaving.delete ? `Deleting` : `Delete`}
@@ -202,30 +194,49 @@ const GalleryDetailPanel = (props: IProps) => {
202
194
  <Button
203
195
  type="button"
204
196
  buttonStyle="line"
205
- onClick={_handleSave}
197
+ onClick={handleSave}
206
198
  disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
207
199
  >
208
200
  {isSaving.save ? `Saving` : `Save`}
209
201
  </Button>
210
202
  <Button
211
203
  type="button"
212
- onClick={_handleSaveAndAdd}
204
+ onClick={handleSaveAndAdd}
213
205
  disabled={isSaving.save || isSaving.saveAdd || isSaving.delete}
214
206
  >
215
207
  {isSaving.saveAdd ? `Saving` : `Save & Add`}
216
208
  </Button>
217
209
  </S.PanelActions>
210
+ {deletedToast && <Toast message="1 image deleted" setIsVisible={setDeletedToast} />}
218
211
  </S.DetailPanelWrapper>
219
212
  );
220
213
  };
221
214
 
222
- interface IProps {
223
- setImage: () => void;
224
- updateImage: (image: IImage) => void;
225
- deleteImage: (imageID: number) => void;
215
+ interface IDetailPanelProps {
216
+ setImage: (imageData: any) => void;
226
217
  imageSelected: IImage | null;
227
218
  isImageSelected: boolean;
228
219
  isGlobalTab: boolean;
220
+ refreshImages: () => void;
221
+ isSaving: IIsSaving;
222
+ }
223
+
224
+ const mapStateToProps = (state: IRootState) => ({
225
+ isSaving: state.gallery.isSaving,
226
+ });
227
+
228
+ interface IDispatchProps {
229
+ updateImage: (imageID: number, imageData: IImageForm, add?: boolean) => Promise<void>;
230
+ deleteImage: (imageID: number) => Promise<boolean>;
231
+ uploadImage: (imageFile: File, site: number | string) => Promise<IImage>;
229
232
  }
230
233
 
231
- export default memo(GalleryDetailPanel);
234
+ const mapDispatchToProps = {
235
+ updateImage: galleryActions.updateImage,
236
+ deleteImage: galleryActions.deleteImage,
237
+ uploadImage: galleryActions.uploadImage,
238
+ };
239
+
240
+ type IProps = IDetailPanelProps & IDispatchProps;
241
+
242
+ export default connect(mapStateToProps, mapDispatchToProps)(memo(GalleryDetailPanel));