@griddo/ax 10.1.32 → 10.1.34
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/package.json +2 -2
- package/src/__tests__/components/Gallery/Gallery.test.tsx +63 -121
- package/src/__tests__/components/Gallery/GalleryPanel/DetailPanel/DetailPanel.test.tsx +3 -1
- package/src/__tests__/components/Gallery/GalleryPanel/GalleryPanel.test.tsx +0 -2
- package/src/components/FieldContainer/index.tsx +2 -2
- package/src/components/Fields/ImageField/index.tsx +1 -1
- package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +6 -2
- package/src/components/FieldsBehavior/index.tsx +1 -1
- package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +7 -2
- package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +1 -2
- package/src/components/Gallery/GalleryPanel/index.tsx +6 -5
- package/src/components/Gallery/index.tsx +24 -22
- package/src/components/TableFilters/CategoryFilter/style.tsx +1 -0
- package/src/components/Tooltip/index.tsx +13 -3
- package/src/components/Tooltip/style.tsx +9 -5
- package/src/containers/Gallery/actions.tsx +3 -24
- package/src/containers/Gallery/reducer.tsx +0 -4
- package/src/containers/StructuredData/actions.tsx +14 -1
- package/src/modules/Categories/CategoriesList/index.tsx +13 -4
- package/src/modules/Content/PageItem/index.tsx +15 -2
- package/src/modules/Content/PageItem/style.tsx +1 -2
- package/src/modules/Content/index.tsx +8 -7
- package/src/modules/Login/index.tsx +2 -2
- package/src/modules/Sites/index.tsx +18 -18
- package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +2 -2
- package/src/modules/StructuredData/StructuredDataList/ContentFilters/utils.tsx +1 -0
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +30 -12
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +9 -1
- package/src/modules/StructuredData/StructuredDataList/index.tsx +13 -15
- package/src/modules/Users/UserList/index.tsx +6 -3
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/ax",
|
|
3
3
|
"description": "Griddo Author Experience",
|
|
4
|
-
"version": "10.1.
|
|
4
|
+
"version": "10.1.34",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
|
|
7
7
|
"Carlos Torres <carlos.torres@secuoyas.com>",
|
|
@@ -230,5 +230,5 @@
|
|
|
230
230
|
"publishConfig": {
|
|
231
231
|
"access": "public"
|
|
232
232
|
},
|
|
233
|
-
"gitHead": "
|
|
233
|
+
"gitHead": "a32a21230a8bbfb36e6ac9f2f4b24065f880a61f"
|
|
234
234
|
}
|
|
@@ -149,72 +149,55 @@ describe("Gallery component rendering", () => {
|
|
|
149
149
|
expect(screen.queryAllByTestId("grid-item")).toHaveLength(2);
|
|
150
150
|
});
|
|
151
151
|
|
|
152
|
-
it("should
|
|
152
|
+
it("should show image data after click", () => {
|
|
153
153
|
const defaultGalleryProps = mock<IGalleryProps>();
|
|
154
154
|
const defaultDispatchProps = mock<IDispatchProps>();
|
|
155
155
|
|
|
156
|
-
const
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
title: "",
|
|
162
|
-
description: "",
|
|
163
|
-
alt: "",
|
|
164
|
-
tags: [],
|
|
165
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
166
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
167
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
168
|
-
damId: "imagen-interior-fullstack",
|
|
169
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
170
|
-
size: 55286,
|
|
171
|
-
width: 680,
|
|
172
|
-
height: 353,
|
|
173
|
-
orientation: "L",
|
|
174
|
-
site: "Global",
|
|
175
|
-
},
|
|
176
|
-
{
|
|
177
|
-
id: 1002,
|
|
178
|
-
name: "Imagen_Interior_fullstack_2.png",
|
|
179
|
-
title: "",
|
|
180
|
-
description: "",
|
|
181
|
-
alt: "",
|
|
182
|
-
tags: [],
|
|
183
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
184
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
185
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
186
|
-
damId: "imagen-interior-fullstack",
|
|
187
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
188
|
-
size: 55286,
|
|
189
|
-
width: 680,
|
|
190
|
-
height: 353,
|
|
191
|
-
orientation: "L",
|
|
192
|
-
site: "Global",
|
|
193
|
-
},
|
|
194
|
-
],
|
|
195
|
-
isImageSelected: true,
|
|
196
|
-
imageSelected: {
|
|
197
|
-
id: 1001,
|
|
198
|
-
name: "Imagen_Interior_fullstack.png",
|
|
199
|
-
title: "",
|
|
200
|
-
description: "",
|
|
201
|
-
alt: "",
|
|
202
|
-
tags: [],
|
|
203
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
204
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
205
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
206
|
-
damId: "imagen-interior-fullstack",
|
|
207
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
208
|
-
size: 55286,
|
|
209
|
-
width: 680,
|
|
210
|
-
height: 353,
|
|
211
|
-
orientation: "L",
|
|
212
|
-
site: "Global",
|
|
156
|
+
const responsePromise = Promise.resolve({
|
|
157
|
+
status: 200,
|
|
158
|
+
statusText: "ok",
|
|
159
|
+
data: {
|
|
160
|
+
items: [],
|
|
213
161
|
},
|
|
214
|
-
|
|
215
|
-
|
|
162
|
+
} as AxiosResponse);
|
|
163
|
+
|
|
164
|
+
mockedAxios.mockResolvedValue(responsePromise);
|
|
165
|
+
|
|
166
|
+
defaultGalleryProps.data = defaultDataProps;
|
|
167
|
+
defaultGalleryProps.site = defaultSiteProps;
|
|
168
|
+
defaultGalleryProps.isLoading = {
|
|
169
|
+
init: false,
|
|
170
|
+
more: false,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const defaultProps = {
|
|
174
|
+
...defaultGalleryProps,
|
|
175
|
+
...defaultDispatchProps,
|
|
216
176
|
};
|
|
217
177
|
|
|
178
|
+
render(
|
|
179
|
+
<ThemeProvider theme={parseTheme(globalTheme)}>
|
|
180
|
+
<Gallery {...defaultProps} />
|
|
181
|
+
</ThemeProvider>,
|
|
182
|
+
{ store }
|
|
183
|
+
);
|
|
184
|
+
store.clearActions();
|
|
185
|
+
|
|
186
|
+
const images = screen.queryAllByTestId("image-item");
|
|
187
|
+
expect(screen.queryAllByTestId("image-item")).toHaveLength(2);
|
|
188
|
+
|
|
189
|
+
act(() => {
|
|
190
|
+
fireEvent.click(images[0]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const imageName = screen.getByTestId("gallery-image-name");
|
|
194
|
+
expect(imageName).toHaveTextContent(defaultDataProps.items[0].name);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it("should hide image data after second click", () => {
|
|
198
|
+
const defaultGalleryProps = mock<IGalleryProps>();
|
|
199
|
+
const defaultDispatchProps = mock<IDispatchProps>();
|
|
200
|
+
|
|
218
201
|
const responsePromise = Promise.resolve({
|
|
219
202
|
status: 200,
|
|
220
203
|
statusText: "ok",
|
|
@@ -252,10 +235,13 @@ describe("Gallery component rendering", () => {
|
|
|
252
235
|
fireEvent.click(images[0]);
|
|
253
236
|
});
|
|
254
237
|
|
|
255
|
-
expect(
|
|
256
|
-
|
|
257
|
-
|
|
238
|
+
expect(screen.getByTestId("gallery-image-name")).toHaveTextContent(defaultDataProps.items[0].name);
|
|
239
|
+
|
|
240
|
+
act(() => {
|
|
241
|
+
fireEvent.click(images[0]);
|
|
258
242
|
});
|
|
243
|
+
|
|
244
|
+
expect(screen.queryByTestId("gallery-image-name")).toBeFalsy();
|
|
259
245
|
});
|
|
260
246
|
|
|
261
247
|
it("should activate the setImage action", async () => {
|
|
@@ -296,25 +282,6 @@ describe("Gallery component rendering", () => {
|
|
|
296
282
|
site: "Global",
|
|
297
283
|
},
|
|
298
284
|
],
|
|
299
|
-
isImageSelected: true,
|
|
300
|
-
imageSelected: {
|
|
301
|
-
id: 1002,
|
|
302
|
-
name: "Imagen_Interior_fullstack_2.png",
|
|
303
|
-
title: "",
|
|
304
|
-
description: "",
|
|
305
|
-
alt: "",
|
|
306
|
-
tags: [],
|
|
307
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
308
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
309
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
310
|
-
damId: "imagen-interior-fullstack",
|
|
311
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
312
|
-
size: 55286,
|
|
313
|
-
width: 680,
|
|
314
|
-
height: 353,
|
|
315
|
-
orientation: "L",
|
|
316
|
-
site: "Global",
|
|
317
|
-
},
|
|
318
285
|
page: 1,
|
|
319
286
|
isFinished: false,
|
|
320
287
|
};
|
|
@@ -359,11 +326,18 @@ describe("Gallery component rendering", () => {
|
|
|
359
326
|
{ store }
|
|
360
327
|
);
|
|
361
328
|
|
|
329
|
+
const images = screen.queryAllByTestId("image-item");
|
|
330
|
+
|
|
331
|
+
act(() => {
|
|
332
|
+
fireEvent.click(images[0]);
|
|
333
|
+
});
|
|
334
|
+
|
|
362
335
|
const buttonDetailPanel = screen.getByTestId("button-default");
|
|
363
336
|
|
|
364
337
|
act(() => {
|
|
365
338
|
fireEvent.click(buttonDetailPanel);
|
|
366
339
|
});
|
|
340
|
+
|
|
367
341
|
await waitFor(() => expect(getImageSelectedMocked).toBeCalled());
|
|
368
342
|
expect(toggleModalMock).toBeCalled();
|
|
369
343
|
});
|
|
@@ -426,25 +400,6 @@ describe("Gallery component rendering", () => {
|
|
|
426
400
|
site: "Global",
|
|
427
401
|
},
|
|
428
402
|
],
|
|
429
|
-
isImageSelected: true,
|
|
430
|
-
imageSelected: {
|
|
431
|
-
id: 1002,
|
|
432
|
-
name: "Imagen_Interior_fullstack_2.png",
|
|
433
|
-
title: "",
|
|
434
|
-
description: "",
|
|
435
|
-
alt: "",
|
|
436
|
-
tags: [],
|
|
437
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
438
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
439
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
440
|
-
damId: "imagen-interior-fullstack",
|
|
441
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
442
|
-
size: 55286,
|
|
443
|
-
width: 680,
|
|
444
|
-
height: 353,
|
|
445
|
-
orientation: "L",
|
|
446
|
-
site: "Global",
|
|
447
|
-
},
|
|
448
403
|
page: 2,
|
|
449
404
|
isFinished: false,
|
|
450
405
|
};
|
|
@@ -469,26 +424,7 @@ describe("Gallery component rendering", () => {
|
|
|
469
424
|
};
|
|
470
425
|
|
|
471
426
|
const dataProps = {
|
|
472
|
-
imageSelected: {
|
|
473
|
-
alt: "",
|
|
474
|
-
damId: "imagen-interior-fullstack",
|
|
475
|
-
description: "",
|
|
476
|
-
height: 353,
|
|
477
|
-
id: 1002,
|
|
478
|
-
name: "Imagen_Interior_fullstack_2.png",
|
|
479
|
-
orientation: "L",
|
|
480
|
-
publicId: "thesaurus-dev/Imagen_Interior_fullstack_6c908d9f-b493-4c93-b4d2-a0439bc00820",
|
|
481
|
-
published: new Date("2022-06-30T07:06:54.352Z"),
|
|
482
|
-
site: "Global",
|
|
483
|
-
size: 55286,
|
|
484
|
-
tags: [],
|
|
485
|
-
thumb: "https://images.dev.griddo.io/w/215/h/161/imagen-interior-fullstack",
|
|
486
|
-
title: "",
|
|
487
|
-
url: "https://images.dev.griddo.io/imagen-interior-fullstack",
|
|
488
|
-
width: 680,
|
|
489
|
-
},
|
|
490
427
|
isFinished: true,
|
|
491
|
-
isImageSelected: true,
|
|
492
428
|
items: [
|
|
493
429
|
{
|
|
494
430
|
alt: "",
|
|
@@ -551,9 +487,15 @@ describe("Gallery component rendering", () => {
|
|
|
551
487
|
{ store }
|
|
552
488
|
);
|
|
553
489
|
|
|
490
|
+
const images = screen.queryAllByTestId("image-item");
|
|
491
|
+
|
|
492
|
+
act(() => {
|
|
493
|
+
fireEvent.click(images[0]);
|
|
494
|
+
});
|
|
495
|
+
|
|
554
496
|
const buttonDetailPanel = screen.getAllByTestId("button-line-inverse");
|
|
555
497
|
|
|
556
|
-
await act(() => {
|
|
498
|
+
await act(async () => {
|
|
557
499
|
fireEvent.click(buttonDetailPanel[2]);
|
|
558
500
|
});
|
|
559
501
|
|
|
@@ -12,7 +12,7 @@ import { parseTheme } from "@ax/helpers";
|
|
|
12
12
|
import { history } from "@ax/routes";
|
|
13
13
|
import GalleryDetailPanel, { IDetailPanelProps, IDispatchProps } from "@ax/components/Gallery/GalleryPanel/DetailPanel";
|
|
14
14
|
import globalTheme from "@ax/themes/theme.json";
|
|
15
|
-
import { IImageForm } from "@ax/types";
|
|
15
|
+
import { IImage, IImageForm } from "@ax/types";
|
|
16
16
|
|
|
17
17
|
// for testing copy to clipboard
|
|
18
18
|
Object.assign(navigator, {
|
|
@@ -182,6 +182,7 @@ describe("Gallery component rendering", () => {
|
|
|
182
182
|
const deleteImageMocked = defaultDispatchProps.deleteImage as jest.MockedFunction<
|
|
183
183
|
(imageID: number) => Promise<boolean>
|
|
184
184
|
>;
|
|
185
|
+
const selectImageMocked = defaultDetailProps.selectImage as jest.MockedFunction<(img: IImage | null) => void>;
|
|
185
186
|
|
|
186
187
|
const defaultProps = {
|
|
187
188
|
...defaultDetailProps,
|
|
@@ -205,6 +206,7 @@ describe("Gallery component rendering", () => {
|
|
|
205
206
|
fireEvent.click(buttonWrapper);
|
|
206
207
|
});
|
|
207
208
|
expect(screen.getByText(/1 image deleted/i)).toBeInTheDocument();
|
|
209
|
+
expect(selectImageMocked).toBeCalled();
|
|
208
210
|
expect(refreshImagesMocked).toBeCalled();
|
|
209
211
|
});
|
|
210
212
|
|
|
@@ -21,8 +21,6 @@ const mockStore = configureStore(middlewares);
|
|
|
21
21
|
|
|
22
22
|
const defaultProps = mock<IGalleryPanelProps>();
|
|
23
23
|
|
|
24
|
-
defaultProps.isImageSelected = true;
|
|
25
|
-
defaultProps.imageSelected = null;
|
|
26
24
|
defaultProps.validFormats = ["png", "jpg"];
|
|
27
25
|
const setImageMocked = defaultProps.setImage as jest.MockedFunction<(imageData: any) => void>;
|
|
28
26
|
defaultProps.isGlobalTab = true;
|
|
@@ -13,7 +13,7 @@ const FieldContainer = (props: IFieldContainerProps) => {
|
|
|
13
13
|
|
|
14
14
|
const hideField = () => updateValue(key, nullValue);
|
|
15
15
|
|
|
16
|
-
const containerValue = selectedContent[objKey];
|
|
16
|
+
const containerValue = selectedContent[objKey] !== undefined ? selectedContent[objKey] : null;
|
|
17
17
|
|
|
18
18
|
const fieldProps = {
|
|
19
19
|
fieldType: type,
|
|
@@ -28,7 +28,7 @@ const FieldContainer = (props: IFieldContainerProps) => {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
const hasMultipleOptions = field.whiteList && field.whiteList.length > 1;
|
|
31
|
-
const nullValue = isContainer ? getNullValue(containerValue, hasMultipleOptions) : null;
|
|
31
|
+
const nullValue = isContainer && containerValue ? getNullValue(containerValue, hasMultipleOptions) : null;
|
|
32
32
|
const hasNullValue = JSON.stringify(containerValue) === JSON.stringify(nullValue);
|
|
33
33
|
|
|
34
34
|
const getField = () => <FieldsBehavior {...fieldProps} key={key} />;
|
|
@@ -81,7 +81,7 @@ const ImageField = (props: IImageFieldProps) => {
|
|
|
81
81
|
</S.IconWrapper>
|
|
82
82
|
<S.Text>Add image</S.Text>
|
|
83
83
|
</S.FieldWrapper>
|
|
84
|
-
{value && Object.keys(value).length > 1 && typeof value !== "string" && (
|
|
84
|
+
{value && previewSrc && Object.keys(value).length > 1 && typeof value !== "string" && (
|
|
85
85
|
<S.ImageDataWrapper data-testid="imageDataWrapper">
|
|
86
86
|
<S.FileName>{value.name}</S.FileName>
|
|
87
87
|
<S.ImageData>
|
|
@@ -87,8 +87,12 @@ const AutoItem = (props: IProps) => {
|
|
|
87
87
|
return false;
|
|
88
88
|
};
|
|
89
89
|
|
|
90
|
-
const handleSelectOnChange = (newValue:
|
|
91
|
-
|
|
90
|
+
const handleSelectOnChange = (newValue: string | null) => {
|
|
91
|
+
if(newValue){
|
|
92
|
+
getCategoryContent(newValue);
|
|
93
|
+
} else {
|
|
94
|
+
setState({ ...state, selected: "", options: [] });
|
|
95
|
+
}
|
|
92
96
|
};
|
|
93
97
|
|
|
94
98
|
const handleCheckChange = (checkValue: any) => {
|
|
@@ -52,7 +52,7 @@ const FieldsBehavior = (props: any): JSX.Element => {
|
|
|
52
52
|
|
|
53
53
|
const containerInfo: any = isComponentContainer && value && isEmptyContainer(value, hasMultipleOptions);
|
|
54
54
|
|
|
55
|
-
const
|
|
55
|
+
const isEmpty = containerInfo ? containerInfo.isEmpty : false;
|
|
56
56
|
const showTitle = hasTitle && (!isEmpty || hasMultipleOptions);
|
|
57
57
|
|
|
58
58
|
const Asterisk = () => (mandatory ? <S.Asterisk>*</S.Asterisk> : null);
|
|
@@ -19,6 +19,7 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
19
19
|
isSaving,
|
|
20
20
|
setImage,
|
|
21
21
|
uploadImage,
|
|
22
|
+
selectImage,
|
|
22
23
|
} = props;
|
|
23
24
|
|
|
24
25
|
const initialState: IImageForm = {
|
|
@@ -96,7 +97,10 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
96
97
|
const handleDelete = async () => {
|
|
97
98
|
if (imageForm.id) {
|
|
98
99
|
const deleted = await deleteImage(imageForm.id);
|
|
99
|
-
if (deleted)
|
|
100
|
+
if (deleted) {
|
|
101
|
+
selectImage(null);
|
|
102
|
+
setDeletedToast(true);
|
|
103
|
+
}
|
|
100
104
|
refreshImages();
|
|
101
105
|
}
|
|
102
106
|
};
|
|
@@ -145,7 +149,7 @@ const GalleryDetailPanel = (props: IProps) => {
|
|
|
145
149
|
</Button>
|
|
146
150
|
<IconAction icon="OpenOutside" size="m" onClick={handleOpenUrl} />
|
|
147
151
|
</S.ImageWrapper>
|
|
148
|
-
<S.ImageName>{imageSelected.name}</S.ImageName>
|
|
152
|
+
<S.ImageName data-testid="gallery-image-name">{imageSelected.name}</S.ImageName>
|
|
149
153
|
<S.Date>
|
|
150
154
|
<strong>Uploaded:</strong> {getFormattedDateWithTimezone(imageSelected.published, "d MMM Y")}
|
|
151
155
|
</S.Date>
|
|
@@ -226,6 +230,7 @@ export interface IDetailPanelProps {
|
|
|
226
230
|
isGlobalTab: boolean;
|
|
227
231
|
refreshImages: () => void;
|
|
228
232
|
isSaving: IIsSaving;
|
|
233
|
+
selectImage(item: IImage | null): void;
|
|
229
234
|
}
|
|
230
235
|
|
|
231
236
|
const mapStateToProps = (state: IRootState) => ({
|
|
@@ -206,14 +206,13 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
206
206
|
|
|
207
207
|
export interface IDispatchProps {
|
|
208
208
|
getSiteImages(params: IGetSiteImages): Promise<void>;
|
|
209
|
-
selectImage(item: IImage): void;
|
|
209
|
+
selectImage(item: IImage | null): void;
|
|
210
210
|
uploadError: (error: boolean, msg?: string) => Promise<void>;
|
|
211
211
|
uploadImage: (imageFiles: File | File[], site: number | string) => Promise<IImage>;
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
const mapDispatchToProps = {
|
|
215
215
|
getSiteImages: galleryActions.getSiteImages,
|
|
216
|
-
selectImage: galleryActions.selectImage,
|
|
217
216
|
uploadError: galleryActions.uploadError,
|
|
218
217
|
uploadImage: galleryActions.uploadImage,
|
|
219
218
|
};
|
|
@@ -7,32 +7,32 @@ import DetailPanel from "./DetailPanel";
|
|
|
7
7
|
import * as S from "./style";
|
|
8
8
|
|
|
9
9
|
const GalleryPanel = (props: IGalleryPanelProps) => {
|
|
10
|
-
const {
|
|
11
|
-
props;
|
|
10
|
+
const { imageSelected, validFormats, setImage, isGlobalTab, site, selectedTab, refreshImages, selectImage } = props;
|
|
12
11
|
const allowUpload = !isGlobalTab || !selectedTab;
|
|
13
12
|
|
|
14
13
|
return (
|
|
15
14
|
<S.GalleryPanel>
|
|
16
15
|
<GalleryDragAndDrop
|
|
17
|
-
isImageSelected={
|
|
16
|
+
isImageSelected={!!imageSelected}
|
|
18
17
|
validFormats={validFormats}
|
|
19
18
|
site={site}
|
|
20
19
|
allowUpload={allowUpload}
|
|
21
20
|
refreshImages={refreshImages}
|
|
21
|
+
selectImage={selectImage}
|
|
22
22
|
/>
|
|
23
23
|
<DetailPanel
|
|
24
24
|
imageSelected={imageSelected}
|
|
25
|
-
isImageSelected={
|
|
25
|
+
isImageSelected={!!imageSelected}
|
|
26
26
|
setImage={setImage}
|
|
27
27
|
isGlobalTab={isGlobalTab}
|
|
28
28
|
refreshImages={refreshImages}
|
|
29
|
+
selectImage={selectImage}
|
|
29
30
|
/>
|
|
30
31
|
</S.GalleryPanel>
|
|
31
32
|
);
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
export interface IGalleryPanelProps {
|
|
35
|
-
isImageSelected: boolean;
|
|
36
36
|
imageSelected: IImage | null;
|
|
37
37
|
validFormats: string[];
|
|
38
38
|
setImage: (imageData: any) => void;
|
|
@@ -40,6 +40,7 @@ export interface IGalleryPanelProps {
|
|
|
40
40
|
site: number | string;
|
|
41
41
|
selectedTab: string;
|
|
42
42
|
refreshImages: () => Promise<void>;
|
|
43
|
+
selectImage(item: IImage | null): void;
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export default memo(GalleryPanel);
|
|
@@ -10,21 +10,21 @@ import Orientation from "./GalleryFilters/Orientation";
|
|
|
10
10
|
import SortBy from "./GalleryFilters/SortBy";
|
|
11
11
|
import Type from "./GalleryFilters/Type";
|
|
12
12
|
import GalleryPanel from "./GalleryPanel";
|
|
13
|
-
import * as S from "./style";
|
|
14
13
|
import { useFilterQuery, useSortedListStatus } from "./hooks";
|
|
15
14
|
import { getSortedListStatus } from "./utils";
|
|
16
15
|
|
|
16
|
+
import * as S from "./style";
|
|
17
|
+
|
|
17
18
|
const itemsPerPage = 50;
|
|
18
19
|
const firstPage = 1;
|
|
19
20
|
|
|
20
21
|
const Gallery = (props: IProps): JSX.Element => {
|
|
21
|
-
const { data, isLoading, getSiteImages,
|
|
22
|
+
const { data, isLoading, getSiteImages, getImageSelected, toggleModal, site, uploadError } = props;
|
|
22
23
|
|
|
23
24
|
const tabs = [];
|
|
24
25
|
if (site) tabs.unshift(...["Local", "Global"]);
|
|
25
26
|
const [selectedTab, setSelectedTab] = useState(tabs[0]);
|
|
26
|
-
const [selectedImage, setSelectedImage] = useState<
|
|
27
|
-
const [imageSelectedData, setImageSelectedData] = useState<IData>(data);
|
|
27
|
+
const [selectedImage, setSelectedImage] = useState<IImage | null>(null);
|
|
28
28
|
const isLocalTab = selectedTab === "Local";
|
|
29
29
|
const isGlobalTab = selectedTab === "Global";
|
|
30
30
|
const galleryScope = isLocalTab ? site.id : "global";
|
|
@@ -57,10 +57,6 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
57
57
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
58
|
}, [galleryScope]);
|
|
59
59
|
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
setImageSelectedData(data);
|
|
62
|
-
}, [data]);
|
|
63
|
-
|
|
64
60
|
useEffect(() => {
|
|
65
61
|
const handleScroll = () => {
|
|
66
62
|
const loadMore =
|
|
@@ -88,17 +84,22 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
88
84
|
};
|
|
89
85
|
|
|
90
86
|
useEffect(() => {
|
|
87
|
+
setSelectedImage(null);
|
|
91
88
|
const params = getParams();
|
|
92
89
|
getSiteImages({ ...params, page: firstPage });
|
|
93
90
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
94
91
|
}, [currentFilterQuery, searchQuery]);
|
|
95
92
|
|
|
96
|
-
const handleClick = (item: IImage) => (
|
|
97
|
-
|
|
98
|
-
|
|
93
|
+
const handleClick = (item: IImage) => () => {
|
|
94
|
+
if(item.id !== selectedImage?.id) {
|
|
95
|
+
setSelectedImage(item);
|
|
96
|
+
} else {
|
|
97
|
+
setSelectedImage(null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
99
100
|
|
|
100
101
|
const setImage = (imageData: any) => {
|
|
101
|
-
const updatedImage = { ...
|
|
102
|
+
const updatedImage = { ... selectedImage,...imageData };
|
|
102
103
|
getImageSelected(updatedImage);
|
|
103
104
|
toggleModal();
|
|
104
105
|
};
|
|
@@ -118,6 +119,13 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
118
119
|
setCurrentFilterQuery(filterQuery);
|
|
119
120
|
};
|
|
120
121
|
|
|
122
|
+
const handleSelectedTab = (tab: string) => {
|
|
123
|
+
if (tab !== selectedTab) {
|
|
124
|
+
setSelectedImage(null);
|
|
125
|
+
setSelectedTab(tab);
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
|
|
121
129
|
const emptyStateProps = {
|
|
122
130
|
icon: "search",
|
|
123
131
|
title: "Oh! No Results Found",
|
|
@@ -132,7 +140,7 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
132
140
|
<S.Header>
|
|
133
141
|
{!!tabs.length && (
|
|
134
142
|
<S.TabsWrapper>
|
|
135
|
-
<Tabs tabs={tabs} active={selectedTab} setSelectedTab={
|
|
143
|
+
<Tabs tabs={tabs} active={selectedTab} setSelectedTab={handleSelectedTab} noMargins />
|
|
136
144
|
</S.TabsWrapper>
|
|
137
145
|
)}
|
|
138
146
|
<S.Filters>
|
|
@@ -169,11 +177,7 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
169
177
|
{data &&
|
|
170
178
|
data.items &&
|
|
171
179
|
data.items.map((item: IImage, index: number) => {
|
|
172
|
-
const isSelected =
|
|
173
|
-
if (isSelected && selectedImage !== item.id) {
|
|
174
|
-
setSelectedImage(item.id);
|
|
175
|
-
setImageSelectedData(data);
|
|
176
|
-
}
|
|
180
|
+
const isSelected = item.id === selectedImage?.id;
|
|
177
181
|
return (
|
|
178
182
|
<S.GridItem data-testid="grid-item" key={item.name + index} orientation={item.orientation}>
|
|
179
183
|
<S.ImageWrapper
|
|
@@ -205,14 +209,14 @@ const Gallery = (props: IProps): JSX.Element => {
|
|
|
205
209
|
</S.GalleryWrapper>
|
|
206
210
|
</S.GalleryTabs>
|
|
207
211
|
<GalleryPanel
|
|
208
|
-
|
|
209
|
-
imageSelected={imageSelectedData?.imageSelected}
|
|
212
|
+
imageSelected={selectedImage}
|
|
210
213
|
validFormats={validFormats}
|
|
211
214
|
setImage={setImage}
|
|
212
215
|
isGlobalTab={!isLocalTab}
|
|
213
216
|
site={galleryScope}
|
|
214
217
|
selectedTab={selectedTab}
|
|
215
218
|
refreshImages={refreshImages}
|
|
219
|
+
selectImage={setSelectedImage}
|
|
216
220
|
/>
|
|
217
221
|
</S.Wrapper>
|
|
218
222
|
);
|
|
@@ -233,13 +237,11 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
233
237
|
|
|
234
238
|
export interface IDispatchProps {
|
|
235
239
|
getSiteImages(params: IGetSiteImages): Promise<void>;
|
|
236
|
-
selectImage(item: IImage): void;
|
|
237
240
|
uploadError: (error: boolean, msg?: string) => Promise<void>;
|
|
238
241
|
}
|
|
239
242
|
|
|
240
243
|
const mapDispatchToProps = {
|
|
241
244
|
getSiteImages: galleryActions.getSiteImages,
|
|
242
|
-
selectImage: galleryActions.selectImage,
|
|
243
245
|
uploadError: galleryActions.uploadError,
|
|
244
246
|
};
|
|
245
247
|
|
|
@@ -4,7 +4,7 @@ import { useHandleClickOutside } from "@ax/hooks";
|
|
|
4
4
|
import * as S from "./style";
|
|
5
5
|
|
|
6
6
|
const Tooltip = (props: ITooltipProps): JSX.Element => {
|
|
7
|
-
const { content, children, hideOnClick = true, bottom, left } = props;
|
|
7
|
+
const { content, children, hideOnClick = true, bottom, left, expanded, top } = props;
|
|
8
8
|
|
|
9
9
|
const initialState: IState = {
|
|
10
10
|
active: false,
|
|
@@ -79,7 +79,13 @@ const Tooltip = (props: ITooltipProps): JSX.Element => {
|
|
|
79
79
|
if (!content) return children;
|
|
80
80
|
|
|
81
81
|
return (
|
|
82
|
-
<S.Tooltip
|
|
82
|
+
<S.Tooltip
|
|
83
|
+
data-testid="tooltip-component"
|
|
84
|
+
onMouseEnter={showTip}
|
|
85
|
+
onMouseLeave={hideTip}
|
|
86
|
+
onMouseDown={handleClick}
|
|
87
|
+
expanded={expanded}
|
|
88
|
+
>
|
|
83
89
|
<div ref={childrenRef}>{children}</div>
|
|
84
90
|
<S.Tip
|
|
85
91
|
data-testid="tip-component"
|
|
@@ -89,6 +95,8 @@ const Tooltip = (props: ITooltipProps): JSX.Element => {
|
|
|
89
95
|
ref={tipRef}
|
|
90
96
|
fixOutOfBounds={fixOutOfBounds}
|
|
91
97
|
left={left}
|
|
98
|
+
expanded={expanded}
|
|
99
|
+
top={top}
|
|
92
100
|
>
|
|
93
101
|
{content}
|
|
94
102
|
</S.Tip>
|
|
@@ -104,11 +112,13 @@ interface IState {
|
|
|
104
112
|
}
|
|
105
113
|
|
|
106
114
|
export interface ITooltipProps {
|
|
107
|
-
content
|
|
115
|
+
content?: string | boolean | JSX.Element[];
|
|
108
116
|
children: any;
|
|
109
117
|
hideOnClick?: boolean;
|
|
110
118
|
bottom?: boolean;
|
|
111
119
|
left?: number;
|
|
120
|
+
expanded?: boolean;
|
|
121
|
+
top?: number;
|
|
112
122
|
}
|
|
113
123
|
|
|
114
124
|
export default Tooltip;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
const Tooltip = styled.div
|
|
4
|
-
position: relative;
|
|
3
|
+
const Tooltip = styled.div<{ expanded?: boolean }>`
|
|
4
|
+
position: ${(p) => (p.expanded ? "static" : "relative")};
|
|
5
5
|
`;
|
|
6
6
|
|
|
7
7
|
const Tip = styled.span<{
|
|
@@ -10,24 +10,28 @@ const Tip = styled.span<{
|
|
|
10
10
|
bottom?: boolean;
|
|
11
11
|
fixOutOfBounds: number;
|
|
12
12
|
left?: number;
|
|
13
|
+
top?: number;
|
|
14
|
+
expanded?: boolean;
|
|
13
15
|
}>`
|
|
14
16
|
position: absolute;
|
|
15
|
-
top: ${(p) => (p.bottom ? "auto" : "-32px")};
|
|
17
|
+
top: ${(p) => (p.top ? `${p.top}px` : p.bottom ? "auto" : "-32px")};
|
|
16
18
|
bottom: ${(p) => (p.bottom ? "-32px" : "auto")};
|
|
17
19
|
left: ${(p) => (p.left ? `${p.left}px` : "auto")};
|
|
18
20
|
border-radius: 2px;
|
|
19
|
-
transform:
|
|
21
|
+
transform: ${(p) =>
|
|
22
|
+
p.expanded ? "none" : `translateX(calc(-50% + ${p.childrenWidth / 2}px - ${p.fixOutOfBounds}px))`};
|
|
20
23
|
padding: 8px;
|
|
21
24
|
color: #fff;
|
|
22
25
|
background: ${(p) => p.theme.color.uiMainMenuBackground};
|
|
23
26
|
font-size: 12px;
|
|
24
27
|
line-height: 1;
|
|
25
28
|
z-index: 100;
|
|
26
|
-
white-space: nowrap;
|
|
29
|
+
white-space: ${(p) => (p.expanded ? "normal" : "nowrap")};
|
|
27
30
|
display: ${(p) => (p.active ? "block" : "none")};
|
|
28
31
|
animation: fadeIn 0.3s;
|
|
29
32
|
font-weight: normal;
|
|
30
33
|
text-transform: none;
|
|
34
|
+
max-width: 350px;
|
|
31
35
|
|
|
32
36
|
@keyframes fadeIn {
|
|
33
37
|
from {
|
|
@@ -67,8 +67,6 @@ function getSiteImages(props: IGetSiteImages): (dispatch: Dispatch, getState: ()
|
|
|
67
67
|
setData({
|
|
68
68
|
items: images,
|
|
69
69
|
page,
|
|
70
|
-
isImageSelected: data.isImageSelected,
|
|
71
|
-
imageSelected: data.imageSelected,
|
|
72
70
|
isFinished: result.data.items.length < items,
|
|
73
71
|
})
|
|
74
72
|
);
|
|
@@ -81,19 +79,6 @@ function getSiteImages(props: IGetSiteImages): (dispatch: Dispatch, getState: ()
|
|
|
81
79
|
};
|
|
82
80
|
}
|
|
83
81
|
|
|
84
|
-
function selectImage(item: IImage): (dispatch: Dispatch, getState: () => IRootState) => Promise<void> {
|
|
85
|
-
return async (dispatch, getState) => {
|
|
86
|
-
const {
|
|
87
|
-
gallery: { data },
|
|
88
|
-
} = getState();
|
|
89
|
-
if (data?.imageSelected?.id === item.id) {
|
|
90
|
-
dispatch(setData({ ...data, imageSelected: null, isImageSelected: false }));
|
|
91
|
-
} else {
|
|
92
|
-
dispatch(setData({ ...data, imageSelected: item, isImageSelected: true }));
|
|
93
|
-
}
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
|
|
97
82
|
function uploadImage(imageFiles: File | File[], site: number | string): (dispatch: Dispatch) => Promise<IImage> {
|
|
98
83
|
return async (dispatch) => {
|
|
99
84
|
try {
|
|
@@ -162,14 +147,8 @@ function deleteImage(imageID: number): (dispatch: Dispatch, getState: () => IRoo
|
|
|
162
147
|
gallery: { data },
|
|
163
148
|
} = getState();
|
|
164
149
|
const responseActions = {
|
|
165
|
-
handleSuccess: () =>
|
|
166
|
-
|
|
167
|
-
return true;
|
|
168
|
-
},
|
|
169
|
-
handleError: (response: any) => {
|
|
170
|
-
appActions.handleError(response)(dispatch);
|
|
171
|
-
return false;
|
|
172
|
-
},
|
|
150
|
+
handleSuccess: () => null,
|
|
151
|
+
handleError: (response: any) => appActions.handleError(response)(dispatch),
|
|
173
152
|
};
|
|
174
153
|
|
|
175
154
|
const callback = async () => images.deleteImage(imageID);
|
|
@@ -182,4 +161,4 @@ function deleteImage(imageID: number): (dispatch: Dispatch, getState: () => IRoo
|
|
|
182
161
|
};
|
|
183
162
|
}
|
|
184
163
|
|
|
185
|
-
export { setIsLoading, getSiteImages, uploadImage, updateImage, deleteImage,
|
|
164
|
+
export { setIsLoading, getSiteImages, uploadImage, updateImage, deleteImage, uploadError };
|
|
@@ -11,8 +11,6 @@ import { GalleryActionsCreators } from "./interfaces";
|
|
|
11
11
|
|
|
12
12
|
export interface IData {
|
|
13
13
|
items: IImage[];
|
|
14
|
-
isImageSelected: boolean;
|
|
15
|
-
imageSelected: IImage | null;
|
|
16
14
|
page: number;
|
|
17
15
|
isFinished: boolean;
|
|
18
16
|
}
|
|
@@ -41,8 +39,6 @@ export interface IGalleryState {
|
|
|
41
39
|
export const initialState = {
|
|
42
40
|
data: {
|
|
43
41
|
items: [],
|
|
44
|
-
isImageSelected: false,
|
|
45
|
-
imageSelected: null,
|
|
46
42
|
page: 1,
|
|
47
43
|
isFinished: false,
|
|
48
44
|
},
|
|
@@ -577,7 +577,7 @@ function getDataContentTranslation(langID: number): (dispatch: Dispatch, getStat
|
|
|
577
577
|
|
|
578
578
|
const responseActions = {
|
|
579
579
|
handleSuccess: (data: IStructuredDataContent) => {
|
|
580
|
-
const newForm = {...data, canBeTranslated: false };
|
|
580
|
+
const newForm = { ...data, canBeTranslated: false };
|
|
581
581
|
dispatch(setForm(newForm));
|
|
582
582
|
dispatch(setIsIATranslated(true));
|
|
583
583
|
},
|
|
@@ -614,6 +614,18 @@ function setIsTranslated(isTranslated: boolean): (dispatch: Dispatch) => Promise
|
|
|
614
614
|
};
|
|
615
615
|
}
|
|
616
616
|
|
|
617
|
+
function resetCurrentData(): (dispatch: Dispatch) => Promise<void> {
|
|
618
|
+
return async (dispatch) => {
|
|
619
|
+
try {
|
|
620
|
+
dispatch(setCurrentData(null));
|
|
621
|
+
dispatch(setCurrentDataID(null));
|
|
622
|
+
dispatch(setCurrentDataContent([]));
|
|
623
|
+
} catch (e) {
|
|
624
|
+
console.log("Error", e);
|
|
625
|
+
}
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
|
|
617
629
|
export {
|
|
618
630
|
setIsActive,
|
|
619
631
|
setCategories,
|
|
@@ -649,4 +661,5 @@ export {
|
|
|
649
661
|
getDataContentTranslation,
|
|
650
662
|
setFormValues,
|
|
651
663
|
setIsTranslated,
|
|
664
|
+
resetCurrentData,
|
|
652
665
|
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useCallback, useState, useRef } from "react";
|
|
1
|
+
import React, { useEffect, useCallback, useState, useRef, useLayoutEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
4
|
import { structuredDataActions } from "@ax/containers/StructuredData";
|
|
@@ -32,6 +32,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
32
32
|
deleteDataContent,
|
|
33
33
|
restoreDataContent,
|
|
34
34
|
setHistoryPush,
|
|
35
|
+
resetCurrentData,
|
|
35
36
|
} = props;
|
|
36
37
|
|
|
37
38
|
const itemsPerPage = 50;
|
|
@@ -67,11 +68,11 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
67
68
|
}, [page]);
|
|
68
69
|
|
|
69
70
|
const getContents = useCallback(
|
|
70
|
-
(id: string) => {
|
|
71
|
+
async (id: string) => {
|
|
71
72
|
const params: any = getParams();
|
|
72
73
|
params.dataID = id;
|
|
73
74
|
setSelectedCategory(id, scope);
|
|
74
|
-
getStructuredDataContents(params, currentSiteID);
|
|
75
|
+
await getStructuredDataContents(params, currentSiteID);
|
|
75
76
|
},
|
|
76
77
|
[getStructuredDataContents, currentSiteID, getParams, setSelectedCategory, scope]
|
|
77
78
|
);
|
|
@@ -81,6 +82,12 @@ const CategoriesList = (props: IProps): JSX.Element => {
|
|
|
81
82
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
82
83
|
}, [lang, currentStructuredData, page]);
|
|
83
84
|
|
|
85
|
+
useLayoutEffect(() => {
|
|
86
|
+
return () => {
|
|
87
|
+
resetCurrentData();
|
|
88
|
+
};
|
|
89
|
+
}, []);
|
|
90
|
+
|
|
84
91
|
const handleClick = (dataID: string) => {
|
|
85
92
|
setSelectedCategory(dataID, scope);
|
|
86
93
|
};
|
|
@@ -235,13 +242,14 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
235
242
|
});
|
|
236
243
|
|
|
237
244
|
interface IDispatchProps {
|
|
238
|
-
getStructuredDataContents(params: IGetStructuredDataParams, siteID?: number | undefined): void
|
|
245
|
+
getStructuredDataContents(params: IGetStructuredDataParams, siteID?: number | undefined): Promise<void>;
|
|
239
246
|
resetCategoryValues(): void;
|
|
240
247
|
setLanguage(lang: { locale: string; id: number | null }): void;
|
|
241
248
|
setSelectedCategory(id: string, scope: string): void;
|
|
242
249
|
deleteDataContent(catID: number[]): Promise<boolean>;
|
|
243
250
|
restoreDataContent(catID: number | number[]): void;
|
|
244
251
|
setHistoryPush(path: string): void;
|
|
252
|
+
resetCurrentData(): Promise<void>;
|
|
245
253
|
}
|
|
246
254
|
|
|
247
255
|
interface ICategoriesProps {
|
|
@@ -266,6 +274,7 @@ const mapDispatchToProps = {
|
|
|
266
274
|
deleteDataContent: structuredDataActions.deleteStructuredDataContent,
|
|
267
275
|
restoreDataContent: structuredDataActions.restoreStructuredDataContent,
|
|
268
276
|
setHistoryPush: appActions.setHistoryPush,
|
|
277
|
+
resetCurrentData: structuredDataActions.resetCurrentData,
|
|
269
278
|
};
|
|
270
279
|
|
|
271
280
|
export default connect(mapStateToProps, mapDispatchToProps)(CategoriesList);
|
|
@@ -487,8 +487,21 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
|
|
|
487
487
|
Home
|
|
488
488
|
</S.Home>
|
|
489
489
|
)}
|
|
490
|
-
<
|
|
491
|
-
|
|
490
|
+
<Tooltip
|
|
491
|
+
content={[
|
|
492
|
+
<>
|
|
493
|
+
<strong>{page.title}</strong>
|
|
494
|
+
<br />
|
|
495
|
+
</>,
|
|
496
|
+
<>{fullPath.page}</>,
|
|
497
|
+
]}
|
|
498
|
+
left={0}
|
|
499
|
+
top={1}
|
|
500
|
+
expanded
|
|
501
|
+
>
|
|
502
|
+
<S.Title width={title.width}>{title.text}</S.Title>
|
|
503
|
+
{!isHome && <S.Slug width={path.width}>{path.text}</S.Slug>}
|
|
504
|
+
</Tooltip>
|
|
492
505
|
</S.NameCell>
|
|
493
506
|
{CategoryColumns}
|
|
494
507
|
{activeColumns.includes("type") && (
|
|
@@ -27,7 +27,6 @@ const NameCell = styled(Cell)`
|
|
|
27
27
|
const Title = styled.div<{ width: number }>`
|
|
28
28
|
${(p) => p.theme.textStyle.uiL};
|
|
29
29
|
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
30
|
-
display: inline;
|
|
31
30
|
overflow: hidden;
|
|
32
31
|
white-space: nowrap;
|
|
33
32
|
text-overflow: ellipsis;
|
|
@@ -35,7 +34,6 @@ const Title = styled.div<{ width: number }>`
|
|
|
35
34
|
`;
|
|
36
35
|
|
|
37
36
|
const Slug = styled.div<{ width: number }>`
|
|
38
|
-
display: inline;
|
|
39
37
|
overflow: hidden;
|
|
40
38
|
white-space: nowrap;
|
|
41
39
|
text-overflow: ellipsis;
|
|
@@ -121,6 +119,7 @@ const StyledActionMenu = styled(ActionMenu)`
|
|
|
121
119
|
`;
|
|
122
120
|
|
|
123
121
|
const PageRow = styled(Row)<{ global?: boolean }>`
|
|
122
|
+
position: relative;
|
|
124
123
|
&:hover {
|
|
125
124
|
${TypeCell}, ${SeoCell} {
|
|
126
125
|
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useEffect, useCallback, useState, useRef } from "react";
|
|
1
|
+
import React, { useEffect, useCallback, useState, useRef, useLayoutEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
import { useHistory, useLocation } from "react-router-dom";
|
|
4
4
|
import { schemas } from "components";
|
|
@@ -237,17 +237,17 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
237
237
|
return params;
|
|
238
238
|
}, [filter, currentSiteInfo, isStructuredData, page, searchQuery, lang]);
|
|
239
239
|
|
|
240
|
-
const getPages = (params: any, filterQuery?: any) => {
|
|
240
|
+
const getPages = async (params: any, filterQuery?: any) => {
|
|
241
241
|
const isStructuredDataPage = filter !== "unique-pages";
|
|
242
242
|
const pageFilter = isStructuredDataPage ? filter : undefined;
|
|
243
|
-
getSitePages(params, pageFilter, filterQuery);
|
|
243
|
+
await getSitePages(params, pageFilter, filterQuery);
|
|
244
244
|
};
|
|
245
245
|
|
|
246
246
|
const getSiteContent = useCallback(
|
|
247
|
-
(filterQuery?: any) => {
|
|
247
|
+
async (filterQuery?: any) => {
|
|
248
248
|
const params = getParams();
|
|
249
249
|
isStructuredData && params.siteID
|
|
250
|
-
? getStructuredDataContents(params, params.siteID)
|
|
250
|
+
? await getStructuredDataContents(params, params.siteID)
|
|
251
251
|
: getPages(params, filterQuery);
|
|
252
252
|
},
|
|
253
253
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
@@ -263,16 +263,17 @@ const Content = (props: IProps): JSX.Element => {
|
|
|
263
263
|
// eslint-disable-next-line
|
|
264
264
|
}, [page, filter, currentFilterQuery, searchQuery]);
|
|
265
265
|
|
|
266
|
-
|
|
266
|
+
useLayoutEffect(() => {
|
|
267
267
|
setPage(firstPage);
|
|
268
268
|
const initialColumns = filter !== "unique-pages" ? filterColumns : defaultColumns;
|
|
269
269
|
setColumnsState(initialColumns);
|
|
270
270
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
271
271
|
}, [filter]);
|
|
272
|
+
|
|
272
273
|
const params = { language: lang.id, recentSitesNumber: 7 };
|
|
273
274
|
const fetchSitesByLang = async () => await getSitesByLang(params);
|
|
274
275
|
|
|
275
|
-
|
|
276
|
+
useLayoutEffect(() => {
|
|
276
277
|
checkUserSession();
|
|
277
278
|
if (history.action !== "POP" && (!locationState || locationState.isFromEditor !== true)) {
|
|
278
279
|
setFilter("unique-pages");
|
|
@@ -28,7 +28,7 @@ const LoginModule = (props: IProps) => {
|
|
|
28
28
|
|
|
29
29
|
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
|
30
30
|
e.preventDefault();
|
|
31
|
-
login(state.email, state.password, state.rememberMe);
|
|
31
|
+
await login(state.email, state.password, state.rememberMe);
|
|
32
32
|
};
|
|
33
33
|
|
|
34
34
|
const _handleEmail = (email: string) => {
|
|
@@ -63,7 +63,7 @@ interface IProps {
|
|
|
63
63
|
isLoggingIn: boolean;
|
|
64
64
|
children: any;
|
|
65
65
|
globalSettings: IGlobalSettings;
|
|
66
|
-
login(email: string, password: string, rememberMe: boolean): void
|
|
66
|
+
login(email: string, password: string, rememberMe: boolean): Promise<void>;
|
|
67
67
|
resetError(): void;
|
|
68
68
|
getGlobalSettings(): void;
|
|
69
69
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, {
|
|
1
|
+
import React, { useLayoutEffect } from "react";
|
|
2
2
|
|
|
3
3
|
import { connect } from "react-redux";
|
|
4
4
|
import { withRouter, RouteComponentProps } from "react-router-dom";
|
|
@@ -25,25 +25,25 @@ const Sites = (props: ISitesProps): JSX.Element => {
|
|
|
25
25
|
globalLangs,
|
|
26
26
|
} = props;
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
const fetchInitialData = async () => {
|
|
30
|
+
setCurrentSiteInfo(null);
|
|
31
|
+
await getUser("me", token);
|
|
32
|
+
await getStructuredData(token);
|
|
33
|
+
await getAllDataPacks();
|
|
33
34
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
35
|
+
const defaultLanguage = globalLangs.find((language: any) => language.isDefault);
|
|
36
|
+
if (defaultLanguage) {
|
|
37
|
+
const { locale, id } = defaultLanguage;
|
|
38
|
+
const lang = {
|
|
39
|
+
locale,
|
|
40
|
+
id,
|
|
41
|
+
};
|
|
41
42
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
setLanguage(lang);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
45
46
|
|
|
46
|
-
useEffect(() => {
|
|
47
47
|
fetchInitialData();
|
|
48
48
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
49
49
|
}, [token]);
|
|
@@ -68,7 +68,7 @@ interface IDispatchProps {
|
|
|
68
68
|
getStructuredData(token: string, siteId?: number): Promise<void>;
|
|
69
69
|
setLanguage(lang: { locale: string; id: number | null }): void;
|
|
70
70
|
getAllDataPacks: () => Promise<void>;
|
|
71
|
-
getUser: (id: string) => Promise<void>;
|
|
71
|
+
getUser: (id: string, token?: string) => Promise<void>;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
export type ISitesProps = IStateProps & IDispatchProps & RouteComponentProps;
|
|
@@ -26,7 +26,7 @@ const ContentFilters = (props: IProps): JSX.Element => {
|
|
|
26
26
|
<S.Heading>{category.title}</S.Heading>
|
|
27
27
|
{category.filters &&
|
|
28
28
|
category.filters.map((filter: any, filterKey: number) => {
|
|
29
|
-
const { name, value } = filter;
|
|
29
|
+
const { name, value, editable } = filter;
|
|
30
30
|
|
|
31
31
|
const handleClick = () => {
|
|
32
32
|
onClick(value);
|
|
@@ -43,7 +43,7 @@ const ContentFilters = (props: IProps): JSX.Element => {
|
|
|
43
43
|
};
|
|
44
44
|
|
|
45
45
|
return (
|
|
46
|
-
<MenuItem key={filterKey} onClick={handleClick} extendedAction={isSelected ? extendedAction : null}>
|
|
46
|
+
<MenuItem key={filterKey} onClick={handleClick} extendedAction={isSelected && editable ? extendedAction : null}>
|
|
47
47
|
<NavLink to="#" className={selectedClass}>
|
|
48
48
|
<S.Link active={isSelected}>
|
|
49
49
|
{name}
|
|
@@ -54,6 +54,20 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
|
|
|
54
54
|
skipReview,
|
|
55
55
|
} = props;
|
|
56
56
|
|
|
57
|
+
const { locale } = lang;
|
|
58
|
+
const {
|
|
59
|
+
pageLanguages,
|
|
60
|
+
metaDescription,
|
|
61
|
+
metaTitle,
|
|
62
|
+
isIndexed,
|
|
63
|
+
availableSites,
|
|
64
|
+
structuredData,
|
|
65
|
+
structuredDataContent,
|
|
66
|
+
fullUrl,
|
|
67
|
+
} = globalPage;
|
|
68
|
+
|
|
69
|
+
const slug = fullUrl ? fullUrl.replace(/^.*\/\/[^\/]+\/[\w-.]+/, "") : "";
|
|
70
|
+
|
|
57
71
|
const activeColumns = Object.keys(columns).filter((col: string) => columns[col].show);
|
|
58
72
|
|
|
59
73
|
const initValue = { title: "", slug: "" };
|
|
@@ -67,19 +81,9 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
|
|
|
67
81
|
const theme: any = useTheme();
|
|
68
82
|
const nameCellPadding = Number(theme.spacing.s.slice(0, -2));
|
|
69
83
|
const title = useAdaptiveText(nameCellRef, globalPage.title, nameCellPadding);
|
|
84
|
+
const path = useAdaptiveText(nameCellRef, slug, nameCellPadding);
|
|
70
85
|
const API_URL = process.env.REACT_APP_API_ENDPOINT;
|
|
71
86
|
|
|
72
|
-
const { locale } = lang;
|
|
73
|
-
const {
|
|
74
|
-
pageLanguages,
|
|
75
|
-
metaDescription,
|
|
76
|
-
metaTitle,
|
|
77
|
-
isIndexed,
|
|
78
|
-
availableSites,
|
|
79
|
-
structuredData,
|
|
80
|
-
structuredDataContent,
|
|
81
|
-
} = globalPage;
|
|
82
|
-
|
|
83
87
|
const publishedTooltip: Record<string, string> = {
|
|
84
88
|
active: "Live",
|
|
85
89
|
"upload-pending": "Publication pending",
|
|
@@ -361,7 +365,21 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
|
|
|
361
365
|
<CheckField name="check" value={globalPage.id} checked={isSelected} onChange={handleOnChange} />
|
|
362
366
|
</S.CheckCell>
|
|
363
367
|
<S.NameCell role="cell" onClick={_handleClick} ref={nameCellRef}>
|
|
364
|
-
<
|
|
368
|
+
<Tooltip
|
|
369
|
+
content={[
|
|
370
|
+
<>
|
|
371
|
+
<strong>{globalPage.title}</strong>
|
|
372
|
+
<br />
|
|
373
|
+
</>,
|
|
374
|
+
<>{slug}</>,
|
|
375
|
+
]}
|
|
376
|
+
left={0}
|
|
377
|
+
top={1}
|
|
378
|
+
expanded
|
|
379
|
+
>
|
|
380
|
+
<S.Title width={title.width}>{title.text}</S.Title>
|
|
381
|
+
<S.Slug width={path.width}>{path.text}</S.Slug>
|
|
382
|
+
</Tooltip>
|
|
365
383
|
</S.NameCell>
|
|
366
384
|
{isAllPages && activeColumns.includes("type") && (
|
|
367
385
|
<S.TypeCell role="cell">{structuredData && getStructuredDataTitle(structuredData)}</S.TypeCell>
|
|
@@ -19,7 +19,13 @@ const NameCell = styled(Cell)`
|
|
|
19
19
|
const Title = styled.div<{ width: number }>`
|
|
20
20
|
${(p) => p.theme.textStyle.uiL};
|
|
21
21
|
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
22
|
-
|
|
22
|
+
overflow: hidden;
|
|
23
|
+
white-space: nowrap;
|
|
24
|
+
text-overflow: ellipsis;
|
|
25
|
+
width: ${(p) => `${p.width}px`};
|
|
26
|
+
`;
|
|
27
|
+
|
|
28
|
+
const Slug = styled.div<{ width: number }>`
|
|
23
29
|
overflow: hidden;
|
|
24
30
|
white-space: nowrap;
|
|
25
31
|
text-overflow: ellipsis;
|
|
@@ -83,6 +89,7 @@ const StyledActionMenu = styled(ActionMenu)`
|
|
|
83
89
|
`;
|
|
84
90
|
|
|
85
91
|
const StructuredDataRow = styled(Row)<{ disabled: boolean }>`
|
|
92
|
+
position: relative;
|
|
86
93
|
cursor: ${(p) => (p.disabled ? "default" : "pointer")};
|
|
87
94
|
&:hover {
|
|
88
95
|
${StyledActionMenu} {
|
|
@@ -174,4 +181,5 @@ export {
|
|
|
174
181
|
FloatingSeo,
|
|
175
182
|
ModalContent,
|
|
176
183
|
Title,
|
|
184
|
+
Slug,
|
|
177
185
|
};
|
|
@@ -148,7 +148,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
148
148
|
live: { title: "Live", show: true },
|
|
149
149
|
status: { title: "Status", show: true },
|
|
150
150
|
translation: { title: "Trans.", show: true },
|
|
151
|
-
...(isStructuredDataFromPage && { seo: { title: "SEO", show: true } }),
|
|
151
|
+
...((isStructuredDataFromPage || currentStructuredData === null) && { seo: { title: "SEO", show: true } }),
|
|
152
152
|
};
|
|
153
153
|
|
|
154
154
|
const extraColumns = categoryColumns.reduce((acc: Record<string, IColumn>, cur: any) => {
|
|
@@ -193,19 +193,19 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
193
193
|
}, [page, searchQuery]);
|
|
194
194
|
|
|
195
195
|
const getStructuredData = useCallback(
|
|
196
|
-
(id: string, filterQuery?: string) => {
|
|
196
|
+
async (id: string, filterQuery?: string) => {
|
|
197
197
|
const params: IGetStructuredDataParams = { ...getParams(), dataID: null };
|
|
198
198
|
params.dataID = id;
|
|
199
199
|
params.filterQuery = filterQuery;
|
|
200
|
-
getStructuredDataContents(params);
|
|
200
|
+
await getStructuredDataContents(params);
|
|
201
201
|
},
|
|
202
202
|
[getParams, getStructuredDataContents]
|
|
203
203
|
);
|
|
204
204
|
|
|
205
|
-
const handleGetGlobalPages = () => {
|
|
205
|
+
const handleGetGlobalPages = async () => {
|
|
206
206
|
const params: IGetGlobalPagesParams = getParams();
|
|
207
207
|
params.filterStructuredData = isStructuredDataFromPage && !isAllPages && filter;
|
|
208
|
-
getGlobalPages(params, currentFilterQuery);
|
|
208
|
+
await getGlobalPages(params, currentFilterQuery);
|
|
209
209
|
};
|
|
210
210
|
|
|
211
211
|
const handleGetStructuredData = () => getStructuredData(filter, currentFilterQuery);
|
|
@@ -233,17 +233,15 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
233
233
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
234
234
|
}, []);
|
|
235
235
|
|
|
236
|
-
|
|
236
|
+
useLayoutEffect(() => {
|
|
237
237
|
const isGlobalData = structuredData.global.some((data) => data.id === currentStructuredData?.id);
|
|
238
238
|
const type = currentStructuredData && isGlobalData ? currentStructuredData.id : "all";
|
|
239
239
|
setStructuredDataType(type);
|
|
240
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
241
|
-
}, [currentStructuredData]);
|
|
242
240
|
|
|
243
|
-
useEffect(() => {
|
|
244
241
|
filterItems("types", "all");
|
|
245
242
|
if (filter === "all-pages" || !isStructuredDataFromPage) {
|
|
246
243
|
filterItems("filterSites", "all");
|
|
244
|
+
setColumnsState({ all: allColumns });
|
|
247
245
|
}
|
|
248
246
|
unselectAllItems();
|
|
249
247
|
if (filter !== "all-pages" && currentStructuredData) {
|
|
@@ -257,7 +255,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
257
255
|
}));
|
|
258
256
|
}
|
|
259
257
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
260
|
-
}, [
|
|
258
|
+
}, [currentStructuredData]);
|
|
261
259
|
|
|
262
260
|
useEffect(() => {
|
|
263
261
|
if (!isFirstRender) {
|
|
@@ -289,9 +287,9 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
289
287
|
} else {
|
|
290
288
|
emptyState.message = isAllPages
|
|
291
289
|
? "You don’t have pages with this content type yet."
|
|
292
|
-
: "To start using pages in your site, create as many pages as you need.";
|
|
293
|
-
emptyState.button = isAllPages ? "View all content" : "Create the first page";
|
|
294
|
-
emptyState.action = isAllPages ? resetFilterValues : addNewAction;
|
|
290
|
+
: isDataEditable ? "To start using pages in your site, create as many pages as you need." : undefined;
|
|
291
|
+
emptyState.button = isAllPages ? "View all content" : isDataEditable ? "Create the first page" : undefined;
|
|
292
|
+
emptyState.action = isAllPages ? resetFilterValues : isDataEditable ? addNewAction : undefined;
|
|
295
293
|
}
|
|
296
294
|
const isEmpty = isStructuredDataFromPage ? !currentSitePages.length : !currentDataContent.length;
|
|
297
295
|
setIsEmpty(isEmpty);
|
|
@@ -320,7 +318,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
320
318
|
setHistoryPush(path, isFromPage);
|
|
321
319
|
};
|
|
322
320
|
|
|
323
|
-
const handleMenuClick = (dataID: string) => {
|
|
321
|
+
const handleMenuClick = async (dataID: string) => {
|
|
324
322
|
setPage(firstPage);
|
|
325
323
|
setSelectedStructuredData(dataID, scope);
|
|
326
324
|
};
|
|
@@ -702,7 +700,7 @@ interface IDispatchProps {
|
|
|
702
700
|
setHistoryPush(route: string, isEditor?: boolean): void;
|
|
703
701
|
resetForm(setDefault?: boolean): void;
|
|
704
702
|
setLanguage(lang: { locale: string; id: number }): void;
|
|
705
|
-
getStructuredDataContents(params: IGetStructuredDataParams): void
|
|
703
|
+
getStructuredDataContents(params: IGetStructuredDataParams): Promise<void>;
|
|
706
704
|
setSelectedStructuredData(id: string, scope: string): void;
|
|
707
705
|
deleteDataContent(dataID: number[]): Promise<boolean>;
|
|
708
706
|
restoreDataContent(dataID: number | number[]): void;
|
|
@@ -68,8 +68,11 @@ const UserList = (props: IProps): JSX.Element => {
|
|
|
68
68
|
} = useBulkSelection(usersIds);
|
|
69
69
|
|
|
70
70
|
useEffect(() => {
|
|
71
|
-
const
|
|
72
|
-
|
|
71
|
+
const handleGetUsers = async () => {
|
|
72
|
+
const query = searchQuery ? (currentFilterQuery ? `&query=${searchQuery}` : `?query=${searchQuery}`) : "";
|
|
73
|
+
await getUsers({ filterQuery: currentFilterQuery, query });
|
|
74
|
+
}
|
|
75
|
+
handleGetUsers();
|
|
73
76
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
74
77
|
}, [currentFilterQuery, searchQuery]);
|
|
75
78
|
|
|
@@ -241,7 +244,7 @@ const mapDispatchToProps = {
|
|
|
241
244
|
};
|
|
242
245
|
|
|
243
246
|
interface IDispatchProps {
|
|
244
|
-
getUsers(params?: any): void
|
|
247
|
+
getUsers(params?: any): Promise<void>;
|
|
245
248
|
getUser(id: number): Promise<void>;
|
|
246
249
|
resetUserData(): void;
|
|
247
250
|
setHistoryPush(route: string): void;
|