@griddo/ax 11.15.9 → 11.16.0-rc.1

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 (95) hide show
  1. package/package.json +2 -2
  2. package/src/__tests__/components/RestrictionsModal/CurrentAccessList/CurrentAccessList.test.tsx +173 -0
  3. package/src/__tests__/components/RestrictionsModal/OptionsStep/OptionsStep.test.tsx +160 -0
  4. package/src/__tests__/components/RestrictionsModal/RestrictionsModal.test.tsx +134 -0
  5. package/src/__tests__/components/RestrictionsModal/RolesList/RolesList.test.tsx +234 -0
  6. package/src/__tests__/components/RestrictionsModal/UsersList/UsersList.test.tsx +288 -0
  7. package/src/__tests__/hooks/users.test.tsx +382 -0
  8. package/src/api/pages.tsx +24 -1
  9. package/src/api/sites.tsx +14 -14
  10. package/src/api/structuredData.tsx +17 -7
  11. package/src/components/Browser/index.tsx +12 -0
  12. package/src/components/Browser/style.tsx +5 -1
  13. package/src/components/ConfigPanel/Form/index.tsx +11 -10
  14. package/src/components/ContentItem/index.tsx +31 -0
  15. package/src/components/ContentItem/style.tsx +43 -0
  16. package/src/components/Fields/ImageField/index.tsx +2 -1
  17. package/src/components/Fields/RadioField/index.tsx +3 -3
  18. package/src/components/Fields/RadioField/style.tsx +1 -1
  19. package/src/components/FloatingNote/index.tsx +12 -1
  20. package/src/components/FloatingNote/style.tsx +2 -1
  21. package/src/components/Modal/style.tsx +11 -0
  22. package/src/components/PageFinder/SelectionListItem/index.tsx +27 -17
  23. package/src/components/PageFinder/index.tsx +65 -28
  24. package/src/components/PageFinder/style.tsx +0 -1
  25. package/src/components/RestrictionsModal/CurrentAccessList/index.tsx +74 -0
  26. package/src/components/RestrictionsModal/CurrentAccessList/style.tsx +155 -0
  27. package/src/components/RestrictionsModal/OptionsStep/index.tsx +55 -0
  28. package/src/components/RestrictionsModal/OptionsStep/style.tsx +29 -0
  29. package/src/components/RestrictionsModal/RolesList/RoleItem/index.tsx +54 -0
  30. package/src/components/RestrictionsModal/RolesList/RoleItem/style.tsx +34 -0
  31. package/src/components/RestrictionsModal/RolesList/index.tsx +107 -0
  32. package/src/components/RestrictionsModal/RolesList/style.tsx +76 -0
  33. package/src/components/RestrictionsModal/UsersList/UserItem/index.tsx +78 -0
  34. package/src/components/RestrictionsModal/UsersList/UserItem/style.tsx +73 -0
  35. package/src/components/RestrictionsModal/UsersList/index.tsx +151 -0
  36. package/src/components/RestrictionsModal/UsersList/style.tsx +55 -0
  37. package/src/components/RestrictionsModal/index.tsx +181 -0
  38. package/src/components/RestrictionsModal/style.tsx +31 -0
  39. package/src/components/RestrictionsModal/utils.tsx +24 -0
  40. package/src/components/ScheduleModal/index.tsx +32 -8
  41. package/src/components/SelectionComponent/index.tsx +65 -0
  42. package/src/components/SelectionComponent/style.tsx +69 -0
  43. package/src/components/TableFilters/RoleFilter/index.tsx +3 -3
  44. package/src/components/TableList/TableItem/style.tsx +7 -13
  45. package/src/components/Tabs/index.tsx +2 -1
  46. package/src/components/UserRolesAndSites/index.tsx +2 -2
  47. package/src/components/index.tsx +6 -0
  48. package/src/containers/PageEditor/actions.tsx +43 -0
  49. package/src/containers/PageEditor/constants.tsx +1 -0
  50. package/src/containers/PageEditor/interfaces.tsx +26 -19
  51. package/src/containers/PageEditor/reducer.tsx +32 -19
  52. package/src/containers/Users/reducer.tsx +1 -0
  53. package/src/helpers/forms.tsx +3 -1
  54. package/src/hooks/index.tsx +2 -1
  55. package/src/hooks/users.tsx +154 -14
  56. package/src/modules/ActivityLog/LogFilters/UserFilter/index.tsx +11 -12
  57. package/src/modules/Content/PageItem/index.tsx +17 -10
  58. package/src/modules/Content/index.tsx +1 -3
  59. package/src/modules/GlobalEditor/Editor/index.tsx +6 -0
  60. package/src/modules/GlobalEditor/PageBrowser/index.tsx +6 -0
  61. package/src/modules/GlobalEditor/Preview/index.tsx +4 -2
  62. package/src/modules/GlobalEditor/index.tsx +72 -45
  63. package/src/modules/Navigation/Menus/List/Table/ConfigPanel/Form/index.tsx +3 -5
  64. package/src/modules/Navigation/Menus/List/Table/ConfigPanel/index.tsx +4 -4
  65. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/ConnectedField/index.tsx +2 -3
  66. package/src/modules/Navigation/Menus/List/Table/SidePanel/Form/index.tsx +3 -4
  67. package/src/modules/Navigation/Menus/List/Table/SidePanel/index.tsx +5 -6
  68. package/src/modules/PageEditor/Editor/index.tsx +6 -0
  69. package/src/modules/PageEditor/PageBrowser/index.tsx +6 -0
  70. package/src/modules/PageEditor/Preview/index.tsx +9 -2
  71. package/src/modules/PageEditor/atoms.tsx +110 -3
  72. package/src/modules/PageEditor/index.tsx +76 -95
  73. package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +1 -3
  74. package/src/modules/Sites/SitesList/GridView/GridSiteItem/style.tsx +19 -2
  75. package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +2 -2
  76. package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +2 -2
  77. package/src/modules/StructuredData/Form/ConnectedField/index.tsx +2 -2
  78. package/src/modules/StructuredData/Form/index.tsx +120 -75
  79. package/src/modules/StructuredData/Form/style.tsx +7 -1
  80. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +15 -8
  81. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +15 -9
  82. package/src/modules/StructuredData/{StructuredDataList/StructuredDataItem/utils.tsx → utils.tsx} +16 -2
  83. package/src/modules/Users/Profile/atoms.tsx +62 -4
  84. package/src/modules/Users/Profile/index.tsx +83 -4
  85. package/src/modules/Users/Profile/style.tsx +60 -0
  86. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +3 -5
  87. package/src/modules/Users/UserCreate/SiteItem/index.tsx +2 -2
  88. package/src/modules/Users/UserCreate/atoms.tsx +69 -0
  89. package/src/modules/Users/UserCreate/index.tsx +149 -70
  90. package/src/modules/Users/UserCreate/style.tsx +24 -1
  91. package/src/modules/Users/UserForm/index.tsx +260 -146
  92. package/src/modules/Users/UserForm/style.tsx +13 -1
  93. package/src/modules/Users/UserList/UserItem/index.tsx +3 -3
  94. package/src/types/index.tsx +19 -6
  95. package/src/types/logs.tsx +1 -1
