@griddo/ax 11.15.7 → 11.15.8-rc.0

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 (88) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/Fields/Wysiwyg/Wysiwyg.atoms.test.tsx +409 -0
  3. package/src/__tests__/components/Fields/Wysiwyg/Wysiwyg.helpers.test.tsx +418 -0
  4. package/src/__tests__/components/Fields/Wysiwyg/Wysiwyg.test.tsx +776 -29
  5. package/src/__tests__/components/FieldsBehavior/FieldsBehavior.test.tsx +368 -11
  6. package/src/__tests__/modules/Users/UserList/UserList.test.tsx +6 -6
  7. package/src/components/Fields/IntegrationsField/IntegrationItem/atoms.tsx +14 -5
  8. package/src/components/Fields/IntegrationsField/IntegrationItem/index.tsx +1 -8
  9. package/src/components/Fields/UrlField/index.tsx +5 -4
  10. package/src/components/Fields/Wysiwyg/atoms.tsx +140 -0
  11. package/src/components/Fields/Wysiwyg/config.tsx +190 -9
  12. package/src/components/Fields/Wysiwyg/helpers.tsx +54 -1
  13. package/src/components/Fields/Wysiwyg/index.tsx +210 -61
  14. package/src/components/Fields/Wysiwyg/style.tsx +32 -1
  15. package/src/components/Fields/Wysiwyg/vendors.tsx +0 -1
  16. package/src/components/FieldsBehavior/index.tsx +66 -31
  17. package/src/components/Icon/components/Sync.js +16 -0
  18. package/src/components/Icon/svgs/Sync.svg +3 -0
  19. package/src/components/RestoreModal/index.tsx +1 -3
  20. package/src/components/ScheduleModal/index.tsx +3 -3
  21. package/src/components/TableList/index.tsx +5 -3
  22. package/src/containers/FileDrive/actions.tsx +5 -5
  23. package/src/containers/Forms/actions.tsx +40 -38
  24. package/src/containers/Gallery/actions.tsx +27 -27
  25. package/src/containers/Integrations/actions.tsx +2 -2
  26. package/src/containers/Navigation/Defaults/actions.tsx +1 -1
  27. package/src/containers/PageEditor/actions.tsx +3 -5
  28. package/src/containers/Redirects/actions.tsx +2 -2
  29. package/src/containers/Sites/actions.tsx +15 -8
  30. package/src/containers/StructuredData/actions.tsx +5 -5
  31. package/src/containers/Users/actions.tsx +2 -2
  32. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +36 -43
  33. package/src/modules/Categories/CategoriesList/CategoryPanel/index.tsx +15 -11
  34. package/src/modules/Categories/CategoriesList/atoms.tsx +42 -12
  35. package/src/modules/Categories/CategoriesList/index.tsx +8 -30
  36. package/src/modules/Content/PageItem/index.tsx +6 -5
  37. package/src/modules/Content/atoms.tsx +7 -3
  38. package/src/modules/Content/index.tsx +8 -16
  39. package/src/modules/FileDrive/FileModal/DetailPanel/index.tsx +14 -14
  40. package/src/modules/FileDrive/FolderItem/index.tsx +46 -54
  41. package/src/modules/FileDrive/GridItem/index.tsx +32 -37
  42. package/src/modules/FileDrive/ListItem/index.tsx +31 -37
  43. package/src/modules/FileDrive/atoms.tsx +59 -12
  44. package/src/modules/FileDrive/index.tsx +88 -98
  45. package/src/modules/Forms/FormCategoriesList/CategoryItem/index.tsx +42 -34
  46. package/src/modules/Forms/FormCategoriesList/atoms.tsx +17 -6
  47. package/src/modules/Forms/FormCategoriesList/index.tsx +30 -34
  48. package/src/modules/Forms/FormEditor/index.tsx +14 -6
  49. package/src/modules/Forms/FormList/FormItem/index.tsx +5 -1
  50. package/src/modules/Forms/FormList/index.tsx +15 -9
  51. package/src/modules/Forms/atoms.tsx +7 -4
  52. package/src/modules/GlobalEditor/atoms.tsx +77 -0
  53. package/src/modules/GlobalEditor/index.tsx +24 -7
  54. package/src/modules/MediaGallery/FolderItem/index.tsx +36 -52
  55. package/src/modules/MediaGallery/GridItem/index.tsx +27 -38
  56. package/src/modules/MediaGallery/ImageModal/DetailPanel/index.tsx +13 -13
  57. package/src/modules/MediaGallery/ImageModal/index.tsx +2 -16
  58. package/src/modules/MediaGallery/ListItem/index.tsx +25 -37
  59. package/src/modules/MediaGallery/atoms.tsx +84 -15
  60. package/src/modules/MediaGallery/index.tsx +62 -122
  61. package/src/modules/Navigation/Defaults/Item/atoms.tsx +2 -4
  62. package/src/modules/Navigation/Defaults/Item/index.tsx +80 -94
  63. package/src/modules/Navigation/Defaults/atoms.tsx +179 -7
  64. package/src/modules/Navigation/Defaults/index.tsx +51 -18
  65. package/src/modules/Navigation/Defaults/style.tsx +10 -7
  66. package/src/modules/Navigation/Menus/List/Table/Item/atoms.tsx +33 -19
  67. package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +18 -19
  68. package/src/modules/Navigation/Menus/List/Table/index.tsx +15 -13
  69. package/src/modules/Navigation/Menus/List/index.tsx +1 -1
  70. package/src/modules/PageEditor/atoms.tsx +14 -11
  71. package/src/modules/PageEditor/index.tsx +11 -16
  72. package/src/modules/Redirects/RedirectItem/index.tsx +37 -39
  73. package/src/modules/Redirects/atoms.tsx +58 -12
  74. package/src/modules/Redirects/index.tsx +30 -58
  75. package/src/modules/Settings/Integrations/IntegrationItem/CopyModal/index.tsx +1 -1
  76. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +57 -69
  77. package/src/modules/Settings/Integrations/atoms.tsx +72 -8
  78. package/src/modules/Settings/Integrations/index.tsx +34 -48
  79. package/src/modules/StructuredData/Form/index.tsx +25 -8
  80. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/atoms.tsx +26 -75
  81. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -33
  82. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +63 -41
  83. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/style.tsx +13 -8
  84. package/src/modules/StructuredData/StructuredDataList/index.tsx +11 -16
  85. package/src/modules/StructuredData/{StructuredDataList/atoms.tsx → atoms.tsx} +25 -28
  86. package/src/modules/Users/UserList/UserItem/index.tsx +18 -35
  87. package/src/modules/Users/UserList/atoms.tsx +61 -0
  88. package/src/modules/Users/UserList/index.tsx +12 -32
