@griddo/ax 10.6.9 → 10.6.12

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 (70) hide show
  1. package/package.json +2 -2
  2. package/src/components/Fields/CheckField/index.tsx +16 -4
  3. package/src/components/Fields/DateField/index.tsx +9 -3
  4. package/src/components/Fields/TimeField/index.tsx +8 -2
  5. package/src/components/Icon/components/CancelEvent.js +10 -0
  6. package/src/components/Icon/svgs/cancel-event.svg +3 -0
  7. package/src/components/MainWrapper/AppBar/index.tsx +8 -1
  8. package/src/components/MainWrapper/AppBar/style.tsx +9 -1
  9. package/src/components/MainWrapper/index.tsx +1 -0
  10. package/src/components/Modal/index.tsx +17 -1
  11. package/src/components/ScheduleModal/index.tsx +100 -0
  12. package/src/components/ScheduleModal/style.tsx +19 -0
  13. package/src/components/TableFilters/LiveFilter/index.tsx +1 -1
  14. package/src/components/index.tsx +3 -0
  15. package/src/containers/PageEditor/actions.tsx +21 -1
  16. package/src/containers/PageEditor/interfaces.tsx +1 -0
  17. package/src/forms/validators.tsx +3 -2
  18. package/src/helpers/dates.tsx +6 -0
  19. package/src/helpers/index.tsx +2 -0
  20. package/src/hooks/bulk.tsx +5 -1
  21. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/index.tsx +17 -12
  22. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +5 -1
  23. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +3 -1
  24. package/src/modules/Categories/CategoriesList/index.tsx +3 -0
  25. package/src/modules/Content/BulkHeader/TableHeader/index.tsx +15 -9
  26. package/src/modules/Content/BulkHeader/index.tsx +3 -0
  27. package/src/modules/Content/PageItem/index.tsx +10 -4
  28. package/src/modules/Content/index.tsx +4 -0
  29. package/src/modules/Content/utils.tsx +13 -10
  30. package/src/modules/FileDrive/BulkListHeader/TableHeader/index.tsx +15 -11
  31. package/src/modules/FileDrive/BulkListHeader/index.tsx +4 -1
  32. package/src/modules/FileDrive/ListItem/index.tsx +3 -1
  33. package/src/modules/FileDrive/index.tsx +6 -3
  34. package/src/modules/GlobalEditor/index.tsx +83 -3
  35. package/src/modules/GlobalEditor/style.tsx +12 -1
  36. package/src/modules/Navigation/Defaults/BulkHeader/TableHeader/index.tsx +18 -12
  37. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +11 -3
  38. package/src/modules/Navigation/Defaults/Item/index.tsx +9 -2
  39. package/src/modules/Navigation/Defaults/index.tsx +4 -0
  40. package/src/modules/PageEditor/index.tsx +82 -2
  41. package/src/modules/PageEditor/style.tsx +9 -1
  42. package/src/modules/Redirects/BulkHeader/TableHeader/index.tsx +17 -10
  43. package/src/modules/Redirects/BulkHeader/index.tsx +4 -0
  44. package/src/modules/Redirects/RedirectItem/index.tsx +8 -1
  45. package/src/modules/Redirects/index.tsx +3 -0
  46. package/src/modules/Settings/Integrations/BulkHeader/TableHeader/index.tsx +16 -11
  47. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +5 -1
  48. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +3 -1
  49. package/src/modules/Settings/Integrations/index.tsx +3 -0
  50. package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +15 -10
  51. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +3 -0
  52. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +14 -3
  53. package/src/modules/Sites/SitesList/index.tsx +3 -0
  54. package/src/modules/StructuredData/Form/index.tsx +72 -6
  55. package/src/modules/StructuredData/Form/style.tsx +17 -1
  56. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +15 -9
  57. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +3 -0
  58. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +10 -3
  59. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +13 -4
  60. package/src/modules/StructuredData/StructuredDataList/atoms.tsx +1 -1
  61. package/src/modules/StructuredData/StructuredDataList/index.tsx +4 -0
  62. package/src/modules/Users/Roles/BulkHeader/TableHeader/index.tsx +24 -10
  63. package/src/modules/Users/Roles/BulkHeader/index.tsx +4 -0
  64. package/src/modules/Users/Roles/RoleItem/index.tsx +3 -2
  65. package/src/modules/Users/Roles/index.tsx +12 -2
  66. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +17 -10
  67. package/src/modules/Users/UserList/BulkHeader/index.tsx +4 -0
  68. package/src/modules/Users/UserList/UserItem/index.tsx +3 -1
  69. package/src/modules/Users/UserList/index.tsx +24 -7
  70. package/src/types/index.tsx +2 -0
