@griddo/ax 10.4.37 → 10.5.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 (38) hide show
  1. package/package.json +2 -2
  2. package/src/api/checkgroups.tsx +1 -1
  3. package/src/api/structuredData.tsx +109 -18
  4. package/src/components/Fields/AsyncCheckGroup/index.tsx +56 -19
  5. package/src/components/Fields/AsyncCheckGroup/style.tsx +21 -4
  6. package/src/components/Fields/CheckField/style.tsx +1 -1
  7. package/src/components/Fields/NoteField/style.tsx +1 -1
  8. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +58 -20
  9. package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/style.tsx +20 -1
  10. package/src/components/Fields/ReferenceField/Context/index.tsx +9 -9
  11. package/src/components/LanguageMenu/index.tsx +1 -1
  12. package/src/components/Tag/index.tsx +3 -2
  13. package/src/components/Tag/style.tsx +5 -4
  14. package/src/containers/StructuredData/actions.tsx +169 -82
  15. package/src/containers/StructuredData/constants.tsx +2 -2
  16. package/src/containers/StructuredData/interfaces.tsx +16 -9
  17. package/src/containers/StructuredData/reducer.tsx +11 -7
  18. package/src/containers/StructuredData/utils.tsx +2 -2
  19. package/src/global.d.ts +0 -1
  20. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/index.tsx +10 -3
  21. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/style.tsx +12 -6
  22. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +21 -2
  23. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +131 -63
  24. package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +67 -7
  25. package/src/modules/Categories/CategoriesList/CategoryNav/index.tsx +2 -8
  26. package/src/modules/Categories/CategoriesList/CategoryPanel/Form/index.tsx +88 -33
  27. package/src/modules/Categories/CategoriesList/CategoryPanel/index.tsx +130 -56
  28. package/src/modules/Categories/CategoriesList/CategoryPanel/style.tsx +1 -1
  29. package/src/modules/Categories/CategoriesList/atoms.tsx +46 -19
  30. package/src/modules/Categories/CategoriesList/helpers.tsx +116 -0
  31. package/src/modules/Categories/CategoriesList/hooks.tsx +61 -0
  32. package/src/modules/Categories/CategoriesList/index.tsx +283 -97
  33. package/src/modules/Categories/CategoriesList/style.tsx +54 -2
  34. package/src/modules/Categories/CategoriesList/utils.tsx +34 -14
  35. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +1 -1
  36. package/src/modules/Users/UserList/hooks.tsx +5 -4
  37. package/src/types/index.tsx +64 -3
  38. package/tsconfig.paths.json +0 -1
@@ -0,0 +1,61 @@
1
+ import { useState } from "react";
2
+ import { IQueryValue } from "@ax/types";
3
+
4
+ const useFilterQuery = () => {
5
+ const initialQueryValues: Record<string, IQueryValue[]> = {
6
+ translated: [{ value: "all", label: "All" }],
7
+ };
8
+
9
+ const [query, setQuery] = useState(initialQueryValues);
10
+ const [currentFilterQuery, setCurrentFilterQuery] = useState("");
11
+
12
+ const setFilterQuery = (filterValues: Record<string, IQueryValue[]>) => {
13
+ const { translated } = filterValues;
14
+ let filterQuery = "";
15
+
16
+ const currentQuery = (pointer: string, values: IQueryValue[]): string => {
17
+ const stringValues =
18
+ Array.isArray(values) && values.length
19
+ ? values.map((value) => (value.value !== "all" ? value.value : "")).join(",")
20
+ : "";
21
+
22
+ return !stringValues.length
23
+ ? filterQuery
24
+ : filterQuery.length
25
+ ? filterQuery.concat(`&${pointer}=${stringValues}`)
26
+ : `&${pointer}=${stringValues}`;
27
+ };
28
+
29
+ const isNotInitialValue = (pointer: string) => {
30
+ return filterValues[pointer] && initialQueryValues[pointer] !== filterValues[pointer];
31
+ };
32
+
33
+ if (isNotInitialValue("translated")) filterQuery = currentQuery("translated", translated);
34
+
35
+ setCurrentFilterQuery(filterQuery);
36
+ };
37
+
38
+ const setFiltersSelection = (pointer: string, filter: IQueryValue[]) => {
39
+ const { translated } = query;
40
+ const filterValues = {
41
+ translated: pointer === "translated" ? filter : translated,
42
+ };
43
+
44
+ setQuery(filterValues);
45
+ setFilterQuery(filterValues);
46
+ };
47
+
48
+ const resetFilterQuery = () => {
49
+ setQuery(initialQueryValues);
50
+ setCurrentFilterQuery("");
51
+ };
52
+
53
+ return {
54
+ setFiltersSelection,
55
+ resetFilterQuery,
56
+ filterValues: query,
57
+ filterQuery: currentFilterQuery,
58
+ };
59
+ };
60
+
61
+ export { useFilterQuery };
@@ -1,18 +1,56 @@
1
1
  import React, { useEffect, useCallback, useState, useRef, useLayoutEffect } from "react";