@@ -15,6 +15,7 @@ import {
15
15
  Notification,
16
16
  OcassionalToast,
17
17
  RestoreModal,
18
+ RestrictionsModal,
18
19
  ScheduleModal,
19
20
  } from "@ax/components";
20
21
  import { appActions } from "@ax/containers/App";
@@ -25,7 +26,7 @@ import { structuredDataActions } from "@ax/containers/StructuredData";
25
26
  import { usersActions } from "@ax/containers/Users";
26
27
  import { RouteLeavingGuard } from "@ax/guards";
27
28
  import { dateToString, getDefaultTheme } from "@ax/helpers";
28
- import { useIsDirty, useModals, usePermissions } from "@ax/hooks";
29
+ import { useIsDirty, useModals, usePermissionsForPage } from "@ax/hooks";
29
30
  import type {
30
31
  HeadingFilter,
31
32
  IErrorItem,
@@ -36,6 +37,7 @@ import type {
36
37
  ISavePageParams,
37
38
  ISite,
38
39
  IUserEditing,
40
+ PageAccessGrantsSummary,
39
41
  } from "@ax/types";
40
42
 
41
43
  import { DeleteModal } from "./atoms";
@@ -73,14 +75,23 @@ const GlobalEditor = (props: IProps) => {
73
75
  restorePage,
74
76
  updateEditorContent,
75
77
  schemaVersion,
78
+ updatePageAccessGrants,
79
+ accessGrants,
76
80
  } = props;
77
81
 
78
- const isAllowedTo = usePermissions({
79
- publishPages: "global.globalData.publishUnpublishAllGlobalData",
80
- createPages: "global.globalData.createAllGlobalData",
81
- deletePages: "global.globalData.deleteAllGlobalData",
82
- editContentPages: "global.globalData.editAllGlobalData",
83
- });
82
+ const isAllowedTo = usePermissionsForPage(
83
+ "global",
84
+ pageID,
85
+ {
86
+ publishPages: "global.globalData.publishUnpublishAllGlobalData",
87
+ createPages: "global.globalData.createAllGlobalData",
88
+ deletePages: "global.globalData.deleteAllGlobalData",
89
+ editContentPages: "global.globalData.editAllGlobalData",
90
+ setPagePermissions: "global.globalData.setPermissionPerPage",
91
+ },
92
+ accessGrants,
93
+ pageLanguages,
94
+ );
84
95
 
85
96
  const defaultTab = isAllowedTo.editContentPages ? "edit" : "view";
86
97
 
@@ -108,6 +119,7 @@ const GlobalEditor = (props: IProps) => {
108
119
  "headingsPreview",
109
120
  "keywordsPreview",
110
121
  "dirtyNavigate",
122
+ "userAccess",
111
123
  "delete",
112
124
  ]);