@@ -106,6 +106,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
106
106
  checkState,
107
107
  addToBulkSelection,
108
108
  selectAllItems,
109
+ setHoverCheck,
109
110
  } = useBulkSelection(catIds);
110
111
 
111
112
  const getParams = useCallback((): IGetStructuredDataParams => {
@@ -222,6 +223,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
222
223
  bulkActions={bulkActions}
223
224
  filterValues={filterValues}
224
225
  filterItems={filterItems}
226
+ setHoverCheck={setHoverCheck}
225
227
  />
226
228
  );
227
229
 
@@ -276,6 +278,7 @@ const CategoriesList = (props: IProps): JSX.Element => {
276
278
  getContents={getContents}
277
279
  icon={getIcon(item, onExpand, onCollapse)}
278
280
  dragHandleProps={provided.dragHandleProps}
281
+ hoverCheck={checkState.hoverCheck}
279
282
  />
280
283
  </S.Draggable>
281
284
  );
@@ -9,6 +9,7 @@ import {
9
9
  StatusFilter,
10
10
  LiveFilter,
11
11
  TranslationsFilter,
12
+ Tooltip,
12
13
  } from "@ax/components";
13
14
  import { IColumn, IQueryValue, ISchemaField } from "@ax/types";
14
15
 
@@ -31,6 +32,7 @@ const TableHeader = (props: IProps): JSX.Element => {
31
32
  filterValues,
32
33
  siteID,
33
34
  maxColumns,
35
+ setHoverCheck,
34
36
  } = props;
35
37
 
36
38
  const activeColumns = columns.filter((col) => col.show).map((col) => col.id);
@@ -54,15 +56,18 @@ const TableHeader = (props: IProps): JSX.Element => {
54
56
  return (
55
57
  <S.TableHeader isScrolling={isScrolling}>
56
58
  <S.CheckHeader>
57
- <CheckField
58
- key="selectAll"
59
- name="selectAll"
60
- value="selectAll"
61
- onChange={selectAllItems}
62
- checked={checkState.isAllSelected}
63
- disabled={false}
64
- error={false}
65
- />
59
+ <Tooltip content="Select All Pages" bottom>
60
+ <CheckField
61
+ key="selectAll"
62
+ name="selectAll"
63
+ value="selectAll"
64
+ onChange={selectAllItems}
65
+ checked={checkState.isAllSelected || checkState.hoverCheck}
66
+ disabled={false}
67
+ error={false}
68
+ setHoverCheck={setHoverCheck}
69
+ />
70
+ </Tooltip>
66
71
  </S.CheckHeader>
67
72
  {isStructuredData ? (
68
73
  <>
@@ -137,6 +142,7 @@ interface IProps {
137
142
  isGlobalPages: boolean;
138
143
  filterValues: any;
139
144
  siteID: number;
145
+ setHoverCheck: (state: boolean) => void;
140
146
  }
141
147
 
142
148
  export default TableHeader;
@@ -28,6 +28,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
28
28
  siteID,
29
29
  maxColumns,
30
30
  exportAction,
31
+ setHoverCheck,
31
32
  } = props;
32
33
 
33
34
  const isAllowedToDeletePage = usePermission("content.deletePages");
@@ -82,6 +83,7 @@ const BulkHeader = (props: IProps): JSX.Element => {
82
83
  isGlobalPages={isGlobalPages}
83
84
  siteID={siteID}
84
85
  maxColumns={maxColumns}
86
+ setHoverCheck={setHoverCheck}
85
87
  />
86
88
  );
87
89
  };
@@ -109,6 +111,7 @@ interface IProps {
109
111
  isGlobalPages: boolean;
110
112
  siteID: number;
111
113
  exportAction?(formats: (number | string)[]): void;
114
+ setHoverCheck: (state: boolean) => void;
112
115
  }
113
116
 
114
117
  export default BulkHeader;
@@ -3,7 +3,7 @@ import { schemas } from "components";
3
3
  import { useTheme } from "styled-components";
4
4
 
5
5
  import { useAdaptiveText, useModal, usePermission } from "@ax/hooks";
6
- import { getHumanLastModifiedDate, getTemplateDisplayName, slugify } from "@ax/helpers";
6
+ import { getHumanLastModifiedDate, getScheduleFormatDate, getTemplateDisplayName, slugify } from "@ax/helpers";
7
7
  import { IPage, ISite, ISavePageParams, ICheck, IColumn, IPageLanguage, IDataPack } from "@ax/types";
8
8
  import { pageStatus, ISetCurrentPageIDAction } from "@ax/containers/PageEditor/interfaces";
9
9
  import {
@@ -37,6 +37,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
37
37
  dataPacks,
38
38
  sites,
39
39
  skipReview,
40
+ hoverCheck,
40
41
  } = props;
41
42
  const { isSelected, siteLanguages, page, lang, isDuplicable } = item;
42
43
  const {
@@ -68,6 +69,8 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
68
69
  templateId,
69
70
  structuredDataContent,
70
71
  liveStatus,
72
+ publicationScheduled,
73
+ haveDraftPage,
71
74
  } = page;
72
75
 
73
76
  const displayName = getTemplateDisplayName(templateId);
@@ -105,13 +108,15 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
105
108
  const API_URL = process.env.REACT_APP_API_ENDPOINT;
106
109
  const isPublished = liveStatus.status === pageStatus.PUBLISHED || liveStatus.status === pageStatus.UPLOAD_PENDING;
107
110
  const isAllowedToDelete = (isPublished && isAllowedToDeletePublishedPage) || (!isPublished && isAllowedToDeletePage);
111
+ const isScheduledPub = !!publicationScheduled && liveStatus.status === pageStatus.SCHEDULED;
108
112
 
109
- const publishedTooltip: any = {
113
+ const publishedTooltip: Record<string, string> = {
110
114
  active: "Live",
111
115
  "upload-pending": "Publication pending",
112
116
  offline: "Offline",
113
117
  "offline-pending": "Offline pending",
114
118
  modified: "Live & Modified",
119
+ scheduled: `Scheduled publication: ${isScheduledPub ? getScheduleFormatDate(publicationScheduled) : ""}`,
115
120
  };
116
121
 
117
122
  const setRoute = (path: string) => setHistoryPush(path, true);
@@ -456,7 +461,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
456
461
 
457
462
  const mainCopyModalAction = { title: "Copy page", onClick: copyToOtherSite, disabled: !site };
458
463
 
459
- const getLiveStatus = () => (page.haveDraftPage ? "modified" : page.liveStatus.status);
464
+ const getLiveStatus = () => (haveDraftPage ? "modified" : liveStatus.status);
460
465
 
461
466
  const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
462
467
 
@@ -491,7 +496,7 @@ const PageItem = (props: IPageItemProps): JSX.Element => {
491
496
  <>
492
497
  <S.PageRow role="rowgroup" selected={isSelected} global={isGlobal}>
493
498
  <S.CheckCell role="cell">
494
- <CheckField name="check" value={page.id} checked={isSelected} onChange={handleOnChange} />
499
+ <CheckField name="check" value={page.id} checked={isSelected || hoverCheck} onChange={handleOnChange} />
495
500
  </S.CheckCell>
496
501
  <S.NameCell role="cell" onClick={goToPage} ref={nameCellRef}>
497
502
  {isHome && (
@@ -689,6 +694,7 @@ interface IPageItemProps {
689
694
  dataPacks: IDataPack[];
690
695
  sites: ISite[];
691
696
  skipReview?: boolean;
697
+ hoverCheck?: boolean;
692
698
  }
693
699
 
694
700
  export default memo(PageItem);
@@ -217,6 +217,7 @@ const Content = (props: IProps): JSX.Element => {
217
217
  checkState,
218
218
  addToBulkSelection,
219
219
  selectAllItems,
220
+ setHoverCheck,
220
221
  } = useBulkSelection(contentIds);
221
222
 
222
223
  const { isVisible, toggleToast, setIsVisible } = useToast();
@@ -582,6 +583,7 @@ const Content = (props: IProps): JSX.Element => {
582
583
  siteID={currentSiteInfo.id}
583
584
  maxColumns={maxColumns}
584
585
  exportAction={exportContent}
586
+ setHoverCheck={setHoverCheck}
585
587
  />
586
588
  );
587
589
 
@@ -633,6 +635,7 @@ const Content = (props: IProps): JSX.Element => {
633
635
  columns={columnsState}
634
636
  categoryColors={categoryColors}
635
637
  addCategoryColors={addCategoryColors}
638
+ hoverCheck={checkState.hoverCheck}
636
639
  />
637
640
  );
638
641
  });
@@ -722,6 +725,7 @@ const Content = (props: IProps): JSX.Element => {
722
725
  addCategoryColors={addCategoryColors}
723
726
  dataPacks={dataPacks}
724
727
  skipReview={skipReviewOnPublish}
728
+ hoverCheck={checkState.hoverCheck}
725
729
  />
726
730
  );
727
731
  });
@@ -1,5 +1,5 @@
1
1
  import { schemas } from "components";
2
- import { arrayInsert, filterDuplicatedValues, getActivatedDataPacksIds } from "@ax/helpers";
2
+ import { arrayInsert, getActivatedDataPacksIds } from "@ax/helpers";
3
3
  import { pageStatus } from "@ax/containers/PageEditor/interfaces";
4
4
  import { IColumn, IDataPack, IPage, ISchemaField, IStructuredData } from "@ax/types";
5
5
 
@@ -7,10 +7,15 @@ const getTemplatesFilters = (activatedDataPacks: IDataPack[]) => {
7
7
  const activatedDataPacksIds = getActivatedDataPacksIds(activatedDataPacks);
8
8
  const { templates } = schemas;
9
9
 
10
- const filters: { label: string; value: string; mode?: string }[] = Object.keys(templates)
11
- .filter((templateName: string) => !templates[templateName].dataPacks)
10
+ const staticFilters: { label: string; value: string; mode?: string }[] = Object.keys(templates)
11
+ .filter((templateName: string) => !Array.isArray(templates[templateName].dataPacks))
12
12
  .map((templateName: string) => templates[templateName].type);
13
13
 
14
+ const uniqueStaticFilters = [...new Map(staticFilters.map((item: any) => [item.value, item])).values()];
15
+ const sortedUniqueFilters = uniqueStaticFilters.sort((a, b) => a.label.localeCompare(b.label));
16
+
17
+ const dataPackFilters: { label: string; value: string; mode?: string }[] = [];
18
+
14
19
  Object.keys(templates)
15
20
  .filter((templateName: string) => Array.isArray(templates[templateName].dataPacks))
16
21
  .forEach((templateName: string) => {
@@ -24,7 +29,7 @@ const getTemplatesFilters = (activatedDataPacks: IDataPack[]) => {
24
29
  );
25
30
 
26
31
  if (currentDataPack) {
27
- filters.push({
32
+ dataPackFilters.push({
28
33
  label: currentDataPack?.title,
29
34
  value: currentDataPack?.id,
30
35
  mode: type.mode,
@@ -34,9 +39,7 @@ const getTemplatesFilters = (activatedDataPacks: IDataPack[]) => {
34
39
  }
35
40
  });
36
41
 
37
- const uniqueFilters: { label: string; value: string; mode: string }[] = filterDuplicatedValues(filters);
38
-
39
- return uniqueFilters;
42
+ return { staticFilters: sortedUniqueFilters, dataPackFilters };
40
43
  };
41
44
 
42
45
  const mapStructuredOptions = (options: any[]) => {
@@ -82,7 +85,7 @@ const getOptionValues = (options: IStructuredData[]) => {
82
85
 
83
86
  const getOptionFilters = (options: IStructuredData[], activatedDataPacks: IDataPack[]) => {
84
87
  const pureOptions = options.filter((option: IStructuredData) => !option.fromPage);
85
- const templateFilters = getTemplatesFilters(activatedDataPacks);
88
+ const { staticFilters, dataPackFilters } = getTemplatesFilters(activatedDataPacks);
86
89
  const activatedDataPacksIds = getActivatedDataPacksIds(activatedDataPacks);
87
90
 
88
91
  const mappedOptions = pureOptions.flatMap((option: IStructuredData) =>
@@ -104,10 +107,10 @@ const getOptionFilters = (options: IStructuredData[], activatedDataPacks: IDataP
104
107
  }, [])
105
108
  );
106
109
 
107
- const filters = [...templateFilters, ...mappedOptions];
110
+ const filters = [...dataPackFilters, ...mappedOptions];
108
111
  const uniqueFilters = [...new Map(filters.map((item: any) => [item.value, item])).values()];
109
112
  const sortedUniqueFilters = uniqueFilters.sort((a, b) => a.label.localeCompare(b.label));
110
- return sortedUniqueFilters;
113
+ return [...staticFilters, ...sortedUniqueFilters];
111
114
  };
112
115
 
113
116
  const getCurrentFilter = (structuredData: any, filter: any) =>
@@ -1,24 +1,27 @@
1
1
  import React from "react";
2
2
 
3
- import { CheckField, TableCounter } from "@ax/components";
3
+ import { CheckField, TableCounter, Tooltip } from "@ax/components";
4
4
 
5
5
  import * as S from "./style";
6
6
 
7
7
  const TableHeader = (props: IProps): JSX.Element => {
8
- const { isScrolling, selectAllItems, checkState, totalItems, isSearching } = props;
8
+ const { isScrolling, selectAllItems, checkState, totalItems, isSearching, setHoverCheck } = props;
9
9
 
10
10
  return (
11
11
  <S.TableHeader isScrolling={isScrolling}>
12
12
  <S.CheckHeader>
13
- <CheckField
14
- key="selectAll"
15
- name="selectAll"
16
- value="selectAll"
17
- onChange={selectAllItems}
18
- checked={checkState.isAllSelected}
19
- disabled={false}
20
- error={false}
21
- />
13
+ <Tooltip content="Select All Files">
14
+ <CheckField
15
+ key="selectAll"
16
+ name="selectAll"
17
+ value="selectAll"
18
+ onChange={selectAllItems}
19
+ checked={checkState.isAllSelected || checkState.hoverCheck}
20
+ disabled={false}
21
+ error={false}
22
+ setHoverCheck={setHoverCheck}
23
+ />
24
+ </Tooltip>
22
25
  </S.CheckHeader>
23
26
  <S.NameHeader>Name</S.NameHeader>
24
27
  <S.TypeHeader>Type</S.TypeHeader>
@@ -39,6 +42,7 @@ interface IProps {
39
42
  selectAllItems: () => void;
40
43
  checkState: Record<string, boolean>;
41
44
  isSearching: boolean;
45
+ setHoverCheck: (state: boolean) => void;
42
46
  }
43
47
 
44
48
  export default TableHeader;
@@ -6,7 +6,8 @@ import TableHeader from "./TableHeader";
6
6
  import * as S from "./style";
7
7
 
8
8
  const BulkListHeader = (props: IBulkHeaderProps): JSX.Element => {
9
- const { showBulk, checkState, selectItems, selectAllItems, totalItems, bulkActions, isSearching } = props;
9
+ const { showBulk, checkState, selectItems, selectAllItems, totalItems, bulkActions, isSearching, setHoverCheck } =
10
+ props;
10
11
 
11
12
  return showBulk ? (
12
13
  <S.BulkWrapper>
@@ -25,6 +26,7 @@ const BulkListHeader = (props: IBulkHeaderProps): JSX.Element => {
25
26
  checkState={checkState}
26
27
  totalItems={totalItems}
27
28
  isSearching={isSearching}
29
+ setHoverCheck={setHoverCheck}
28
30
  />
29
31
  );
30
32
  };
@@ -37,6 +39,7 @@ export interface IBulkHeaderProps {
37
39
  totalItems: number;
38
40
  bulkActions: IBulkAction[];
39
41
  isSearching: boolean;
42
+ setHoverCheck: (state: boolean) => void;
40
43
  }
41
44
 
42
45
  export default BulkListHeader;
@@ -17,6 +17,7 @@ const ListItem = (props: IProps) => {
17
17
  isAllowedToEdit,
18
18
  isAllowedToMove,
19
19
  isSearching,
20
+ hoverCheck,
20
21
  onChange,
21
22
  onClick,
22
23
  onDelete,
@@ -117,7 +118,7 @@ const ListItem = (props: IProps) => {
117
118
  <>
118
119
  <S.ItemRow role="rowgroup" selected={isSelected}>
119
120
  <S.CheckCell role="cell">
120
- <CheckField name="check" value={id} checked={isSelected} onChange={handleChange} />
121
+ <CheckField name="check" value={id} checked={isSelected || hoverCheck} onChange={handleChange} />
121
122
  </S.CheckCell>
122
123
  <S.IconCell role="cell" onClick={handleClick}>
123
124
  <img src={iconUrl} alt={`${fileType} Icon`} />
@@ -192,6 +193,7 @@ interface IProps {
192
193
  onClick(file: IFile): void;
193
194
  onDelete(fileID: number): void;
194
195
  onMove(fileID: number, folderID: number): void;
196
+ hoverCheck: boolean;
195
197
  }
196
198
 
197
199
  export default ListItem;
@@ -201,6 +201,7 @@ const FileDrive = (props: IProps) => {
201
201
  checkState,
202
202
  addToBulkSelection,
203
203
  selectAllItems,
204
+ setHoverCheck,
204
205
  } = useBulkSelection(filesIds);
205
206
 
206
207
  const handleSelectedTab = (tab: "site" | "global") => {
@@ -431,10 +432,11 @@ const FileDrive = (props: IProps) => {
431
432
  selectItems={selectItems}
432
433
  bulkActions={bulkActions}
433
434
  isSearching={isSearching}
435
+ setHoverCheck={setHoverCheck}
434
436
  />
435
437
  );
436
438
 
437
- const GridList = () => (
439
+ const gridList = (
438
440
  <S.GridWrapper isSearching={isSearching}>
439
441
  {progressItems.map((progress: number, index: number) => (
440
442
  <UploadItem uploadPercentage={progress} grid={true} key={index} />
@@ -461,7 +463,7 @@ const FileDrive = (props: IProps) => {
461
463
  </S.GridWrapper>
462
464
  );
463
465
 
464
- const ListTable = () => (
466
+ const listTable = (
465
467
  <TableList tableHeader={Header} hasFixedHeader={true} tableRef={tableRef} className="no-padding">
466
468
  <>
467
469
  {progressItems.map((progress: number, index: number) => (
@@ -483,6 +485,7 @@ const FileDrive = (props: IProps) => {
483
485
  isAllowedToEdit={allowedToEditFile}
484
486
  isAllowedToMove={!isRoot || (isRoot && (folders.length > 0 || isSearching))}
485
487
  isSearching={isSearching}
488
+ hoverCheck={checkState.hoverCheck}
486
489
  />
487
490
  );
488
491
  })}
@@ -681,7 +684,7 @@ const FileDrive = (props: IProps) => {
681
684
  )}
682
685
  {!hasFolders && isRoot && !isSearching && <NewFolderButton />}
683
686
  </S.SectionHeader>
684
- <S.DocumentsWrapper>{isGrid ? <GridList /> : <ListTable />}</S.DocumentsWrapper>
687
+ <S.DocumentsWrapper>{isGrid ? gridList : listTable}</S.DocumentsWrapper>
685
688
  {totalItems === 0 && !hasFolders && (
686
689
  <S.EmptyStateWrapper data-testid="empty-state" margin={!isRoot}>
687
690
  {isSearching ? <EmptyState {...emptySearchStateProps} /> : <EmptyState {...emptyProps} />}
@@ -4,7 +4,16 @@ import { RouteComponentProps } from "react-router-dom";
4
4
  import { withErrorBoundary } from "react-error-boundary";
5
5
 
6
6
  import { IErrorItem, INotification, IRootState, ISavePageParams, ISite, IUserEditing } from "@ax/types";
7
- import { MainWrapper, Loading, ErrorToast, Notification, Modal, ErrorPage } from "@ax/components";
7
+ import {
8
+ MainWrapper,
9
+ Loading,
10
+ ErrorToast,
11
+ Notification,
12
+ Modal,
13
+ ErrorPage,
14
+ CancelScheduleModal,
15
+ ScheduleModal,
16
+ } from "@ax/components";
8
17
  import { pageEditorActions } from "@ax/containers/PageEditor";
9
18
  import { structuredDataActions } from "@ax/containers/StructuredData";
10
19
  import { usersActions } from "@ax/containers/Users";
@@ -13,7 +22,7 @@ import { sitesActions } from "@ax/containers/Sites";
13
22
  import { pageStatus } from "@ax/containers/PageEditor/interfaces";
14
23
  import { RouteLeavingGuard } from "@ax/guards";
15
24
  import { useIsDirty, useModal, usePermission } from "@ax/hooks";
16
- import { getDefaultTheme } from "@ax/helpers";
25
+ import { dateToString, getDefaultTheme } from "@ax/helpers";
17
26
 
18
27
  import Editor from "./Editor";
19
28
  import Preview from "./Preview";
@@ -45,6 +54,7 @@ const GlobalEditor = (props: IProps) => {
45
54
  isNewTranslation,
46
55
  currentSiteErrorPages,
47
56
  getUserCurrentPermissions,
57
+ schedulePublication,
48
58
  } = props;
49
59
 
50
60
  const isAllowedToPublishPages = usePermission("global.globalData.publishUnpublishAllGlobalData");
@@ -61,6 +71,9 @@ const GlobalEditor = (props: IProps) => {
61
71
  const [notification, setNotification] = useState<INotification | null>(null);
62
72
  const { isDirty, setIsDirty, resetDirty } = useIsDirty(editorContent, isNewTranslation);
63
73
  const [errorPagesChecked, setErrorPagesChecked] = useState(false);
74
+ const [scheduleDate, setScheduleDate] = useState({ date: "", time: "" });
75
+ const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
76
+ const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
64
77
  const browserRef = useRef<HTMLDivElement>(null);
65
78
 
66
79
  const isPublished = props.pageStatus === pageStatus.PUBLISHED || props.pageStatus === pageStatus.UPLOAD_PENDING;
@@ -68,7 +81,8 @@ const GlobalEditor = (props: IProps) => {
68
81
  const hasDraft: boolean = editorContent && !!editorContent.haveDraftPage;
69
82
  const isLivePageChanged = editorContent && editorContent.liveChanged;
70
83
  const structuredData = editorContent ? editorContent.structuredData : "";
71
- const isEditLive: boolean = isPublished && hasDraft;
84
+ const isEditLive = isPublished && hasDraft;
85
+ const isScheduled = props.pageStatus === pageStatus.SCHEDULED;
72
86
 
73
87
  const errorNotificationText =
74
88
  "There are some errors on the page so you can not publish yet. Please review them in the error panel.";
@@ -228,6 +242,25 @@ const GlobalEditor = (props: IProps) => {
228
242
  if (isSaved) resetDirty();
229
243
  };
230
244
 
245
+ const handleSchedulePublication = async () => {
246
+ const date = new Date(`${scheduleDate.date} ${scheduleDate.time}`);
247
+ const dateString = dateToString(date, "dd/MM/yyyy HH:mm:ss");
248
+ const saved = await schedulePublication(dateString);
249
+ if (saved) {
250
+ resetDirty();
251
+ toggleScheduleModal();
252
+ }
253
+ };
254
+
255
+ const handleCancelSchedulePublication = async () => {
256
+ const saved = await schedulePublication(null);
257
+ if (saved) {
258
+ setScheduleDate({ date: "", time: "" });
259
+ resetDirty();
260
+ toggleCancelScheduleModal();
261
+ }
262
+ };
263
+
231
264
  const getPublishButton = (status: string) => {
232
265
  switch (status) {
233
266
  case pageStatus.OFFLINE:
@@ -270,6 +303,22 @@ const GlobalEditor = (props: IProps) => {
270
303
 
271
304
  const menuOptions = [];
272
305
 
306
+ if (isAllowedToPublishPages && !isScheduled) {
307
+ menuOptions.push({
308
+ label: "Schedule",
309
+ icon: "calendar",
310
+ action: toggleScheduleModal,
311
+ });
312
+ }
313
+
314
+ if (isAllowedToPublishPages && isScheduled) {
315
+ menuOptions.push({
316
+ label: "Cancel Schedule",
317
+ icon: "cancelEvent",
318
+ action: toggleCancelScheduleModal,
319
+ });
320
+ }
321
+
273
322
  if (isAllowedToEditContentPage) {
274
323
  menuOptions.push({
275
324
  label: "Review",
@@ -418,6 +467,20 @@ const GlobalEditor = (props: IProps) => {
418
467
 
419
468
  const mainUnpublishAction = { title: "Ok", onClick: toggleUnpublishModal };
420
469
 
470
+ const mainScheduleModalAction = {
471
+ title: "Schedule",
472
+ onClick: handleSchedulePublication,
473
+ };
474
+
475
+ const secondaryScheduleModalAction = { title: "Cancel", onClick: toggleScheduleModal };
476
+
477
+ const mainCancelScheduleModalAction = {
478
+ title: "Cancel Schedule",
479
+ onClick: handleCancelSchedulePublication,
480
+ };
481
+
482
+ const secondaryCancelScheduleModalAction = { title: "Back", onClick: toggleCancelScheduleModal };
483
+
421
484
  const tabIcons = isAllowedToEditContentPage
422
485
  ? [
423
486
  { name: "edit", text: "Edit mode" },
@@ -457,6 +520,7 @@ const GlobalEditor = (props: IProps) => {
457
520
  pageStatusActions={pageStatusActions}
458
521
  tabs={tabsPreview}
459
522
  isDirty={isDirty}
523
+ scheduledPublication={editorContent?.publicationScheduled}
460
524
  >
461
525
  {selectedTab === "edit" ? (
462
526
  <>
@@ -531,6 +595,20 @@ const GlobalEditor = (props: IProps) => {
531
595
  </S.ModalContent>
532
596
  )}
533
597
  </Modal>
598
+ <ScheduleModal
599
+ isOpen={isScheduleOpen}
600
+ toggleModal={toggleScheduleModal}
601
+ mainModalAction={mainScheduleModalAction}
602
+ secondaryModalAction={secondaryScheduleModalAction}
603
+ scheduleDate={scheduleDate}
604
+ setScheduleDate={setScheduleDate}
605
+ />
606
+ <CancelScheduleModal
607
+ isOpen={isCancelScheduleOpen}
608
+ toggleModal={toggleCancelScheduleModal}
609
+ mainModalAction={mainCancelScheduleModalAction}
610
+ secondaryModalAction={secondaryCancelScheduleModalAction}
611
+ />
534
612
  </MainWrapper>
535
613
  </>
536
614
  );
@@ -593,6 +671,7 @@ const mapDispatchToProps = {
593
671
  setStructuredDataFilter: structuredDataActions.setFilter,
594
672
  discardDraft: pageEditorActions.discardDraft,
595
673
  getUserCurrentPermissions: usersActions.getUserCurrentPermissions,
674
+ schedulePublication: pageEditorActions.schedulePublication,
596
675
  };
597
676
 
598
677
  interface IPageEditorDispatchProps {
@@ -612,6 +691,7 @@ interface IPageEditorDispatchProps {
612
691
  setStructuredDataFilter(filter: string | null): void;
613
692
  discardDraft(): Promise<void>;
614
693
  getUserCurrentPermissions(): void;
694
+ schedulePublication(date: string | null): Promise<boolean>;
615
695
  }
616
696
 
617
697
  type IProps = IPageEditorStateProps & IPageEditorDispatchProps & RouteComponentProps;
@@ -16,6 +16,17 @@ const NotificationWrapper = styled.div`
16
16
 
17
17
  const ModalContent = styled.div`
18
18
  padding: ${(p) => p.theme.spacing.m};
19
+ p {
20
+ margin-bottom: ${(p) => p.theme.spacing.m};
21
+ }
19
22
  `;
20
23
 
21
- export { Content, NotificationWrapper, ModalContent };
24
+ const ModalFields = styled.div`
25
+ display: flex;
26
+ width: 100%;
27
+ & > div:nth-child(odd) {
28
+ margin-right: ${(p) => p.theme.spacing.m};
29
+ }
30
+ `;
31
+
32
+ export { Content, NotificationWrapper, ModalContent, ModalFields };
@@ -1,22 +1,26 @@
1
1
  import React from "react";
2
2
 
3
- import { CheckField, TableCounter } from "@ax/components";
3
+ import { CheckField, TableCounter, Tooltip } from "@ax/components";
4
4
  import * as S from "./style";
5
5
 
6
- const TableHeader = (props: IProps) => {
7
- const { totalItems, selectAllItems, isScrolling } = props;
6
+ const TableHeader = (props: IProps): JSX.Element => {
7
+ const { totalItems, selectAllItems, isScrolling, checkState, setHoverCheck } = props;
8
+
8
9
  return (
9
10
  <S.TableHeader isScrolling={isScrolling}>
10
11
  <S.CheckHeader>
11
- <CheckField
12
- key="selectAll"
13
- name="selectAll"
14
- value="selectAll"
15
- onChange={selectAllItems}
16
- checked={false}
17
- disabled={false}
18
- error={false}
19
- />
12
+ <Tooltip content="Select All Items" bottom>
13
+ <CheckField
14
+ key="selectAll"
15
+ name="selectAll"
16
+ value="selectAll"
17
+ onChange={selectAllItems}
18
+ checked={checkState.isAllSelected || checkState.hoverCheck}
19
+ disabled={false}
20
+ error={false}
21
+ setHoverCheck={setHoverCheck}
22
+ />
23
+ </Tooltip>
20
24
  </S.CheckHeader>
21
25
  <S.NameHeader>Name</S.NameHeader>
22
26
  <S.TransHeader>Translations</S.TransHeader>
@@ -32,6 +36,8 @@ interface IProps {
32
36
  totalItems: number;
33
37
  isScrolling: boolean;
34
38
  selectAllItems: () => void;
39
+ checkState: Record<string, boolean>;
40
+ setHoverCheck: (state: boolean) => void;
35
41
  }
36
42
 
37
43
  export default TableHeader;