@@ -38,6 +38,7 @@ import type {
38
38
  IUserEditing,
39
39
  } from "@ax/types";
40
40
 
41
+ import { DeleteModal } from "./atoms";
41
42
  import Editor from "./Editor";
42
43
  import Preview from "./Preview";
43
44
 
@@ -83,6 +84,7 @@ const GlobalEditor = (props: IProps) => {
83
84
 
84
85
  const defaultTab = isAllowedTo.editContentPages ? "edit" : "view";
85
86
 
87
+ const [deleteAllVersions, setDeleteAllVersions] = useState(false);
86
88
  const [isReadOnly, setIsReadOnly] = useState(false);
87
89
  const [selectedTab, setSelectedTab] = useState(defaultTab);
88
90
  const [notification, setNotification] = useState<INotification | null>(null);
@@ -106,6 +108,7 @@ const GlobalEditor = (props: IProps) => {
106
108
  "headingsPreview",
107
109
  "keywordsPreview",
108
110
  "dirtyNavigate",
111
+ "delete",
109
112
  ]);
110
113
 
111
114
  const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
@@ -118,6 +121,7 @@ const GlobalEditor = (props: IProps) => {
118
121
  const isEditLive = isPublished && hasDraft;
119
122
  const isScheduled = props.pageStatus === pageStatus.SCHEDULED;
120
123
  const isDeleted = editorContent.deleted;
124
+ const isTranslated = pageLanguages.length > 1;
121
125
  const defaultContentTab = "content";
122
126
 
123
127
  const errorNotificationText =
@@ -185,13 +189,17 @@ const GlobalEditor = (props: IProps) => {
185
189
  const setRoute = (path: string) => props.setHistoryPush(path, true);
186
190
 
187
191
  const removePage = async () => {
188
- const { deletePage } = props;
192
+ const { deletePage, deleteBulk } = props;
189
193
  const path = "/data";
190
194
 
191
- const deleted = await deletePage();
192
-
193
- if (deleted) {
194
- setRoute(path);
195
+ try {
196
+ const allPageVersions = pageLanguages.map((lang: IPageLanguage) => lang.pageId);
197
+ const isDeleted = deleteAllVersions ? await deleteBulk(allPageVersions) : await deletePage();
198
+ if (isDeleted) {
199
+ setRoute(path);
200
+ }
201
+ } finally {
202
+ toggleModal("delete");
195
203
  }
196
204
  };
197
205
 
@@ -388,9 +396,9 @@ const GlobalEditor = (props: IProps) => {
388
396
 
389
397
  if (!isDraft && isAllowedTo.deletePages) {
390
398
  menuOptions.push({
391
- label: "Delete page",
399
+ label: "Delete",
392
400
  icon: "delete",
393
- action: removePage,
401
+ action: () => toggleModal("delete"),
394
402
  });
395
403
  }
396
404
 
@@ -759,6 +767,13 @@ const GlobalEditor = (props: IProps) => {
759
767
  </S.ModalContent>
760
768
  )}
761
769
  </Modal>
770
+ <DeleteModal
771
+ isOpen={isOpen("delete")}
772
+ toggleModal={() => toggleModal("delete")}
773
+ onDelete={removePage}
774
+ isDeleting={isSaving}
775
+ {...{ isTranslated, deleteAllVersions, setDeleteAllVersions }}
776
+ />
762
777
  <ScheduleModal
763
778
  isOpen={isOpen("schedule")}
764
779
  toggleModal={() => toggleModal("schedule")}
@@ -875,6 +890,7 @@ const mapDispatchToProps = {
875
890
  sendPagePing: pageEditorActions.sendPagePing,
876
891
  setStructuredDataFilter: structuredDataActions.setFilter,
877
892
  discardDraft: pageEditorActions.discardDraft,
893
+ deleteBulk: pageEditorActions.deleteBulk,
878
894
  getUserCurrentPermissions: usersActions.getUserCurrentPermissions,
879
895
  schedulePublication: pageEditorActions.schedulePublication,
880
896
  restorePage: pageEditorActions.restorePage,
@@ -902,6 +918,7 @@ interface IPageEditorDispatchProps {
902
918
  sendPagePing(pageID: number): Promise<boolean>;
903
919
  setStructuredDataFilter(filter: string | null): void;
904
920
  discardDraft(): Promise<void>;
921
+ deleteBulk(ids: number[]): Promise<boolean>;
905
922
  getUserCurrentPermissions(): Promise<void>;
906
923
  schedulePublication(date: string | null, isDraft: boolean): Promise<boolean>;
907
924
  restorePage(id: number | number[]): Promise<boolean>;
@@ -1,12 +1,13 @@
1
- import React, { useState } from "react";
1
+ import { useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { IActionMenuOption, IFolder, IGetFolderParams } from "@ax/types";
5
4
  import { Icon, Tooltip } from "@ax/components";
6
- import { useModal } from "@ax/hooks";
7
5
  import { galleryActions } from "@ax/containers/Gallery";
8
- import { DeleteFolderModal, MoveItemModal, RenameFolderModal } from "../atoms";
9
6
  import { trimText } from "@ax/helpers";
7
+ import { useModals } from "@ax/hooks";
8
+ import type { IActionMenuOption, IFolder, IGetFolderParams, IRootState } from "@ax/types";
9
+
10
+ import { DeleteFolderModal, MoveItemModal, RenameFolderModal } from "../atoms";
10
11
 
11
12
  import * as S from "./style";
12
13
 
@@ -16,6 +17,7 @@ const FolderItem = (props: IProps) => {
16
17
  isAllowedToDelete,
17
18
  isAllowedToEdit,
18
19
  isAllowedToMove,
20
+ isSaving,
19
21
  onClick,
20
22
  onDelete,
21
23
  toggleToast,
@@ -26,9 +28,7 @@ const FolderItem = (props: IProps) => {
26
28
 
27
29
  const initState = { folderName, parentId };
28
30
  const [folderForm, setFolderForm] = useState(initState);
29
- const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
30
- const { isOpen: isRenameOpen, toggleModal: toggleRenameModal } = useModal();
31
- const { isOpen: isMoveOpen, toggleModal: toggleMoveModal } = useModal();
31
+ const { isOpen, toggleModal } = useModals(["delete", "rename", "move"]);
32
32
 
33
33
  const setName = (name: string) => setFolderForm({ ...folderForm, folderName: name });
34
34
  const setParent = (folderID: number) => setFolderForm({ ...folderForm, parentId: folderID });
@@ -40,7 +40,7 @@ const FolderItem = (props: IProps) => {
40
40
  {
41
41
  label: "Rename",
42
42
  icon: "edit",
43
- action: () => toggleRenameModal(),
43
+ action: () => toggleModal("rename"),
44
44
  },
45
45
  ];
46
46
 
@@ -50,7 +50,7 @@ const FolderItem = (props: IProps) => {
50
50
  {
51
51
  label: "Move to...",
52
52
  icon: "change",
53
- action: () => toggleMoveModal(),
53
+ action: () => toggleModal("move"),
54
54
  },
55
55
  ];
56
56
  }
@@ -62,16 +62,16 @@ const FolderItem = (props: IProps) => {
62
62
  {
63
63
  label: "Delete",
64
64
  icon: "delete",
65
- action: () => toggleDeleteModal(),
65
+ action: () => toggleModal("delete"),
66
66
  },
67
67
  ];
68
68
  }
69
69
 
70
70
  const handleClick = () => onClick(id);
71
71
 
72
- const handleDeleteFolder = () => {
73
- onDelete(folder);
74
- isDeleteOpen && toggleDeleteModal();
72
+ const handleDeleteFolder = async () => {
73
+ await onDelete(folder);
74
+ isOpen("delete") && toggleModal("delete");
75
75
  };
76
76
 
77
77
  const handleUpdateFolder = async (toast = false) => {
@@ -82,39 +82,17 @@ const FolderItem = (props: IProps) => {
82
82
  params,
83
83
  );
84
84
  if (updated) {
85
- isRenameOpen && toggleRenameModal();
85
+ isOpen("rename") && toggleModal("rename");
86
86
  toast && toggleToast("1 Folder moved to another folder");
87
87
  }
88
88
  };
89
89
 
90
90
  const handleModalClose = () => {
91
91
  setFolderForm(initState);
92
- isMoveOpen && toggleMoveModal();
93
- isRenameOpen && toggleRenameModal();
92
+ isOpen("move") && toggleModal("move");
93
+ isOpen("rename") && toggleModal("rename");
94
94
  };
95
95
 
96
- const mainDeleteModalAction = {
97
- title: "Delete Folder",
98
- onClick: () => handleDeleteFolder(),
99
- };
100
-
101
- const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
102
-
103
- const mainRenameModalAction = {
104
- title: "Rename Folder",
105
- onClick: () => handleUpdateFolder(),
106
- disabled: folderForm.folderName.trim().length === 0,
107
- };
108
-
109
- const secondaryRenameModalAction = { title: "Cancel", onClick: handleModalClose };
110
-
111
- const mainMoveModalAction = {
112
- title: "Move",
113
- onClick: () => handleUpdateFolder(true),
114
- };
115
-
116
- const secondaryMoveModalAction = { title: "Cancel", onClick: handleModalClose };
117
-
118
96
  const FolderTitle = () =>
119
97
  folderName.length > 15 ? (
120
98
  <Tooltip content={folderName}>
@@ -133,35 +111,36 @@ const FolderItem = (props: IProps) => {
133
111
  <FolderTitle />
134
112
  <S.StyledActionMenu icon="more" options={menuOptions} tooltip="File actions" />
135
113
  </S.Wrapper>
136
- {isDeleteOpen && (
114
+ {isOpen("delete") && (
137
115
  <DeleteFolderModal
138
- isOpen={isDeleteOpen}
139
- toggleModal={toggleDeleteModal}
140
- mainModalAction={mainDeleteModalAction}
141
- secondaryModalAction={secondaryDeleteModalAction}
116
+ isOpen={isOpen("delete")}
117
+ toggleModal={() => toggleModal("delete")}
142
118
  title={folderName}
119
+ onDelete={handleDeleteFolder}
120
+ isDeleting={isSaving}
143
121
  />
144
122
  )}
145
- {isRenameOpen && (
123
+ {isOpen("rename") && (
146
124
  <RenameFolderModal
147
- isOpen={isRenameOpen}
125
+ isOpen={isOpen("rename")}
148
126
  toggleModal={handleModalClose}
149
- mainModalAction={mainRenameModalAction}
150
- secondaryModalAction={secondaryRenameModalAction}
151
127
  value={folderForm.folderName}
152
128
  setValue={setName}
129
+ onUpdateFolder={() => handleUpdateFolder()}
130
+ isNameEmpty={folderForm.folderName.trim().length === 0}
131
+ isUpdating={isSaving}
153
132
  />
154
133
  )}
155
- {isMoveOpen && (
134
+ {isOpen("move") && (
156
135
  <MoveItemModal
157
- isOpen={isMoveOpen}
136
+ isOpen={isOpen("move")}
158
137
  toggleModal={handleModalClose}
159
- mainModalAction={mainMoveModalAction}
160
- secondaryModalAction={secondaryMoveModalAction}
161
138
  folder={folderForm.parentId}
162
139
  setFolder={setParent}
163
140
  hidden={[folder.id]}
164
141
  hideRoot={!parentId}
142
+ onMove={() => handleUpdateFolder(true)}
143
+ isMoving={isSaving}
165
144
  />
166
145
  )}
167
146
  </>
@@ -173,6 +152,7 @@ interface IProps {
173
152
  isAllowedToEdit: boolean;
174
153
  isAllowedToDelete: boolean;
175
154
  isAllowedToMove: boolean;
155
+ isSaving: boolean;
176
156
  onClick(folderID: number): void;
177
157
  onDelete(folder: IFolder): void;
178
158
  toggleToast(state: string): void;
@@ -184,8 +164,12 @@ interface IProps {
184
164
  ): Promise<boolean>;
185
165
  }
186
166
 
167
+ const mapStateToProps = (state: IRootState) => ({
168
+ isSaving: state.app.isSaving,
169
+ });
170
+
187
171
  const mapDispatchToProps = {
188
172
  updateFolder: galleryActions.updateFolder,
189
173
  };
190
174
 
191
- export default connect(null, mapDispatchToProps)(FolderItem);
175
+ export default connect(mapStateToProps, mapDispatchToProps)(FolderItem);
@@ -1,7 +1,9 @@
1
- import React, { useState } from "react";
1
+ import type React from "react";
2
+ import { useState } from "react";
3
+
2
4
  import { Icon } from "@ax/components";
3
- import { IActionMenuOption, ICheck, IImage } from "@ax/types";
4
- import { useModal } from "@ax/hooks";
5
+ import { useModals } from "@ax/hooks";
6
+ import type { IActionMenuOption, ICheck, IImage } from "@ax/types";
5
7
 
6
8
  import { DeleteFileModal, MoveItemModal } from "../atoms";
7
9
 
@@ -15,6 +17,7 @@ const GridItem = (props: IProps) => {
15
17
  isAllowedToDelete,
16
18
  isAllowedToEdit,
17
19
  isAllowedToMove,
20
+ isSaving,
18
21
  onChange,
19
22
  onClick,
20
23
  onDelete,
@@ -24,8 +27,7 @@ const GridItem = (props: IProps) => {
24
27
  const { id, name, thumb, alt, folderId } = image;
25
28
 
26
29
  const [selectedFolder, setSelectedFolder] = useState<number | null>(currentFolderID || null);
27
- const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
28
- const { isOpen: isMoveOpen, toggleModal: toggleMoveModal } = useModal();
30
+ const { isOpen, toggleModal } = useModals(["delete", "move"]);
29
31
 
30
32
  const handleCheckClick = (isChecked: boolean) => (e: React.MouseEvent<HTMLDivElement>) => {
31
33
  e.stopPropagation();
@@ -51,7 +53,7 @@ const GridItem = (props: IProps) => {
51
53
  {
52
54
  label: "Move to...",
53
55
  icon: "change",
54
- action: () => toggleMoveModal(),
56
+ action: () => toggleModal("move"),
55
57
  },
56
58
  ];
57
59
  }
@@ -63,70 +65,56 @@ const GridItem = (props: IProps) => {
63
65
  {
64
66
  label: "Delete",
65
67
  icon: "delete",
66
- action: () => toggleDeleteModal(),
68
+ action: () => toggleModal("delete"),
67
69
  },
68
70
  ];
69
71
  }
70
72
 
71
- const handleDeleteFile = () => {
72
- onDelete(id);
73
- isDeleteOpen && toggleDeleteModal();
73
+ const handleDeleteFile = async () => {
74
+ await onDelete(id);
75
+ toggleModal("delete");
74
76
  };
75
77
 
76
- const handleMoveFile = () => {
78
+ const handleMoveFile = async () => {
77
79
  if (folderId !== selectedFolder) {
78
- onMove(id, selectedFolder || "root");
79
- isMoveOpen && toggleMoveModal();
80
+ await onMove(id, selectedFolder || "root");
81
+ isOpen("move") && toggleModal("move");
80
82
  setSelectedFolder(0);
81
83
  }
82
84
  };
83
85
 
84
86
  const handleModalClose = () => {
85
87
  setSelectedFolder(0);
86
- isMoveOpen && toggleMoveModal();
87
- };
88
-
89
- const mainDeleteModalAction = {
90
- title: "Delete image",
91
- onClick: () => handleDeleteFile(),
88
+ toggleModal("move");
92
89
  };
93
90
 
94
- const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
95
-
96
- const mainMoveModalAction = {
97
- title: "Move",
98
- onClick: () => handleMoveFile(),
99
- };
100
-
101
- const secondaryMoveModalAction = { title: "Cancel", onClick: handleModalClose };
102
-
103
91
  return (
104
92
  <>
105
93
  <S.ImageWrapper data-testid="image-item" onClick={handleClick} selected={isSelected}>
106
94
  <img src={thumb} alt={alt} />
107
- <S.IconWrapper onClick={handleCheckClick(isSelected ? false : true)}>
95
+ <S.IconWrapper onClick={handleCheckClick(!isSelected)}>
108
96
  <Icon name={isSelected ? "successSolid" : "emptyCheck"} size="24" />
109
97
  </S.IconWrapper>
110
98
  <S.StyledActionMenu icon="more" options={menuOptions} />
111
99
  </S.ImageWrapper>
112
- {isDeleteOpen && (
100
+ {isOpen("delete") && (
113
101
  <DeleteFileModal
114
- isOpen={isDeleteOpen}
115
- toggleModal={toggleDeleteModal}
116
- mainModalAction={mainDeleteModalAction}
117
- secondaryModalAction={secondaryDeleteModalAction}
102
+ isOpen={isOpen("delete")}
103
+ toggleModal={() => toggleModal("delete")}
118
104
  title={name}
105
+ onDelete={handleDeleteFile}
106
+ isDeleting={isSaving}
119
107
  />
120
108
  )}
121
- {isMoveOpen && (
109
+ {isOpen("move") && (
122
110
  <MoveItemModal
123
- isOpen={isMoveOpen}
111
+ isOpen={isOpen("move")}
124
112
  toggleModal={handleModalClose}
125
- mainModalAction={mainMoveModalAction}
126
- secondaryModalAction={secondaryMoveModalAction}
127
113
  folder={selectedFolder}
128
114
  setFolder={setSelectedFolder}
115
+ onMove={handleMoveFile}
129
116
  hideRoot={!folderId}
117
+ isMoving={isSaving}
130
118
  />
131
119
  )}
132
120
  </>
@@ -140,6 +128,7 @@ interface IProps {
140
128
  isAllowedToEdit: boolean;
141
129
  isAllowedToDelete: boolean;
142
130
  isAllowedToMove: boolean;
131
+ isSaving: boolean;
143
132
  onChange(e: ICheck): void;
144
133
  onClick(image: IImage): void;
145
134
  onDelete(imageID: number): void;
@@ -9,9 +9,10 @@ import { LOCALE } from "@ax/locales";
9
9
  import type { IGetFolderParams, IImage, IRootState } from "@ax/types";
10
10
 
11
11
  import { DeleteFileModal } from "../../atoms";
12
- import * as S from "./style";
13
12
  import UsageContent from "./UsageContent";
14
13
 
14
+ import * as S from "./style";
15
+
15
16
  const DetailPanel = (props: IProps) => {
16
17
  const {
17
18
  image,
@@ -30,6 +31,7 @@ const DetailPanel = (props: IProps) => {
30
31
  const { name, size, published, title, alt, tags, width, height } = image;
31
32
 
32
33
  const [selectedTab, setSelectedTab] = useState("details");
34
+ const [isDeleting, setIsDeleting] = useState(false);
33
35
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
34
36
 
35
37
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
@@ -59,9 +61,14 @@ const DetailPanel = (props: IProps) => {
59
61
 
60
62
  const handleCopyUrl = () => navigator.clipboard.writeText(image.url);
61
63
 
62
- const handleDeleteFile = () => {
63
- onDelete(image.id);
64
- isDeleteOpen && toggleDeleteModal();
64
+ const handleDeleteFile = async () => {
65
+ setIsDeleting(true);
66
+ try {
67
+ await onDelete(image.id);
68
+ } finally {
69
+ setIsDeleting(false);
70
+ toggleDeleteModal();
71
+ }
65
72
  };
66
73
 
67
74
  const handleSelectedTab = (tab: "details" | "in use") => {
@@ -70,13 +77,6 @@ const DetailPanel = (props: IProps) => {
70
77
  }
71
78
  };
72
79
 
73
- const mainNewModalAction = {
74
- title: "Delete image",
75
- onClick: () => handleDeleteFile(),
76
- };
77
-
78
- const secondaryNewModalAction = { title: "Cancel", onClick: toggleDeleteModal };
79
-
80
80
  const tabs = ["details", "in use"];
81
81
 
82
82
  return (
@@ -168,9 +168,9 @@ const DetailPanel = (props: IProps) => {
168
168
  <DeleteFileModal
169
169
  isOpen={isDeleteOpen}
170
170
  toggleModal={toggleDeleteModal}
171
- mainModalAction={mainNewModalAction}
172
- secondaryModalAction={secondaryNewModalAction}
173
171
  title={image.name}
172
+ onDelete={handleDeleteFile}
173
+ isDeleting={isDeleting}
174
174
  isChild={true}
175
175
  />
176
176
  )}
@@ -69,19 +69,12 @@ const ImageModal = (props: IProps) => {
69
69
  message: "1 image replaced",
70
70
  };
71
71
 
72
- const handleMainAction = () => {
72
+ const handleDiscard = () => {
73
73
  toggleSaveModal();
74
74
  resetDirty();
75
75
  changeFile(isArrowPrev);
76
76
  };
77
77
 
78
- const mainModalAction = {
79
- title: "Yes, discard changes",
80
- onClick: () => handleMainAction(),
81
- };
82
-
83
- const secondaryModalAction = { title: "Cancel", onClick: toggleSaveModal };
84
-
85
78
  return (
86
79
  <>
87
80
  <S.Wrapper>
@@ -139,14 +132,7 @@ const ImageModal = (props: IProps) => {
139
132
  />
140
133
  </S.Wrapper>
141
134
  {isVisible && <Toast {...toastProps} />}
142
- {isSaveOpen && (
143
- <NotSavedModal
144
- isOpen={isSaveOpen}
145
- toggleModal={toggleSaveModal}
146
- mainModalAction={mainModalAction}
147
- secondaryModalAction={secondaryModalAction}
148
- />
149
- )}
135
+ {isSaveOpen && <NotSavedModal isOpen={isSaveOpen} toggleModal={toggleSaveModal} onDiscard={handleDiscard} />}
150
136
  </>
151
137
  );
152
138
  };
@@ -1,8 +1,9 @@
1
- import React, { useState } from "react";
1
+ import { useState } from "react";
2
+
2
3
  import { CheckField, ElementsTooltip, Tooltip } from "@ax/components";
3
- import { IActionMenuOption, ICheck, IImage } from "@ax/types";
4
4
  import { formatBytes, getFileExtension, trimText } from "@ax/helpers";
5
- import { useModal } from "@ax/hooks";
5
+ import { useModals } from "@ax/hooks";
6
+ import type { IActionMenuOption, ICheck, IImage } from "@ax/types";
6
7
 
7
8
  import { DeleteFileModal, MoveItemModal } from "../atoms";
8
9
 
@@ -17,6 +18,7 @@ const ListItem = (props: IProps) => {
17
18
  isAllowedToEdit,
18
19
  isAllowedToMove,
19
20
  hoverCheck,
21
+ isSaving,
20
22
  onChange,
21
23
  onClick,
22
24
  onDelete,
@@ -26,8 +28,7 @@ const ListItem = (props: IProps) => {
26
28
  const imageType = getFileExtension(image.name);
27
29
 
28
30
  const [selectedFolder, setSelectedFolder] = useState<number | null>(currentFolderID || null);
29
- const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
30
- const { isOpen: isMoveOpen, toggleModal: toggleMoveModal } = useModal();
31
+ const { isOpen, toggleModal } = useModals(["delete", "move"]);
31
32
 
32
33
  const handleChange = (value: ICheck) => onChange(value);
33
34
  const handleClick = () => onClick(image);
@@ -49,7 +50,7 @@ const ListItem = (props: IProps) => {
49
50
  {
50
51
  label: "Move to...",
51
52
  icon: "change",
52
- action: () => toggleMoveModal(),
53
+ action: () => toggleModal("move"),
53
54
  },
54
55
  ];
55
56
  }
@@ -61,43 +62,29 @@ const ListItem = (props: IProps) => {
61
62
  {
62
63
  label: "Delete",
63
64
  icon: "delete",
64
- action: () => toggleDeleteModal(),
65
+ action: () => toggleModal("delete"),
65
66
  },
66
67
  ];
67
68
  }
68
69
 
69
- const handleDeleteFile = () => {
70
- onDelete(id);
71
- isDeleteOpen && toggleDeleteModal();
70
+ const handleDeleteFile = async () => {
71
+ await onDelete(id);
72
+ toggleModal("delete");
72
73
  };
73
74
 
74
- const handleMoveFile = () => {
75
+ const handleMoveFile = async () => {
75
76
  if (folderId !== selectedFolder) {
76
- onMove(id, selectedFolder || "root");
77
- isMoveOpen && toggleMoveModal();
77
+ await onMove(id, selectedFolder || "root");
78
+ isOpen("move") && toggleModal("move");
78
79
  setSelectedFolder(0);
79
80
  }
80
81
  };
81
82
 
82
83
  const handleModalClose = () => {
83
84
  setSelectedFolder(0);
84
- isMoveOpen && toggleMoveModal();
85
- };
86
-
87
- const mainDeleteModalAction = {
88
- title: "Delete image",
89
- onClick: () => handleDeleteFile(),
85
+ toggleModal("move");
90
86
  };
91
87
 
92
- const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
93
-
94
- const mainMoveModalAction = {
95
- title: "Move",
96
- onClick: () => handleMoveFile(),
97
- };
98
-
99
- const secondaryMoveModalAction = { title: "Cancel", onClick: handleModalClose };
100
-
101
88
  const FileTitle = () => (name.length > 50 ? <Tooltip content={name}>{trimText(name, 50)}</Tooltip> : <>{name}</>);
102
89
 
103
90
  return (
@@ -140,24 +127,24 @@ const ListItem = (props: IProps) => {
140
127
  <S.StyledActionMenu icon="more" options={menuOptions} tooltip="File actions" />
141
128
  </S.ActionsCell>
142
129
  </S.ItemRow>
143
- {isDeleteOpen && (
130
+ {isOpen("delete") && (
144
131
  <DeleteFileModal
145
- isOpen={isDeleteOpen}
146
- toggleModal={toggleDeleteModal}
147
- mainModalAction={mainDeleteModalAction}
148
- secondaryModalAction={secondaryDeleteModalAction}
132
+ isOpen={isOpen("delete")}
133
+ toggleModal={() => toggleModal("delete")}
149
134
  title={name}
135
+ onDelete={handleDeleteFile}
136
+ isDeleting={isSaving}
150
137
  />
151
138
  )}
152
- {isMoveOpen && (
139
+ {isOpen("move") && (
153
140
  <MoveItemModal
154
- isOpen={isMoveOpen}
141
+ isOpen={isOpen("move")}
155
142
  toggleModal={handleModalClose}
156
- mainModalAction={mainMoveModalAction}
157
- secondaryModalAction={secondaryMoveModalAction}
158
143
  folder={selectedFolder}
159
144
  setFolder={setSelectedFolder}
145
+ onMove={handleMoveFile}
160
146
  hideRoot={!folderId}
147
+ isMoving={isSaving}
161
148
  />
162
149
  )}
163
150
  </>
@@ -176,6 +163,7 @@ interface IProps {
176
163
  onDelete(fileID: number): void;
177
164
  onMove(fileID: number, folderID: number | "root"): void;
178
165
  hoverCheck: boolean;
166
+ isSaving: boolean;
179
167
  }
180
168
 
181
169
  export default ListItem;