@griddo/ax 11.10.41 → 11.10.42

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 (79) hide show
  1. package/config/griddo-config/index.js +1 -0
  2. package/package.json +2 -2
  3. package/src/components/BulkSelectionOptions/index.tsx +78 -28
  4. package/src/components/BulkSelectionOptions/style.tsx +7 -1
  5. package/src/components/BulkSelectionOptions/utils.tsx +25 -0
  6. package/src/components/ConfigPanel/Form/ConnectedField/PageConnectedField/index.tsx +2 -2
  7. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +14 -4
  8. package/src/components/ConfigPanel/index.tsx +6 -0
  9. package/src/components/Fields/ComponentArray/SameComponentArray/index.tsx +9 -9
  10. package/src/components/Fields/ReferenceField/ItemList/Item/index.tsx +5 -6
  11. package/src/components/Fields/ReferenceField/ItemList/Item/style.tsx +16 -10
  12. package/src/components/Fields/ReferenceField/ManualPanel/Item/index.tsx +3 -4
  13. package/src/components/Fields/ReferenceField/ManualPanel/Item/style.tsx +7 -1
  14. package/src/components/Fields/VisualUniqueSelection/ScrollableSelection/index.tsx +3 -3
  15. package/src/components/Fields/VisualUniqueSelection/index.tsx +2 -4
  16. package/src/components/FloatingMenu/index.tsx +5 -5
  17. package/src/components/Loader/components/SmallCircle.js +29 -0
  18. package/src/components/Loader/index.tsx +3 -2
  19. package/src/components/MainWrapper/AppBar/atoms.tsx +4 -6
  20. package/src/components/MainWrapper/AppBar/index.tsx +14 -12
  21. package/src/components/MainWrapper/AppBar/style.tsx +23 -18
  22. package/src/components/MainWrapper/index.tsx +3 -3
  23. package/src/components/OcassionalToast/index.tsx +1 -1
  24. package/src/components/OcassionalToast/style.tsx +6 -0
  25. package/src/components/PageFinder/SelectionListItem/index.tsx +5 -4
  26. package/src/components/PageFinder/SelectionListItem/style.tsx +7 -1
  27. package/src/components/PageFinder/index.tsx +1 -0
  28. package/src/constants/index.ts +27 -0
  29. package/src/forms/elements.tsx +8 -24
  30. package/src/forms/index.tsx +0 -2
  31. package/src/helpers/containerEvaluations.tsx +5 -0
  32. package/src/helpers/content.ts +35 -0
  33. package/src/helpers/index.tsx +91 -89
  34. package/src/helpers/strings.tsx +19 -0
  35. package/src/helpers/types.ts +1 -0
  36. package/src/hooks/forms.tsx +2 -1
  37. package/src/locales/en-US.ts +29 -0
  38. package/src/locales/es-ES.ts +29 -0
  39. package/src/locales/index.ts +11 -0
  40. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +5 -0
  41. package/src/modules/Categories/CategoriesList/index.tsx +10 -4
  42. package/src/modules/Content/BulkHeader/index.tsx +11 -0
  43. package/src/modules/Content/PageItem/index.tsx +1 -0
  44. package/src/modules/Content/atoms.tsx +1 -1
  45. package/src/modules/Content/index.tsx +27 -2
  46. package/src/modules/FileDrive/BulkGridHeader/index.tsx +6 -1
  47. package/src/modules/FileDrive/BulkListHeader/index.tsx +5 -0
  48. package/src/modules/FileDrive/index.tsx +10 -0
  49. package/src/modules/Forms/FormCategoriesList/BulkHeader/index.tsx +15 -2
  50. package/src/modules/Forms/FormCategoriesList/index.tsx +8 -0
  51. package/src/modules/Forms/FormEditor/index.tsx +10 -1
  52. package/src/modules/Forms/FormList/BulkHeader/index.tsx +5 -0
  53. package/src/modules/Forms/FormList/index.tsx +12 -3
  54. package/src/modules/Forms/FormUseModal/FormUseItem/index.tsx +1 -2
  55. package/src/modules/Forms/FormUseModal/index.tsx +3 -3
  56. package/src/modules/GlobalEditor/index.tsx +1 -0
  57. package/src/modules/MediaGallery/BulkGridHeader/index.tsx +6 -1
  58. package/src/modules/MediaGallery/BulkListHeader/index.tsx +15 -2
  59. package/src/modules/MediaGallery/index.tsx +47 -36
  60. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +5 -0
  61. package/src/modules/Navigation/Defaults/index.tsx +26 -23
  62. package/src/modules/PageEditor/Editor/index.tsx +6 -0
  63. package/src/modules/PageEditor/index.tsx +52 -0
  64. package/src/modules/Redirects/BulkHeader/index.tsx +6 -1
  65. package/src/modules/Redirects/index.tsx +7 -2
  66. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +5 -0
  67. package/src/modules/Settings/Integrations/index.tsx +7 -1
  68. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +5 -0
  69. package/src/modules/Sites/SitesList/index.tsx +7 -2
  70. package/src/modules/StructuredData/Form/index.tsx +1 -0
  71. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +5 -0
  72. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/atoms.tsx +3 -2
  73. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +17 -11
  74. package/src/modules/StructuredData/StructuredDataList/index.tsx +6 -0
  75. package/src/modules/Users/Roles/BulkHeader/index.tsx +5 -0
  76. package/src/modules/Users/Roles/index.tsx +6 -1
  77. package/src/modules/Users/UserList/BulkHeader/index.tsx +5 -0
  78. package/src/modules/Users/UserList/index.tsx +12 -7
  79. package/tsconfig.paths.json +4 -1