113
125
 
@@ -158,6 +170,10 @@ const GlobalEditor = (props: IProps) => {
158
170
  return () => clearInterval(interval);
159
171
  }, []);
160
172
 
173
+ useEffect(() => {
174
+ setSelectedTab(defaultTab);
175
+ }, [defaultTab]);
176
+
161
177
  // biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
162
178
  useEffect(() => {
163
179
  resetDirty();
@@ -347,6 +363,14 @@ const GlobalEditor = (props: IProps) => {
347
363
 
348
364
  const menuOptions = [];
349
365
 
366
+ if (isAllowedTo.setPagePermissions) {
367
+ menuOptions.push({
368
+ label: "Page user access",
369
+ icon: "users",
370
+ action: () => toggleModal("userAccess"),
371
+ });
372
+ }
373
+
350
374
  if (isAllowedTo.publishPages && !isScheduled && !isPublished && props.pageStatus !== pageStatus.OFFLINE_PENDING) {
351
375
  menuOptions.push({
352
376
  label: "Schedule",
@@ -409,6 +433,7 @@ const GlobalEditor = (props: IProps) => {
409
433
  };
410
434
 
411
435
  const isEditable = editorContent?.editable;
436
+ const isRestricted = !!(accessGrants.users.length || accessGrants.roles.length);
412
437
 
413
438
  const handleSavePage = async (setToast?: (msg: string) => void) => {
414
439
  const { savePage } = props;
@@ -542,26 +567,6 @@ const GlobalEditor = (props: IProps) => {
542
567
 
543
568
  const mainUnpublishAction = { title: "Ok", onClick: () => toggleModal("unpublish") };
544
569
 
545
- const mainScheduleModalAction = {
546
- title: "Schedule",
547
- onClick: handleSchedulePublication,
548
- };
549
-
550
- const secondaryScheduleModalAction = {
551
- title: "Cancel",
552
- onClick: () => toggleModal("schedule"),
553
- };
554
-
555
- const mainCancelScheduleModalAction = {
556
- title: "Cancel Schedule",
557
- onClick: handleCancelSchedulePublication,
558
- };
559
-
560
- const secondaryCancelScheduleModalAction = {
561
- title: "Back",
562
- onClick: () => toggleModal("cancelSchedule"),
563
- };
564
-
565
570
  const tabIcons = isAllowedTo.editContentPages
566
571
  ? [
567
572
  { name: "edit", text: "Edit mode" },
@@ -646,6 +651,13 @@ const GlobalEditor = (props: IProps) => {
646
651
  setTab(defaultContentTab);
647
652
  };
648
653
 
654
+ const handleUpdateAccessGrants = async (accessGrants: PageAccessGrantsSummary) => {
655
+ const isUpdated = await updatePageAccessGrants({ siteID: "global", pageID, accessGrants });
656
+ if (isUpdated) {
657
+ toggleModal("userAccess");
658
+ }
659
+ };
660
+
649
661
  return isLoading ? (
650
662
  <Loading />
651
663
  ) : (
@@ -725,12 +737,14 @@ const GlobalEditor = (props: IProps) => {
725
737
  toggleKeywordsPreview={handleToggleKeywordsEditor}
726
738
  isKeywordsPreviewOpen={isOpen("keywordsPreview")}
727
739
  keywordsFilter={keywordsFilter}
740
+ isRestrictedPage={isRestricted}
741
+ onRestrictionsClick={isAllowedTo.setPagePermissions ? () => toggleModal("userAccess") : undefined}
728
742
  />
729
743
  </S.Content>
730
744
  </>
731
745
  ) : (
732
746
  <>
733
- <Preview theme={theme} />
747
+ <Preview theme={theme} isRestrictedPage={isRestricted} />
734
748
  {toastMsg?.length && <OcassionalToast icon="warning" message={toastMsg} />}
735
749
  </>
736
750
  )}
@@ -777,16 +791,14 @@ const GlobalEditor = (props: IProps) => {
777
791
  <ScheduleModal
778
792
  isOpen={isOpen("schedule")}
779
793
  toggleModal={() => toggleModal("schedule")}
780
- mainModalAction={mainScheduleModalAction}
781
- secondaryModalAction={secondaryScheduleModalAction}
794
+ onSchedule={handleSchedulePublication}
782
795
  scheduleDate={scheduleDate}
783
796
  setScheduleDate={setScheduleDate}
784
797
  />
785
798
  <CancelScheduleModal
786
799
  isOpen={isOpen("cancelSchedule")}
787
800
  toggleModal={() => toggleModal("cancelSchedule")}
788
- mainModalAction={mainCancelScheduleModalAction}
789
- secondaryModalAction={secondaryCancelScheduleModalAction}
801
+ onCancelSchedule={handleCancelSchedulePublication}
790
802
  />
791
803
  <RestoreModal
792
804
  isOpen={isOpen("restore")}
@@ -814,20 +826,27 @@ const GlobalEditor = (props: IProps) => {
814
826
  addKeywords={handleAddKeywords}
815
827
  deleteKeyword={handledeleteKeyword}
816
828
  />
829
+ <RestrictionsModal
830
+ isOpen={isOpen("userAccess")}
831
+ toggleModal={() => toggleModal("userAccess")}
832
+ currentAccessGrants={accessGrants}
833
+ onUpdateAccessGrants={handleUpdateAccessGrants}
834
+ isSaving={isSaving}
835
+ />
836
+ <Modal
837
+ isOpen={isOpen("dirtyNavigate")}
838
+ hide={handleCancelDirtyNavigation}
839
+ size="S"
840
+ title="Unsaved changes"
841
+ mainAction={{ title: "Yes, discard changes", onClick: handleConfirmDirtyNavigation }}
842
+ secondaryAction={{ title: "Cancel", onClick: handleCancelDirtyNavigation }}
843
+ >
844
+ <S.ModalContent>
845
+ Some content <strong>is not saved</strong> on this page. If you exit without saving it, it will be lost. Do
846
+ you want to discard your changes?
847
+ </S.ModalContent>
848
+ </Modal>
817
849
  </MainWrapper>
818
- <Modal
819
- isOpen={isOpen("dirtyNavigate")}
820
- hide={handleCancelDirtyNavigation}
821
- size="S"
822
- title="Unsaved changes"
823
- mainAction={{ title: "Yes, discard changes", onClick: handleConfirmDirtyNavigation }}
824
- secondaryAction={{ title: "Cancel", onClick: handleCancelDirtyNavigation }}
825
- >
826
- <S.ModalContent>
827
- Some content <strong>is not saved</strong> on this page. If you exit without saving it, it will be lost. Do
828
- you want to discard your changes?
829
- </S.ModalContent>
830
- </Modal>
831
850
  </>
832
851
  );
833
852
  };
@@ -851,6 +870,7 @@ const mapStateToProps = (state: IRootState): IPageEditorStateProps => ({
851
870
  currentSiteErrorPages: state.sites.currentSiteErrorPages,
852
871
  skipReviewOnPublish: state.app.globalSettings.skipReviewOnPublish,
853
872
  schemaVersion: state.structuredData.schemaVersion,
873
+ accessGrants: state.pageEditor.accessGrants,
854
874
  });
855
875
 
856
876
  interface IPageEditorStateProps {
@@ -872,6 +892,7 @@ interface IPageEditorStateProps {
872
892
  currentSiteErrorPages: number[];
873
893
  skipReviewOnPublish?: boolean;
874
894
  schemaVersion: string;
895
+ accessGrants: PageAccessGrantsSummary;
875
896
  }
876
897
 
877
898
  const mapDispatchToProps = {
@@ -895,6 +916,7 @@ const mapDispatchToProps = {
895
916
  schedulePublication: pageEditorActions.schedulePublication,
896
917
  restorePage: pageEditorActions.restorePage,
897
918
  updateEditorContent: pageEditorActions.updateEditorContent,
919
+ updatePageAccessGrants: pageEditorActions.updatePageAccessGrants,
898
920
  };
899
921
 
900
922
  interface IPageEditorDispatchProps {
@@ -923,6 +945,11 @@ interface IPageEditorDispatchProps {
923
945
  schedulePublication(date: string | null, isDraft: boolean): Promise<boolean>;
924
946
  restorePage(id: number | number[]): Promise<boolean>;
925
947
  updateEditorContent(selectedEditorID: number, key: string, value: any): void;
948
+ updatePageAccessGrants(data: {
949
+ siteID: number | "global";
950
+ pageID: number | null;
951
+ accessGrants: PageAccessGrantsSummary;
952
+ }): Promise<boolean>;
926
953
  }
927
954
 
928
955
  type IProps = IPageEditorStateProps & IPageEditorDispatchProps & RouteComponentProps;
@@ -1,8 +1,6 @@
1
- import React from "react";
2
-
3
1
  import { FieldsBehavior } from "@ax/components";
4
2
  import { getModuleStyles } from "@ax/helpers";
5
- import { IMenuItemConfig } from "@ax/types";
3
+ import type { IMenuItemConfig } from "@ax/types";
6
4
 
7
5
  import * as S from "./style";
8
6
 
@@ -25,8 +23,8 @@ const Form = (props: IProps): JSX.Element => {
25
23
  text: "You can choose how to view the children of this section in navigation modules.",
26
24
  };
27
25
 
28
- const headerValue = form && form.headerStyle ? form.headerStyle : headerOptions[0] ? headerOptions[0].value : null;
29
- const footerValue = form && form.footerStyle ? form.footerStyle : footerOptions[0] ? footerOptions[0].value : null;
26
+ const headerValue = form?.headerStyle ? form.headerStyle : headerOptions[0] ? headerOptions[0].value : null;
27
+ const footerValue = form?.footerStyle ? form.footerStyle : footerOptions[0] ? footerOptions[0].value : null;
30
28
 
31
29
  return (
32
30
  <>
@@ -1,9 +1,9 @@
1
- import React, { useState } from "react";
2
-
1
+ import { useState } from "react";
3
2
  import { connect } from "react-redux";
3
+
4
+ import { Button, FloatingPanel } from "@ax/components";
4
5
  import { menuActions } from "@ax/containers/Navigation";
5
- import { IRootState, IMenuItem, IMenuItemConfig } from "@ax/types";
6
- import { FloatingPanel, Button } from "@ax/components";
6
+ import type { IMenuItem, IMenuItemConfig, IRootState } from "@ax/types";
7
7
 
8
8
  import Form from "./Form";
9
9
 
@@ -1,9 +1,8 @@
1
- import React from "react";
2
1
  import { connect } from "react-redux";
3
2
 
4
- import { IRootState } from "@ax/types";
5
- import { menuActions } from "@ax/containers/Navigation/Menu";
6
3
  import { FieldsBehavior } from "@ax/components";
4
+ import { menuActions } from "@ax/containers/Navigation/Menu";
5
+ import type { IRootState } from "@ax/types";
7
6
 
8
7
  const ConnectedField = (props: IProps) => {
9
8
  const { form, type, name, updateFormValue, setIsGalleryOpened } = props;
@@ -1,11 +1,10 @@
1
- import React from "react";
2
1
  import { connect } from "react-redux";
3
2
 
3
+ import { FieldsBehavior } from "@ax/components";
4
4
  import { menuActions } from "@ax/containers/Navigation";
5
5
  import { sitesActions } from "@ax/containers/Sites";
6
- import { FieldsBehavior } from "@ax/components";
7
- import { IRootState, IMenuForm, ISchemaField } from "@ax/types";
8
6
  import { getMenuItems } from "@ax/helpers";
7
+ import type { IMenuForm, IRootState, ISchemaField } from "@ax/types";
9
8
 
10
9
  import ConnectedField from "./ConnectedField";
11
10
 
@@ -34,7 +33,7 @@ const Form = (props: IProps): JSX.Element => {
34
33
  const typeLink = type ? type : "link";
35
34
 
36
35
  const menuItems = getMenuItems(typeLink);
37
- const schemaFields = menuItems && menuItems.fields ? menuItems.fields : [];
36
+ const schemaFields = menuItems?.fields ? menuItems.fields : [];
38
37
 
39
38
  const generateFields = (fields: ISchemaField[]) =>
40
39
  fields.map((field: ISchemaField) => (
@@ -1,13 +1,12 @@
1
- import React, { useState } from "react";
2
-
1
+ import { useState } from "react";
3
2
  import { connect } from "react-redux";
4
- import { menuActions } from "@ax/containers/Navigation";
5
3
 
6
- import { FloatingPanel, Button } from "@ax/components";
4
+ import { Button, FloatingPanel } from "@ax/components";
5
+ import { menuActions } from "@ax/containers/Navigation";
6
+ import type { IMenuForm, IMenuItem, IRootState } from "@ax/types";
7
7
 
8
8
  import Form from "./Form";
9
9
 
10
- import { IRootState, IMenuItem, IMenuForm } from "@ax/types";
11
10
  import * as S from "./style";
12
11
 
13
12
  const SidePanel = (props: IProps): JSX.Element => {
@@ -40,7 +39,7 @@ const SidePanel = (props: IProps): JSX.Element => {
40
39
  };
41
40
 
42
41
  const editItemAction = () => {
43
- menuItem = { ...menuItem, editorID: item && item.editorID };
42
+ menuItem = { ...menuItem, editorID: item?.editorID };
44
43
  updateMenuItem(menuItem);
45
44
  toggleModal();
46
45
  };
@@ -61,6 +61,8 @@ const Editor = (props: IProps) => {
61
61
  keywordsFilter,
62
62
  isDirty,
63
63
  onNavigateWithDirty,
64
+ isRestrictedPage,
65
+ onRestrictionsClick,
64
66
  } = props;
65
67
 
66
68
  if (!site) {
@@ -102,6 +104,8 @@ const Editor = (props: IProps) => {
102
104
  toggleKeywordsPreview={toggleKeywordsPreview}
103
105
  isKeywordsPreviewOpen={isKeywordsPreviewOpen}
104
106
  keywordsFilter={keywordsFilter}
107
+ isRestrictedPage={isRestrictedPage}
108
+ onRestrictionsClick={onRestrictionsClick}
105
109
  />
106
110
  }
107
111
  rightPanel={
@@ -183,6 +187,8 @@ interface IPageBrowserDispatchProps {
183
187
  keywordsFilter: string[];
184
188
  isDirty?: boolean;
185
189
  onNavigateWithDirty?: (navigateCallback: () => void) => void;
190
+ isRestrictedPage?: boolean;
191
+ onRestrictionsClick?(): void;
186
192
  }
187
193
 
188
194
  type IProps = IEditorStateProps & IPageBrowserDispatchProps;
@@ -26,6 +26,8 @@ const PageBrowser = (props: IProps) => {
26
26
  setScrollEditorID,
27
27
  toggleHeadingsPreview,
28
28
  toggleKeywordsPreview,
29
+ isRestrictedPage,
30
+ onRestrictionsClick,
29
31
  } = props;
30
32
 
31
33
  if (!currentSiteInfo) {
@@ -70,6 +72,8 @@ const PageBrowser = (props: IProps) => {
70
72
  toggleKeywordsPreview={toggleKeywordsPreview}
71
73
  headingFilter={headingsFilter}
72
74
  keywordsFilter={keywordsFilter}
75
+ isRestrictedPage={isRestrictedPage}
76
+ onRestrictionsClick={onRestrictionsClick}
73
77
  />
74
78
  );
75
79
  };
@@ -100,6 +104,8 @@ interface IPageBrowserDispatchProps {
100
104
  setScrollEditorID(editorID: number | null): void;
101
105
  toggleHeadingsPreview?(): void;
102
106
  toggleKeywordsPreview?(): void;
107
+ isRestrictedPage?: boolean;
108
+ onRestrictionsClick?(): void;
103
109
  }
104
110
 
105
111
  type IProps = IPageBrowserStateProps & IPageBrowserDispatchProps;
@@ -1,12 +1,19 @@
1
1
  import PageBrowser from "../PageBrowser";
2
+
2
3
  import * as S from "./style";
3
4
 
4
- const PreviewBrowser = () => {
5
+ const PreviewBrowser = (props: IPreviewProps) => {
6
+ const { isRestrictedPage } = props;
7
+
5
8
  return (
6
9
  <S.BrowserWrapper>
7
- <PageBrowser isTemplateActivated={true} isReadOnly={true} isPreview={true} />
10
+ <PageBrowser isTemplateActivated={true} isReadOnly={true} isPreview={true} isRestrictedPage={isRestrictedPage} />
8
11
  </S.BrowserWrapper>
9
12
  );
10
13
  };
11
14
 
15
+ interface IPreviewProps {
16
+ isRestrictedPage?: boolean;
17
+ }
18
+
12
19
  export default PreviewBrowser;
@@ -1,10 +1,49 @@
1
- import React from "react";
1
+ import type React from "react";
2
2
 
3
+ import { FieldsBehavior, Modal } from "@ax/components";
3
4
  import type { IModal } from "@ax/types";
4
- import { Modal, FieldsBehavior } from "@ax/components";
5
5
 
6
6
  import * as S from "./style";
7
7
 
8
+ const DirtyNavigateModal = (props: IDirtyNavigateModal): JSX.Element => {
9
+ const { isOpen, onConfirm, onCancel } = props;
10
+
11
+ const mainAction = {
12
+ title: "Yes, discard changes",
13
+ onClick: onConfirm,
14
+ };
15
+
16
+ const secondaryAction = {
17
+ title: "Cancel",
18
+ onClick: onCancel,
19
+ };
20
+
21
+ return (
22
+ <Modal
23
+ isOpen={isOpen}
24
+ hide={onCancel}
25
+ size="S"
26
+ title="Unsaved changes"
27
+ mainAction={mainAction}
28
+ secondaryAction={secondaryAction}
29
+ height="auto"
30
+ >
31
+ <S.ModalContent>
32
+ <>
33
+ Some content <strong>is not saved</strong> on this page. If you exit without saving it, it will be lost. Do
34
+ you want to discard your changes?
35
+ </>
36
+ </S.ModalContent>
37
+ </Modal>
38
+ );
39
+ };
40
+
41
+ interface IDirtyNavigateModal {
42
+ isOpen: boolean;
43
+ onConfirm: () => void;
44
+ onCancel: () => void;
45
+ }
46
+
8
47
  const DeleteModal = (props: IDeleteModal): JSX.Element => {
9
48
  const { isOpen, toggleModal, isTranslated, deleteAllVersions, setDeleteAllVersions, onDelete, isDeleting } = props;
10
49
 
@@ -74,4 +113,72 @@ interface IDeleteModal extends IModal {
74
113
  isDeleting?: boolean;
75
114
  }
76
115
 
77
- export { DeleteModal };
116
+ const UserEditingModal = (props: IUserEditingModal): JSX.Element => {
117
+ const { isOpen, toggleModal, userEditing, onBack } = props;
118
+
119
+ const mainAction = {
120
+ title: "Preview Page",
121
+ onClick: () => toggleModal(),
122
+ };
123
+
124
+ const secondaryAction = {
125
+ title: "Ok, go back",
126
+ onClick: onBack,
127
+ };
128
+
129
+ return (
130
+ <Modal
131
+ isOpen={isOpen}
132
+ hide={() => toggleModal()}
133
+ size="S"
134
+ title="This page is currently being edited"
135
+ mainAction={mainAction}
136
+ secondaryAction={secondaryAction}
137
+ >
138
+ {isOpen && (
139
+ <S.ModalContent>
140
+ <p>
141
+ <strong>{userEditing?.name}</strong> is currently working on this page. You can preview the page but{" "}
142
+ <strong>you cannot make changes to it</strong> until {userEditing?.name} leaves the page.
143
+ </p>
144
+ </S.ModalContent>
145
+ )}
146
+ </Modal>
147
+ );
148
+ };
149
+
150
+ interface IUserEditingModal {
151
+ isOpen: boolean;
152
+ toggleModal: () => void;
153
+ userEditing?: { name: string; id: number } | null;
154
+ onBack: () => void;
155
+ }
156
+
157
+ const UnpublishModal = (props: IUnpublishModal): JSX.Element => {
158
+ const { isOpen, toggleModal } = props;
159
+
160
+ const mainAction = {
161
+ title: "Ok",
162
+ onClick: () => toggleModal(),
163
+ };
164
+
165
+ return (
166
+ <Modal isOpen={isOpen} hide={() => toggleModal()} size="S" title="Unpublish Modified Page" mainAction={mainAction}>
167
+ {isOpen && (
168
+ <S.ModalContent>
169
+ <p>
170
+ There are some saved changes that are not published on this page. To Unpublish this page,{" "}
171
+ <strong>you must publish these changes or discard them.</strong>
172
+ </p>
173
+ </S.ModalContent>
174
+ )}
175
+ </Modal>
176
+ );
177
+ };
178
+
179
+ interface IUnpublishModal {
180
+ isOpen: boolean;
181
+ toggleModal: () => void;
182
+ }
183
+
184
+ export { UnpublishModal, UserEditingModal, DeleteModal, DirtyNavigateModal };