2
2
  import { connect } from "react-redux";
3
+ import Tree, {
4
+ mutateTree,
5
+ moveItemOnTree,
6
+ RenderItemParams,
7
+ TreeItem,
8
+ TreeData,
9
+ ItemId,
10
+ TreeSourcePosition,
11
+ TreeDestinationPosition,
12
+ } from "@atlaskit/tree";
3
13
 
4
14
  import { structuredDataActions } from "@ax/containers/StructuredData";
5
15
  import { appActions } from "@ax/containers/App";
6
- import { IRootState, IStructuredData, IStructuredDataContent, IGetStructuredDataParams, IDataPack } from "@ax/types";
16
+ import {
17
+ IRootState,
18
+ IStructuredData,
19
+ IGetStructuredDataParams,
20
+ IDataPack,
21
+ IStructuredDataCategory,
22
+ ICategoryGroup,
23
+ IOrderCategoryParams,
24
+ IQueryValue,
25
+ } from "@ax/types";
7
26
  import { useBulkSelection, useModal, usePermission, useToast } from "@ax/hooks";
8
- import { MainWrapper, TableList, ErrorToast, Toast, EmptyState } from "@ax/components";
9
-
10
- import { DeleteModal } from "./atoms";
11
- import { getAllLangCategoriesIds } from "./utils";
27
+ import {
28
+ MainWrapper,
29
+ TableList,
30
+ ErrorToast,
31
+ Toast,
32
+ EmptyState,
33
+ Icon,
34
+ SearchTagsBar,
35
+ FilterTagsBar,
36
+ } from "@ax/components";
37
+ import { sortBy } from "@ax/helpers";
38
+
39
+ import { DeleteGroupModal, DeleteModal } from "./atoms";
40
+ import { filterCategoriesAndGroups, getAllLangCategoriesIds } from "./utils";
12
41
  import CategoryItem from "./CategoryItem";
13
42
  import CategoryPanel from "./CategoryPanel";
14
43
  import CategoryNav from "./CategoryNav";
15
44
  import BulkHeader from "./BulkHeader";
45
+ import {
46
+ findChild,
47
+ findChildByIndex,
48
+ formatItem,
49
+ getElementsFromTree,
50
+ getIDsFromTree,
51
+ normalizeItems,
52
+ } from "./helpers";
53
+ import { useFilterQuery } from "./hooks";
16
54
 
17
55
  import * as S from "./style";
18
56
 