@@ -1,3 +1,4 @@
1
+ import { itemLabel } from "@ax/constants";
1
2
  import type { IBulkSelectedItems } from "@ax/hooks";
2
3
  import type { IBulkAction } from "@ax/types";
3
4
 
@@ -6,7 +7,8 @@ import GridHeader from "./GridHeader";
6
7
  import * as S from "./style";
7
8
 
8
9
  const BulkGridHeader = (props: IBulkHeaderProps): JSX.Element => {
9
- const { showBulk, checkState, selectItems, selectAllItems, totalItems, bulkActions, selectedItems } = props;
10
+ const { showBulk, checkState, selectItems, selectAllItems, totalItems, bulkActions, selectedItems, isLoading } =
11
+ props;
10
12
 
11
13
  return showBulk ? (
12
14
  <S.BulkWrapper>
@@ -17,6 +19,8 @@ const BulkGridHeader = (props: IBulkHeaderProps): JSX.Element => {
17
19
  selectItems={selectItems}
18
20
  totalItems={totalItems}
19
21
  selectedItems={selectedItems}
22
+ isLoading={isLoading}
23
+ itemLabel={itemLabel.IMAGE}
20
24
  />
21
25
  </S.BulkWrapper>
22
26
  ) : (
@@ -32,6 +36,7 @@ export interface IBulkHeaderProps {
32
36
  totalItems: number;
33
37
  bulkActions: IBulkAction[];
34
38
  selectedItems: IBulkSelectedItems;
39
+ isLoading: boolean;
35
40
  }
36
41
 
37
42
  export default BulkGridHeader;
@@ -1,3 +1,4 @@
1
+ import { itemLabel } from "@ax/constants";
1
2
  import type { IBulkSelectedItems } from "@ax/hooks";
2
3
  import type { IBulkAction } from "@ax/types";
3
4
 
@@ -6,8 +7,17 @@ import TableHeader from "./TableHeader";
6
7
  import * as S from "./style";
7
8
 
8
9
  const BulkListHeader = (props: IBulkHeaderProps): JSX.Element => {
9
- const { showBulk, checkState, selectItems, selectAllItems, totalItems, bulkActions, setHoverCheck, selectedItems } =
10
- props;
10
+ const {
11
+ showBulk,
12
+ checkState,
13
+ selectItems,
14
+ selectAllItems,
15
+ totalItems,
16
+ bulkActions,
17
+ setHoverCheck,
18
+ selectedItems,
19
+ isLoading,
20
+ } = props;
11
21
 
12
22
  return showBulk ? (
13
23
  <S.BulkWrapper>
@@ -18,6 +28,8 @@ const BulkListHeader = (props: IBulkHeaderProps): JSX.Element => {
18
28
  selectItems={selectItems}
19
29
  totalItems={totalItems}
20
30
  selectedItems={selectedItems}
31
+ isLoading={isLoading}
32
+ itemLabel={itemLabel.IMAGE}
21
33
  />
22
34
  </S.BulkWrapper>
23
35
  ) : (
@@ -40,6 +52,7 @@ export interface IBulkHeaderProps {
40
52
  bulkActions: IBulkAction[];
41
53
  setHoverCheck: (state: boolean) => void;
42
54
  selectedItems: IBulkSelectedItems;
55
+ isLoading: boolean;
43
56
  }
44
57
 
45
58
  export default BulkListHeader;
@@ -86,10 +86,11 @@ const MediaGallery = (props: IProps) => {
86
86
  const firstPage = 1;
87
87
  const lastPage = Math.ceil(totalItems / itemsPerPage);
88
88
  const [page, setPage] = useState(firstPage);
89
+ const [isBulkLoading, setIsBulkLoading] = useState(false);
89
90
  const [isPanelOpen, setPanelOpen] = useState(false);
90
91
  const [fileSelected, setFileSelected] = useState<IImage | null>(null);
91
92
  const [selectedFolder, setSelectedFolder] = useState<number | null>(currentFolderID || null);
92
- const imagesIds: number[] = (items && items.map((image) => image.id)) || [];
93
+ const imagesIds = items?.map((image) => image.id) || [];
93
94
  const [galleryItems, setGalleryItems] = useState<number[]>(imagesIds);
94
95
  const [galleryDelete, setGalleryDelete] = useState(true);
95
96
  const [progressItems, setProgressItems] = useState<IProgressState>({
@@ -381,6 +382,7 @@ const MediaGallery = (props: IProps) => {
381
382
  };
382
383
 
383
384
  const handleBulkDeleteFile = async () => {
385
+ setIsBulkLoading(true);
384
386
  const params = getParams();
385
387
  const resetedParams = { ...params, page: firstPage, infinite: false, loading: true };
386
388
  const isDeleted = await deleteImage(selectedItems.all, resetedParams);
@@ -389,18 +391,22 @@ const MediaGallery = (props: IProps) => {
389
391
  resetBulkSelection();
390
392
  }
391
393
  toggleDeleteModal();
394
+ setIsBulkLoading(false);
392
395
  };
393
396
 
394
397
  const handleMoveImage = async (imageID: number, folderID: number | "root") => {
398
+ setIsBulkLoading(true);
395
399
  const params = getParams();
396
400
  const resetedParams = { ...params, page: firstPage, infinite: false, loading: true };
397
401
  const isMoved = await moveImage(imageID, folderID, resetedParams);
398
402
  if (isMoved) {
399
403
  toggleToast("1 Image moved to a folder");
400
404
  }
405
+ setIsBulkLoading(false);
401
406
  };
402
407
 
403
408
  const handleBulkMoveFile = async () => {
409
+ setIsBulkLoading(true);
404
410
  const params = getParams();
405
411
  const resetedParams = { ...params, page: firstPage, infinite: false, loading: true };
406
412
  const isMoved = await moveImage(selectedItems.all, selectedFolder || "root", resetedParams);
@@ -410,6 +416,7 @@ const MediaGallery = (props: IProps) => {
410
416
  setSelectedFolder(0);
411
417
  resetBulkSelection();
412
418
  }
419
+ setIsBulkLoading(false);
413
420
  };
414
421
 
415
422
  const handleBulkEditFile = () => {
@@ -435,9 +442,11 @@ const MediaGallery = (props: IProps) => {
435
442
  };
436
443
 
437
444
  const handleDownloadBulk = async () => {
445
+ setIsBulkLoading(true);
438
446
  const fileName = isSiteView ? `${currentSiteInfo.name}-images.zip` : "global-images.zip";
439
447
  await downloadImages(selectedItems.all, true, fileName);
440
448
  resetBulkSelection();
449
+ setIsBulkLoading(false);
441
450
  };
442
451
 
443
452
  const handleUpdateCurrentFolder = (folderID: number | null) => {
@@ -513,6 +522,7 @@ const MediaGallery = (props: IProps) => {
513
522
  bulkActions={bulkActions}
514
523
  setHoverCheck={setHoverCheck}
515
524
  selectedItems={selectedItems}
525
+ isLoading={isBulkLoading}
516
526
  />
517
527
  );
518
528
 
@@ -525,23 +535,23 @@ const MediaGallery = (props: IProps) => {
525
535
  <UploadItem uploadPercentage={progress} grid={true} key={index} />
526
536
  ))}
527
537
  {items?.map((image: IImage) => {
528
- const isItemSelected = isSelected(image.id);
529
- return (
530
- <GridItem
531
- image={image}
532
- isSelected={isItemSelected}
533
- onChange={addToBulkSelection}
534
- key={`${image.name}${image.id}`}
535
- onClick={handleClick}
536
- onDelete={handleDeleteFile}
537
- onMove={handleMoveImage}
538
- currentFolderID={currentFolderID}
539
- isAllowedToDelete={allowedToDeleteFile}
540
- isAllowedToEdit={allowedToEditFile}
541
- isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
542
- />
543
- );
544
- })}
538
+ const isItemSelected = isSelected(image.id);
539
+ return (
540
+ <GridItem
541
+ image={image}
542
+ isSelected={isItemSelected}
543
+ onChange={addToBulkSelection}
544
+ key={`${image.name}${image.id}`}
545
+ onClick={handleClick}
546
+ onDelete={handleDeleteFile}
547
+ onMove={handleMoveImage}
548
+ currentFolderID={currentFolderID}
549
+ isAllowedToDelete={allowedToDeleteFile}
550
+ isAllowedToEdit={allowedToEditFile}
551
+ isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
552
+ />
553
+ );
554
+ })}
545
555
  </S.GridWrapper>
546
556
  );
547
557
 
@@ -566,24 +576,24 @@ const MediaGallery = (props: IProps) => {
566
576
  <UploadItem uploadPercentage={progress} grid={false} key={index} />
567
577
  ))}
568
578
  {items?.map((image: IImage) => {
569
- const isItemSelected = isSelected(image.id);
570
- return (
571
- <ListItem
572
- image={image}
573
- isSelected={isItemSelected}
574
- onChange={addToBulkSelection}
575
- key={`${image.name}${image.id}`}
576
- onClick={handleClick}
577
- onDelete={handleDeleteFile}
578
- onMove={handleMoveImage}
579
- currentFolderID={currentFolderID}
580
- isAllowedToDelete={allowedToDeleteFile}
581
- isAllowedToEdit={allowedToEditFile}
582
- isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
583
- hoverCheck={checkState.hoverCheck}
584
- />
585
- );
586
- })}
579
+ const isItemSelected = isSelected(image.id);
580
+ return (
581
+ <ListItem
582
+ image={image}
583
+ isSelected={isItemSelected}
584
+ onChange={addToBulkSelection}
585
+ key={`${image.name}${image.id}`}
586
+ onClick={handleClick}
587
+ onDelete={handleDeleteFile}
588
+ onMove={handleMoveImage}
589
+ currentFolderID={currentFolderID}
590
+ isAllowedToDelete={allowedToDeleteFile}
591
+ isAllowedToEdit={allowedToEditFile}
592
+ isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
593
+ hoverCheck={checkState.hoverCheck}
594
+ />
595
+ );
596
+ })}
587
597
  </>
588
598
  </TableList>
589
599
  );
@@ -770,6 +780,7 @@ const MediaGallery = (props: IProps) => {
770
780
  selectItems={selectItems}
771
781
  bulkActions={bulkActions}
772
782
  selectedItems={selectedItems}
783
+ isLoading={isBulkLoading}
773
784
  />
774
785
  )}
775
786
  {!hasFolders && isRoot && !isSearching && <NewFolderButton />}
@@ -1,4 +1,5 @@
1
1
  import { BulkSelectionOptions } from "@ax/components";
2
+ import { itemLabel } from "@ax/constants";
2
3
  import type { IBulkSelectedItems } from "@ax/hooks";
3
4
 
4
5
  import TableHeader from "./TableHeader";
@@ -14,6 +15,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
14
15
  isScrolling,
15
16
  setHoverCheck,
16
17
  selectedItems,
18
+ isLoading,
17
19
  } = props;
18
20
 
19
21
  return showBulk ? (
@@ -23,6 +25,8 @@ const BulkHeader = (props: IProps): JSX.Element => {
23
25
  selectItems={selectItems}
24
26
  totalItems={totalItems}
25
27
  selectedItems={selectedItems}
28
+ isLoading={isLoading}
29
+ itemLabel={itemLabel.NAVIGATION}
26
30
  />
27
31
  ) : (
28
32
  <TableHeader
@@ -45,6 +49,7 @@ interface IProps {
45
49
  isScrolling: boolean;
46
50
  setHoverCheck: (state: boolean) => void;
47
51
  selectedItems: IBulkSelectedItems;
52
+ isLoading: boolean;
48
53
  }
49
54
 
50
55
  export default BulkHeader;
@@ -13,6 +13,7 @@ import BulkHeader from "./BulkHeader";
13
13
  import DefaultItem from "./Item";
14
14
  import DefaultNav from "./Nav";
15
15
  import ReplaceNavModal from "./ReplaceNavModal";
16
+
16
17
  import * as S from "./style";
17
18
 
18
19
  const DefaultsList = (props: IProps): JSX.Element => {
@@ -36,6 +37,7 @@ const DefaultsList = (props: IProps): JSX.Element => {
36
37
  currentSiteInfo,
37
38
  } = props;
38
39
 
40
+ const [isBulkLoading, setIsBulkLoading] = useState(false);
39
41
  const [page, setPage] = useState(1);
40
42
  const [deletedNav, setDeletedNav] = useState<number | number[] | null>(null);
41
43
  const [updatedPages, setUpdatedPages] = useState<number | null>(null);
@@ -51,7 +53,7 @@ const DefaultsList = (props: IProps): JSX.Element => {
51
53
  const isAllowedToDeleteHeaders = usePermission("navigation.deleteSiteHeaders") && selectedDefault === "Headers";
52
54
  const isAllowedToDeleteFooters = usePermission("navigation.deleteSiteFooters") && selectedDefault === "Footers";
53
55
 
54
- const navIds = currentDefaultsContent && currentDefaultsContent.map((nav: any) => nav.id);
56
+ const navIds = currentDefaultsContent?.map((nav: any) => nav.id);
55
57
 
56
58
  const {
57
59
  resetBulkSelection,
@@ -108,8 +110,7 @@ const DefaultsList = (props: IProps): JSX.Element => {
108
110
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
109
111
  useEffect(() => {
110
112
  const isNavigationModulesChanged =
111
- currentSiteInfo &&
112
- currentSiteInfo.navigationModules?.[currentType] &&
113
+ currentSiteInfo?.navigationModules?.[currentType] &&
113
114
  currentDefaultsContent.some(
114
115
  (navigation) => navigation.component !== currentSiteInfo?.navigationModules[currentType],
115
116
  );
@@ -175,11 +176,13 @@ const DefaultsList = (props: IProps): JSX.Element => {
175
176
  const handleErrorAction = () => toggleReplaceModal();
176
177
 
177
178
  const bulkDelete = async () => {
179
+ setIsBulkLoading(true);
178
180
  const deleted = await deleteNavigation(selectedItems.all, currentType, handleErrorAction);
179
181
  setDeletedNav(selectedItems.all);
180
182
  if (deleted) {
181
183
  toggleToast();
182
184
  }
185
+ setIsBulkLoading(false);
183
186
  };
184
187
 
185
188
  const unselectAllItems = () => resetBulkSelection();
@@ -212,6 +215,7 @@ const DefaultsList = (props: IProps): JSX.Element => {
212
215
  checkState={checkState}
213
216
  isScrolling={isScrolling}
214
217
  setHoverCheck={setHoverCheck}
218
+ isLoading={isBulkLoading}
215
219
  />
216
220
  );
217
221
 
@@ -248,26 +252,25 @@ const DefaultsList = (props: IProps): JSX.Element => {
248
252
  hasFixedHeader={true}
249
253
  tableRef={tableRef}
250
254
  >
251
- {currentDefaultsContent &&
252
- currentDefaultsContent.map((defaultContent: IHeader | IFooter) => {
253
- const isItemSelected = isSelected(defaultContent.id);
254
- return (
255
- <DefaultItem
256
- defaultContent={defaultContent}
257
- key={defaultContent.id}
258
- languages={siteLanguages}
259
- lang={lang}
260
- toggleToast={toggleToast}
261
- setDeletedNav={setDeletedNav}
262
- isSelected={isItemSelected}
263
- onChange={addToBulkSelection}
264
- handleClick={setContent}
265
- setHistoryPush={setHistoryPush}
266
- toggleReplaceModal={toggleReplaceModal}
267
- hoverCheck={checkState.hoverCheck}
268
- />
269
- );
270
- })}
255
+ {currentDefaultsContent?.map((defaultContent: IHeader | IFooter) => {
256
+ const isItemSelected = isSelected(defaultContent.id);
257
+ return (
258
+ <DefaultItem
259
+ defaultContent={defaultContent}
260
+ key={defaultContent.id}
261
+ languages={siteLanguages}
262
+ lang={lang}
263
+ toggleToast={toggleToast}
264
+ setDeletedNav={setDeletedNav}
265
+ isSelected={isItemSelected}
266
+ onChange={addToBulkSelection}
267
+ handleClick={setContent}
268
+ setHistoryPush={setHistoryPush}
269
+ toggleReplaceModal={toggleReplaceModal}
270
+ hoverCheck={checkState.hoverCheck}
271
+ />
272
+ );
273
+ })}
271
274
  </TableList>
272
275
  </S.TableWrapper>
273
276
  {isVisible && <Toast {...toastProps} />}
@@ -43,6 +43,8 @@ const Editor = (props: IProps) => {
43
43
  content,
44
44
  isEditLive,
45
45
  isDisabled,
46
+ isDirty,
47
+ onNavigateWithDirty,
46
48
  } = props;
47
49
 
48
50
  if (!site) {
@@ -101,6 +103,8 @@ const Editor = (props: IProps) => {
101
103
  footer={footer}
102
104
  isEditLive={isEditLive}
103
105
  isDisabled={isDisabled}
106
+ isDirty={isDirty}
107
+ onNavigateWithDirty={onNavigateWithDirty}
104
108
  />
105
109
  }
106
110
  />
@@ -146,6 +150,8 @@ interface IPageBrowserDispatchProps {
146
150
  browserRef: any;
147
151
  isEditLive: boolean;
148
152
  isDisabled: boolean;
153
+ isDirty?: boolean;
154
+ onNavigateWithDirty?: (navigateCallback: () => void) => void;
149
155
  }
150
156
 
151
157
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -93,6 +93,8 @@ const PageEditor = (props: IProps) => {
93
93
  const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
94
94
  const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
95
95
  const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
96
+ const { isOpen: isDirtyNavigateOpen, toggleModal: toggleDirtyNavigateModal } = useModal();
97
+ const [pendingNavigateCallback, setPendingNavigateCallback] = useState<(() => void) | null>(null);
96
98
  const [errorPagesChecked, setErrorPagesChecked] = useState(false);
97
99
  const browserRef = useRef<HTMLDivElement>(null);
98
100
 
@@ -616,6 +618,40 @@ const PageEditor = (props: IProps) => {
616
618
  }
617
619
  };
618
620
 
621
+ const onNavigateWithDirty = (navigateCallback: () => void) => {
622
+ setPendingNavigateCallback(() => navigateCallback);
623
+ toggleDirtyNavigateModal();
624
+ };
625
+
626
+ const handleConfirmDirtyNavigation = () => {
627
+ toggleDirtyNavigateModal();
628
+ if (pendingNavigateCallback) {
629
+ pendingNavigateCallback();
630
+ setPendingNavigateCallback(null);
631
+ }
632
+ };
633
+
634
+ const handleCancelDirtyNavigation = () => {
635
+ toggleDirtyNavigateModal();
636
+ setPendingNavigateCallback(null);
637
+ };
638
+
639
+ const dirtyNavigateModalText = (
640
+ <>
641
+ Some content <strong>is not saved</strong> on this page.
642
+ </>
643
+ );
644
+
645
+ const mainDirtyNavigateAction = {
646
+ title: "Yes, discard changes",
647
+ onClick: handleConfirmDirtyNavigation,
648
+ };
649
+
650
+ const secondaryDirtyNavigateAction = {
651
+ title: "Cancel",
652
+ onClick: handleCancelDirtyNavigation,
653
+ };
654
+
619
655
  return isLoading ? (
620
656
  <Loading />
621
657
  ) : (
@@ -642,6 +678,7 @@ const PageEditor = (props: IProps) => {
642
678
  tabs={tabsPreview}
643
679
  isDirty={isDirty}
644
680
  scheduledPublication={editorContent?.publicationScheduled}
681
+ isSaving={isSaving}
645
682
  >
646
683
  {selectedTab === "edit" ? (
647
684
  <>
@@ -693,6 +730,8 @@ const PageEditor = (props: IProps) => {
693
730
  setNotification={setNotification}
694
731
  isEditLive={isEditLive}
695
732
  isDisabled={isDeleted}
733
+ isDirty={isDirty}
734
+ onNavigateWithDirty={onNavigateWithDirty}
696
735
  />
697
736
  </S.Content>
698
737
  </>
@@ -766,6 +805,19 @@ const PageEditor = (props: IProps) => {
766
805
  restorePage: handleRestorePage,
767
806
  }}
768
807
  />
808
+ <Modal
809
+ isOpen={isDirtyNavigateOpen}
810
+ hide={handleCancelDirtyNavigation}
811
+ size="S"
812
+ title="Unsaved changes"
813
+ mainAction={mainDirtyNavigateAction}
814
+ secondaryAction={secondaryDirtyNavigateAction}
815
+ >
816
+ <S.ModalContent>
817
+ {dirtyNavigateModalText} If you exit without saving it, it will be lost. Do you want to discard
818
+ your changes?
819
+ </S.ModalContent>
820
+ </Modal>
769
821
  </MainWrapper>
770
822
  </>
771
823
  );
@@ -1,7 +1,8 @@
1
1
  import { BulkSelectionOptions } from "@ax/components";
2
+ import { itemLabel } from "@ax/constants";
2
3
  import type { IQueryValue } from "@ax/types";
3
4
 
4
- import { IBulkSelectedItems } from "src/hooks/bulk";
5
+ import type { IBulkSelectedItems } from "src/hooks/bulk";
5
6
 
6
7
  import TableHeader from "./TableHeader";
7
8
 
@@ -21,6 +22,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
21
22
  sortedListStatus,
22
23
  setHoverCheck,
23
24
  selectedItems,
25
+ isLoading,
24
26
  } = props;
25
27
 
26
28
  const bulkActions = [
@@ -38,6 +40,8 @@ const BulkHeader = (props: IProps): JSX.Element => {
38
40
  selectItems={selectItems}
39
41
  totalItems={totalItems}
40
42
  selectedItems={selectedItems}
43
+ isLoading={isLoading}
44
+ itemLabel={itemLabel.REDIRECT}
41
45
  />
42
46
  ) : (
43
47
  <TableHeader
@@ -70,6 +74,7 @@ interface IProps {
70
74
  sortedListStatus: { isAscending: boolean; sortedByDate: boolean };
71
75
  setHoverCheck: (state: boolean) => void;
72
76
  selectedItems: IBulkSelectedItems;
77
+ isLoading: boolean;
73
78
  }
74
79
 
75
80
  export default BulkHeader;
@@ -21,9 +21,10 @@ import BulkHeader from "./BulkHeader";
21
21
  import { useFilterQuery, useSortedListStatus } from "./hooks";
22
22
  import RedirectItem from "./RedirectItem";
23
23
  import RedirectPanel from "./RedirectPanel";
24
- import * as S from "./style";
25
24
  import { getSortedListStatus } from "./utils";
26
25
 
26
+ import * as S from "./style";
27
+
27
28
  const Redirects = (props: IProps): JSX.Element => {
28
29
  const {
29
30
  navItems,
@@ -45,6 +46,7 @@ const Redirects = (props: IProps): JSX.Element => {
45
46
  const firstPage = 1;
46
47
  const [page, setPage] = useState(1);
47
48
  const [isScrolling, setIsScrolling] = useState(false);
49
+ const [isBulkLoading, setIsBulkLoading] = useState(false);
48
50
  const [isOpenedSecond, setIsOpenedSecond] = useState(false);
49
51
  const { isOpen, toggleModal } = useModal();
50
52
  const { isOpen: isOpenDelete, toggleModal: toggleModalDelete } = useModal();
@@ -73,7 +75,7 @@ const Redirects = (props: IProps): JSX.Element => {
73
75
 
74
76
  const [formValues, setFormValues] = useState<IRedirect>(initState);
75
77
 
76
- const redIds = redirects && redirects.map((red: any) => red.id);
78
+ const redIds = redirects?.map((red: any) => red.id);
77
79
 
78
80
  const getParams = useCallback(() => {
79
81
  const params = {
@@ -127,11 +129,13 @@ const Redirects = (props: IProps): JSX.Element => {
127
129
  } = useBulkSelection(redIds);
128
130
 
129
131
  const bulkDelete = async () => {
132
+ setIsBulkLoading(true);
130
133
  const deleted = await deleteRedirect(selectedItems.all);
131
134
  if (deleted) {
132
135
  toggleToast();
133
136
  }
134
137
  toggleModalDelete();
138
+ setIsBulkLoading(false);
135
139
  };
136
140
 
137
141
  const unselectAllItems = () => resetBulkSelection();
@@ -174,6 +178,7 @@ const Redirects = (props: IProps): JSX.Element => {
174
178
  sortedListStatus={sortedListStatus}
175
179
  setHoverCheck={setHoverCheck}
176
180
  selectedItems={selectedItems}
181
+ isLoading={isBulkLoading}
177
182
  />
178
183
  );
179
184
 
@@ -1,4 +1,5 @@
1
1
  import { BulkSelectionOptions } from "@ax/components";
2
+ import { itemLabel } from "@ax/constants";
2
3
  import type { IBulkSelectedItems } from "@ax/hooks";
3
4
  import type { IQueryValue } from "@ax/types";
4
5
 
@@ -17,6 +18,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
17
18
  bulkActions,
18
19
  setHoverCheck,
19
20
  selectedItems,
21
+ isLoading,
20
22
  } = props;
21
23
 
22
24
  return showBulk ? (
@@ -26,6 +28,8 @@ const BulkHeader = (props: IProps): JSX.Element => {
26
28
  selectItems={selectItems}
27
29
  totalItems={totalItems}
28
30
  selectedItems={selectedItems}
31
+ isLoading={isLoading}
32
+ itemLabel={itemLabel.ADDON}
29
33
  />
30
34
  ) : (
31
35
  <TableHeader
@@ -52,6 +56,7 @@ interface IProps {
52
56
  bulkActions: { icon: string; text: string; action: () => void }[];
53
57
  setHoverCheck: (state: boolean) => void;
54
58
  selectedItems: IBulkSelectedItems;
59
+ isLoading: boolean;
55
60
  }
56
61
 
57
62
  export default BulkHeader;
@@ -45,6 +45,7 @@ const Integrations = (props: IIntegrationsProps): JSX.Element => {
45
45
  throw new Error(`ERROR: User reached Integrations with null site info`);
46
46
  }
47
47
 
48
+ const [isBulkLoading, setIsBulkLoading] = useState(false);
48
49
  const [isScrolling, setIsScrolling] = useState(false);
49
50
  const tableRef = useRef<HTMLDivElement>(null);
50
51
  const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery, isFiltered } = useFilterQuery();
@@ -125,6 +126,7 @@ const Integrations = (props: IIntegrationsProps): JSX.Element => {
125
126
  } = useBulkSelection(filteredIntegrationsIds);
126
127
 
127
128
  const bulkDelete = async () => {
129
+ setIsBulkLoading(true);
128
130
  const params = getParams();
129
131
  const deleted = await deleteIntegration(selectedItems.all, params);
130
132
  if (deleted) {
@@ -132,9 +134,11 @@ const Integrations = (props: IIntegrationsProps): JSX.Element => {
132
134
  }
133
135
  toggleModalDelete();
134
136
  resetBulkSelection();
137
+ setIsBulkLoading(false);
135
138
  };
136
139
 
137
140
  const bulkDeactivate = async () => {
141
+ setIsBulkLoading(true);
138
142
  const params = getParams();
139
143
  const changed = await changeIntegrationState(selectedItems.all, false, params);
140
144
  if (changed) {
@@ -142,6 +146,7 @@ const Integrations = (props: IIntegrationsProps): JSX.Element => {
142
146
  }
143
147
  toggleModalDeactivate();
144
148
  resetBulkSelection();
149
+ setIsBulkLoading(false);
145
150
  };
146
151
 
147
152
  const unselectAllItems = () => resetBulkSelection();
@@ -185,6 +190,7 @@ const Integrations = (props: IIntegrationsProps): JSX.Element => {
185
190
  bulkActions={bulkActions}
186
191
  setHoverCheck={setHoverCheck}
187
192
  selectedItems={selectedItems}
193
+ isLoading={isBulkLoading}
188
194
  />
189
195
  );
190
196
 
@@ -326,7 +332,7 @@ const mapStateToProps = (state: IRootState) => ({
326
332
  isLoading: state.app.isLoading,
327
333
  integrations: state.integrations.integrations,
328
334
  totalItems: state.integrations.totalItems,
329
- currentSite: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
335
+ currentSite: state.sites.currentSiteInfo?.id ?? null,
330
336
  });
331
337
 
332
338
  const mapDispatchToProps = {
@@ -1,4 +1,5 @@
1
1
  import { BulkSelectionOptions } from "@ax/components";
2
+ import { itemLabel } from "@ax/constants";
2
3
  import type { IBulkSelectedItems } from "@ax/hooks";
3
4
  import { usePermission } from "@ax/hooks";
4
5
  import type { IQueryValue } from "@ax/types";
@@ -22,6 +23,7 @@ const BulkHeader = (props: IBulkHeaderProps): JSX.Element => {
22
23
  filterValues,
23
24
  setHoverCheck,
24
25
  selectedItems,
26
+ isLoading,
25
27
  } = props;
26
28
 
27
29
  const allowedToPublishSite = usePermission("general.publishSite");
@@ -54,6 +56,8 @@ const BulkHeader = (props: IBulkHeaderProps): JSX.Element => {
54
56
  selectItems={selectItems}
55
57
  totalItems={totalItems}
56
58
  selectedItems={selectedItems}
59
+ isLoading={isLoading}
60
+ itemLabel={itemLabel.SITE}
57
61
  />
58
62
  </S.BulkWrapper>
59
63
  ) : (
@@ -85,6 +89,7 @@ export interface IBulkHeaderProps {
85
89
  filterValues: Record<string, IQueryValue[]>;
86
90
  setHoverCheck: (state: boolean) => void;
87
91
  selectedItems: IBulkSelectedItems;
92
+ isLoading: boolean;
88
93
  }
89
94
 
90
95
  export default BulkHeader;