@@ -22,7 +60,6 @@ const CategoriesList = (props: IProps): JSX.Element => {
22
60
  currentStructuredData,
23
61
  currentDataContent,
24
62
  getStructuredDataContents,
25
- resetCategoryValues,
26
63
  lang,
27
64
  siteLanguages,
28
65
  globalLangs,
@@ -32,19 +69,22 @@ const CategoriesList = (props: IProps): JSX.Element => {
32
69
  setSelectedCategory,
33
70
  activatedDataPacks,
34
71
  deleteDataContent,
35
- restoreDataContent,
36
72
  setHistoryPush,
37
73
  resetCurrentData,
74
+ deleteCategoryGroup,
75
+ orderCategory,
38
76
  } = props;
39
77
 
40
- const itemsPerPage = 50;
41
- const [page, setPage] = useState(1);
78
+ const [tree, setTree] = useState<TreeData>({ rootId: "root", items: {} });
42
79
  const [isScrolling, setIsScrolling] = useState(false);
43
- const [deletedItem, setDeletedItem] = useState<number | number[] | null>(null);
44
- const [deleteAllVersions, setDeleteAllVersions] = useState(false);
45
- const { isVisible, toggleToast, setIsVisible } = useToast();
80
+ const [deleteGroupCategories, setDeleteGroupCategories] = useState(false);
81
+ const [itemDragging, setItemDragging] = useState<ItemId | null>(null);
82
+ const [searchQuery, setSearchQuery] = useState<string>("");
83
+ const { isVisible, toggleToast, setIsVisible, state: toastState } = useToast();
46
84
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
85
+ const { isOpen: isGroupOpen, toggleModal: toggleGroupModal } = useModal();
47
86
  const tableRef = useRef<HTMLDivElement>(null);
87
+ const { setFiltersSelection, resetFilterQuery, filterValues, filterQuery } = useFilterQuery();
48
88
 
49
89
  const allowedToCreateSiteCategory = usePermission("categories.createSiteTaxonomies");
50
90
  const allowedToCreateGlobalCategory = usePermission("global.globalData.createTaxonomies");
@@ -54,9 +94,9 @@ const CategoriesList = (props: IProps): JSX.Element => {
54
94
  const allowedToCreateTaxonomy = currentSiteID ? allowedToCreateSiteCategory : allowedToCreateGlobalCategory;
55
95
 
56
96
  const scope = currentSiteID ? "site" : "global";
57
- const currentCategories = categories[scope];
97
+ const currentCategories = categories[scope].sort(sortBy("title", false));
58
98
 
59
- const catIds = currentDataContent && currentDataContent.map((cat: any) => cat.id);
99
+ const catIds = getIDsFromTree(currentDataContent);
60
100
 
61
101
  const {
62
102
  resetBulkSelection,
@@ -68,70 +108,81 @@ const CategoriesList = (props: IProps): JSX.Element => {
68
108
  selectAllItems,
69
109
  } = useBulkSelection(catIds);
70
110
 
71
- const getParams = useCallback(() => {
111
+ const getParams = useCallback((): IGetStructuredDataParams => {
72
112
  return {
73
- page,
74
- itemsPerPage,
75
- pagination: true,
113
+ page: 1,
114
+ itemsPerPage: 50,
115
+ pagination: false,
76
116
  deleted: false,
77
117
  include_draft: false,
118
+ groupingCategories: true,
119
+ query: searchQuery,
120
+ filterQuery,
78
121
  };
79
- }, [page]);
122
+ }, [searchQuery, filterQuery]);
80
123
 
81
124
  const getContents = useCallback(
82
125
  async (id: string) => {
83
- const params: any = getParams();
126
+ const params = getParams();
84
127
  params.dataID = id;
85
- setSelectedCategory(id, scope);
86
128
  await getStructuredDataContents(params, currentSiteID);
87
129
  },
88
- [getStructuredDataContents, currentSiteID, getParams, setSelectedCategory, scope]
130
+ [getStructuredDataContents, currentSiteID, getParams]
89
131
  );
90
132
 
91
- useEffect(() => {
92
- currentStructuredData && getContents(currentStructuredData.id);
93
- // eslint-disable-next-line react-hooks/exhaustive-deps
94
- }, [lang, currentStructuredData, page]);
95
-
96
133
  useLayoutEffect(() => {
134
+ if (currentCategories.length) {
135
+ setSelectedCategory(currentCategories[0].id, scope);
136
+ }
97
137
  return () => {
98
138
  resetCurrentData();
99
139
  };
140
+ // eslint-disable-next-line react-hooks/exhaustive-deps
100
141
  }, []);
101
142
 
143
+ useEffect(() => {
144
+ currentStructuredData?.taxonomy && getContents(currentStructuredData.id);
145
+ // eslint-disable-next-line react-hooks/exhaustive-deps
146
+ }, [lang, currentStructuredData, searchQuery, filterValues]);
147
+
148
+ useEffect(() => {
149
+ setTree(normalizeItems(currentDataContent, tree));
150
+ // eslint-disable-next-line react-hooks/exhaustive-deps
151
+ }, [currentDataContent]);
152
+
102
153
  const handleClick = (dataID: string) => {
103
154
  setSelectedCategory(dataID, scope);
104
155
  };
105
156
 
106
157
  const { isOpen, toggleModal } = useModal();
107
158
 
108
- const openModal = () => {
109
- resetCategoryValues();
110
- toggleModal();
111
- };
112
-
113
159
  const rightButtonProps = allowedToCreateTaxonomy
114
160
  ? {
115
161
  label: "New",
116
- action: openModal,
162
+ action: toggleModal,
117
163
  }
118
164
  : undefined;
119
165
 
120
166
  const bulkDelete = async () => {
121
- const idsToBeDeleted = getAllLangCategoriesIds(currentDataContent, selectedItems, deleteAllVersions);
122
- const deleted = await deleteDataContent(idsToBeDeleted);
123
- if (deleted) {
167
+ const { groups, categories } = filterCategoriesAndGroups(currentDataContent);
168
+ const idsCatsToBeDeleted = getAllLangCategoriesIds(categories, selectedItems);
169
+ const idsGroupsToBeDeleted = getAllLangCategoriesIds(groups, selectedItems);
170
+ const deletedCats = idsCatsToBeDeleted.length && (await deleteDataContent(idsCatsToBeDeleted, false));
171
+ const deletedGroups =
172
+ idsGroupsToBeDeleted.length && (await deleteCategoryGroup(idsGroupsToBeDeleted, deleteGroupCategories, false));
173
+ if (deletedCats || deletedGroups) {
174
+ currentStructuredData && getContents(currentStructuredData.id);
175
+ toggleToast(`${selectedItems.all.length} categories and/or groups deleted`);
124
176
  unselectAllItems();
125
- setDeletedItem(idsToBeDeleted);
126
177
  isDeleteOpen && toggleDeleteModal();
127
- toggleToast();
178
+ isGroupOpen && toggleGroupModal();
128
179
  }
129
180
  };
130
181
 
131
182
  const handleToggleDeleteModal = () => {
132
183
  const selectedCategories = currentDataContent.filter((category) => selectedItems.all.includes(category.id));
133
- const hasTranslations = selectedCategories.some((category) => category.dataLanguages.length > 1);
134
- hasTranslations ? toggleDeleteModal() : bulkDelete();
184
+ const hasGroups = selectedCategories.some((category) => category.type === "group");
185
+ hasGroups ? toggleGroupModal() : toggleDeleteModal();
135
186
  };
136
187
 
137
188
  const unselectAllItems = () => resetBulkSelection();
@@ -142,17 +193,12 @@ const CategoriesList = (props: IProps): JSX.Element => {
142
193
 
143
194
  const onScroll = (e: any) => setIsScrolling(e.target.scrollTop > 0);
144
195
 
145
- const undoAction = () => {
146
- if (deletedItem) {
147
- restoreDataContent(deletedItem);
148
- }
149
- setIsVisible(false);
150
- };
196
+ const filterItems = async (filterPointer: string, filtersSelected: IQueryValue[]) =>
197
+ setFiltersSelection(filterPointer, filtersSelected);
151
198
 
152
199
  const toastProps = {
153
- action: () => undoAction(),
154
200
  setIsVisible,
155
- message: deletedItem ? `Categories deleted` : "",
201
+ message: toastState,
156
202
  };
157
203
 
158
204
  const bulkActions = allowedToDeleteTaxonomy
@@ -174,6 +220,8 @@ const CategoriesList = (props: IProps): JSX.Element => {
174
220
  checkState={checkState}
175
221
  isScrolling={isScrolling}
176
222
  bulkActions={bulkActions}
223
+ filterValues={filterValues}
224
+ filterItems={filterItems}
177
225
  />
178
226
  );
179
227
 
@@ -185,11 +233,121 @@ const CategoriesList = (props: IProps): JSX.Element => {
185
233
  setLanguage,
186
234
  };
187
235
 
188
- const pagination = {
189
- setPage,
190
- itemsPerPage,
191
- totalItems,
192
- currPage: page,
236
+ const getIcon = (item: TreeItem, onExpand: (itemId: ItemId) => void, onCollapse: (itemId: ItemId) => void) => {
237
+ const handleCollapse = (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent) => {
238
+ e.stopPropagation();
239
+ onCollapse(item.id);
240
+ };
241
+
242
+ const handleExpand = (e: React.MouseEvent<HTMLDivElement> | React.KeyboardEvent) => {
243
+ e.stopPropagation();
244
+ onExpand(item.id);
245
+ };
246
+
247
+ if (item.children && item.children.length > 0) {
248
+ return item.isExpanded ? (
249
+ <div onClick={handleCollapse} role="button" tabIndex={0} onKeyDown={handleCollapse}>
250
+ <Icon name="UpArrow" />
251
+ </div>
252
+ ) : (
253
+ <div onClick={handleExpand} role="button" tabIndex={0} onKeyDown={handleExpand}>
254
+ <Icon name="DownArrow" />
255
+ </div>
256
+ );
257
+ } else {
258
+ return <></>;
259
+ }
260
+ };
261
+
262
+ const renderItem = ({ item, onExpand, onCollapse, provided }: RenderItemParams) => {
263
+ const category = formatItem(item, tree);
264
+ const isItemSelected = isSelected(category.id);
265
+ return (
266
+ <S.Draggable ref={provided.innerRef} {...provided.draggableProps}>
267
+ <CategoryItem
268
+ category={category}
269
+ key={category.id}
270
+ languages={availableLanguages}
271
+ lang={lang}
272
+ isTranslatable={isTranslatable}
273
+ isSelected={isItemSelected}
274
+ onChange={addToBulkSelection}
275
+ toggleToast={toggleToast}
276
+ getContents={getContents}
277
+ icon={getIcon(item, onExpand, onCollapse)}
278
+ dragHandleProps={provided.dragHandleProps}
279
+ />
280
+ </S.Draggable>
281
+ );
282
+ };
283
+
284
+ const onExpand = (itemId: ItemId) => {
285
+ const newTree = mutateTree(tree, itemId, { isExpanded: true });
286
+ setTree(newTree);
287
+ };
288
+
289
+ const onCollapse = (itemId: ItemId) => {
290
+ const newTree = mutateTree(tree, itemId, { isExpanded: false });
291
+ setTree(newTree);
292
+ };
293
+
294
+ const onDragStart = (item: ItemId) => setItemDragging(item);
295
+
296
+ const onDragEnd = async (source: TreeSourcePosition, destination?: TreeDestinationPosition) => {
297
+ if (!destination) {
298
+ setItemDragging(null);
299
+ return;
300
+ }
301
+
302
+ if (destination.index === source.index && destination.parentId === source.parentId) {
303
+ setItemDragging(null);
304
+ return;
305
+ }
306
+
307
+ const item = itemDragging ? findChild(tree, itemDragging) : null;
308
+
309
+ if (!item) {
310
+ setItemDragging(null);
311
+ return;
312
+ }
313
+
314
+ const { parentId } = destination;
315
+ const parentIdNumber = parentId === "root" ? 0 : parseInt(parentId as string);
316
+ const parent: any = getElementsFromTree(tree).find((data: any) => data.id === parentIdNumber);
317
+
318
+ if (parentId === "root" || parent?.type === "group") {
319
+ const newTree = moveItemOnTree(tree, source, destination);
320
+ newTree.items[parentId].isExpanded = true;
321
+ setTree(newTree);
322
+
323
+ const isGoingUp =
324
+ (destination.parentId === source.parentId &&
325
+ destination.index !== undefined &&
326
+ destination.index < source.index) ||
327
+ destination.parentId !== source.parentId;
328
+
329
+ const index =
330
+ destination.index !== undefined
331
+ ? destination.index - (isGoingUp ? 1 : 0)
332
+ : parent
333
+ ? parent.children.length - 1
334
+ : 0;
335
+
336
+ const itemDestination = findChildByIndex(tree, parentId, index);
337
+
338
+ const params: IOrderCategoryParams = {
339
+ contentId: item.id,
340
+ type: item.type,
341
+ position: itemDestination ? itemDestination.position + 1 : 1,
342
+ parentGroup: parentIdNumber,
343
+ structuredData: item.structuredData,
344
+ relatedSite: currentSiteID || null,
345
+ language: lang.id,
346
+ };
347
+
348
+ await orderCategory(params);
349
+ setItemDragging(null);
350
+ }
193
351
  };
194
352
 
195
353
  const isEmpty = currentDataContent && currentDataContent.length === 0;
@@ -200,16 +358,23 @@ const CategoriesList = (props: IProps): JSX.Element => {
200
358
  ? `To start using ${categoryName} categories, create as many of them as yo need.`
201
359
  : "To start using categories in your site, you must active all Content type packages as you need.",
202
360
  button: hasCategories ? `Create ${categoryName} category` : "Activate Content type packages",
203
- action: hasCategories ? openModal : () => setHistoryPush("/sites/settings/content-types"),
361
+ action: hasCategories ? toggleModal : () => setHistoryPush("/sites/settings/content-types"),
204
362
  };
205
363
 
206
364
  const mainDeleteModalAction = {
207
- title: "Delete categories",
365
+ title: "Yes, delete it",
208
366
  onClick: bulkDelete,
209
367
  };
210
368
 
211
369
  const secondaryDeleteModalAction = { title: "Cancel", onClick: toggleDeleteModal };
212
370
 
371
+ const mainDeleteGroupModalAction = {
372
+ title: "Delete",
373
+ onClick: bulkDelete,
374
+ };
375
+
376
+ const secondaryDeleteGroupModalAction = { title: "Cancel", onClick: toggleGroupModal };
377
+
213
378
  return (
214
379
  <MainWrapper
215
380
  title="Categories"
@@ -217,6 +382,8 @@ const CategoriesList = (props: IProps): JSX.Element => {
217
382
  language={lang}
218
383
  availableLanguages={availableLanguages}
219
384
  languageActions={languageActions}
385
+ searchAction={setSearchQuery}
386
+ searchValue={searchQuery}
220
387
  >
221
388
  <S.CategoryListWrapper>
222
389
  <CategoryNav
@@ -224,52 +391,71 @@ const CategoriesList = (props: IProps): JSX.Element => {
224
391
  categories={currentCategories}
225
392
  onClick={handleClick}
226
393
  dataPacks={activatedDataPacks}
227
- setSelectedCategory={setSelectedCategory}
228
- scope={scope}
229
394
  />
230
395
  <S.TableWrapper>
231
396
  <ErrorToast />
232
- <TableList
233
- tableHeader={TableHeader}
234
- pagination={pagination}
235
- onScroll={onScroll}
236
- hasFixedHeader={true}
237
- tableRef={tableRef}
238
- >
397
+ <S.Intro>
398
+ <S.IntroTitle>Create and manage categories</S.IntroTitle>
399
+ <div>Define your categories by creating the elements you need and asign them to your content types.</div>
400
+ </S.Intro>
401
+ <TableList tableHeader={TableHeader} onScroll={onScroll} hasFixedHeader={true} tableRef={tableRef}>
402
+ <S.SearchTags>
403
+ <SearchTagsBar query={searchQuery} setQuery={setSearchQuery} />
404
+ <FilterTagsBar
405
+ filters={filterValues}
406
+ setFilters={setFiltersSelection}
407
+ resetFilters={resetFilterQuery}
408
+ labels={{ translated: "Translated" }}
409
+ />
410
+ </S.SearchTags>
239
411
  {isEmpty ? (
240
412
  <S.EmptyWrapper>
241
413
  <EmptyState {...emptyStateProps} />
242
414
  </S.EmptyWrapper>
243
415
  ) : (
244
- currentDataContent.map((category: any) => {
245
- const isItemSelected = isSelected(category.id);
246
- return (
247
- <CategoryItem
248
- category={category}
249
- key={category.id}
250
- languages={availableLanguages}
251
- lang={lang}
252
- isTranslatable={isTranslatable}
253
- isSelected={isItemSelected}
254
- onChange={addToBulkSelection}
255
- toggleToast={toggleToast}
256
- setDeletedItem={setDeletedItem}
257
- getContents={getContents}
416
+ <>
417
+ <S.Notification>
418
+ Arrange your category list by <strong>drag & drop</strong>. You can move categories between groups,
419
+ but <strong>it&apos;s not possible to nest one category within another to create a new group.</strong>
420
+ </S.Notification>
421
+ <S.Droppable>
422
+ <Tree
423
+ tree={tree}
424
+ renderItem={renderItem}
425
+ onExpand={onExpand}
426
+ onCollapse={onCollapse}
427
+ onDragEnd={onDragEnd}
428
+ onDragStart={onDragStart}
429
+ isDragEnabled
430
+ isNestingEnabled
258
431
  />
259
- );
260
- })
432
+ </S.Droppable>
433
+ </>
261
434
  )}
262
435
  </TableList>
263
436
  </S.TableWrapper>
264
437
  </S.CategoryListWrapper>
265
- <CategoryPanel isOpen={isOpen} toggleModal={toggleModal} getContents={getContents} />
266
- {isDeleteOpen && <DeleteModal
267
- isOpen={isDeleteOpen}
268
- toggleModal={toggleDeleteModal}
269
- mainModalAction={mainDeleteModalAction}
270
- secondaryModalAction={secondaryDeleteModalAction}
271
- {...{ deleteAllVersions, setDeleteAllVersions }}
272
- />}
438
+ {isOpen && (
439
+ <CategoryPanel isOpen={isOpen} toggleModal={toggleModal} getContents={getContents} toggleToast={toggleToast} />
440
+ )}
441
+ {isDeleteOpen && (
442
+ <DeleteModal
443
+ isOpen={isDeleteOpen}
444
+ toggleModal={toggleDeleteModal}
445
+ mainModalAction={mainDeleteModalAction}
446
+ secondaryModalAction={secondaryDeleteModalAction}
447
+ />
448
+ )}
449
+ {isGroupOpen && (
450
+ <DeleteGroupModal
451
+ isOpen={isGroupOpen}
452
+ toggleModal={toggleGroupModal}
453
+ mainModalAction={mainDeleteGroupModalAction}
454
+ secondaryModalAction={secondaryDeleteGroupModalAction}
455
+ deleteGroupCategories={deleteGroupCategories}
456
+ setDeleteGroupCategories={setDeleteGroupCategories}
457
+ />
458
+ )}
273
459
  {isVisible && <Toast {...toastProps} />}
274
460
  </MainWrapper>
275
461
  );
@@ -280,7 +466,7 @@ const mapStateToProps = (state: IRootState) => ({
280
466
  currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
281
467
  siteLanguages: state.sites.currentSiteLanguages,
282
468
  globalLangs: state.app.globalLangs,
283
- currentDataContent: state.structuredData.currentDataContent,
469
+ currentDataContent: state.structuredData.currentDataCategory,
284
470
  currentStructuredData: state.structuredData.currentStructuredData,
285
471
  lang: state.app.lang,
286
472
  totalItems: state.sites.totalItems,
@@ -289,24 +475,24 @@ const mapStateToProps = (state: IRootState) => ({
289
475
 
290
476
  interface IDispatchProps {
291
477
  getStructuredDataContents(params: IGetStructuredDataParams, siteID: number | null): Promise<void>;
292
- resetCategoryValues(): void;
293
478
  setLanguage(lang: { locale: string; id: number | null }): void;
294
479
  setSelectedCategory(id: string, scope: string): void;
295
- deleteDataContent(catID: number[]): Promise<boolean>;
296
- restoreDataContent(catID: number | number[]): void;
480
+ deleteDataContent(catID: number[], refresh?: boolean): Promise<boolean>;
297
481
  setHistoryPush(path: string): void;
298
482
  resetCurrentData(): Promise<void>;
483
+ deleteCategoryGroup(id: number | number[], deleteChildren: boolean, refresh?: boolean): Promise<boolean>;
484
+ orderCategory(params: IOrderCategoryParams): Promise<boolean>;
299
485
  }
300
486
 
301
487
  interface ICategoriesProps {
302
488
  currentSiteID: number | null;
303
- currentDataContent: IStructuredDataContent[];
489
+ currentDataContent: (IStructuredDataCategory | ICategoryGroup)[];
304
490
  currentStructuredData: IStructuredData | null;
305
- lang: { locale: string; id: number | null };
491
+ lang: { locale: string; id: number };
306
492
  siteLanguages: any[];
307
493
  globalLangs: any[];
308
494
  totalItems: number;
309
- categories: any;
495
+ categories: { global: IStructuredData[]; site: IStructuredData[] };
310
496
  activatedDataPacks: IDataPack[];
311
497
  }
312
498
 
@@ -314,13 +500,13 @@ type IProps = ICategoriesProps & IDispatchProps;
314
500
 
315
501
  const mapDispatchToProps = {
316
502
  getStructuredDataContents: structuredDataActions.getStructuredDataContents,
317
- resetCategoryValues: structuredDataActions.resetCategoryValues,
318
503
  setLanguage: appActions.setLanguage,
319
504
  setSelectedCategory: structuredDataActions.setSelectedCategory,
320
505
  deleteDataContent: structuredDataActions.deleteStructuredDataContent,
321
- restoreDataContent: structuredDataActions.restoreStructuredDataContent,
506
+ deleteCategoryGroup: structuredDataActions.deleteCategoryGroup,
322
507
  setHistoryPush: appActions.setHistoryPush,
323
508
  resetCurrentData: structuredDataActions.resetCurrentData,
509
+ orderCategory: structuredDataActions.orderCategory,
324
510
  };
325
511
 
326
512
  export default connect(mapStateToProps, mapDispatchToProps)(CategoriesList);