@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
@@ -1,6 +1,7 @@
1
- import { useCallback, useEffect, useState } from "react";
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
+ import { sites as sitesApi } from "@ax/api";
4
5
  import {
5
6
  ErrorToast,
6
7
  FieldsBehavior,
@@ -15,12 +16,13 @@ import { appActions } from "@ax/containers/App";
15
16
  import { sitesActions } from "@ax/containers/Sites";
16
17
  import { usersActions } from "@ax/containers/Users";
17
18
  import { RouteLeavingGuard } from "@ax/guards";
19
+ import { isReqOk } from "@ax/helpers";
18
20
  import { useModals, useShouldBeSaved, useURLSearchParam } from "@ax/hooks";
19
- import type { IImage, IRole, IRootState, ISite, IUser } from "@ax/types";
21
+ import type { IImage, ILanguage, IPage, IRole, IRootState, ISite, IUser } from "@ax/types";
20
22
 
21
23
  import { timezones } from "../../Settings/Globals/constants";
22
24
  import { PasswordModal } from "../UserForm/atoms";
23
- import { LinkDeviceModal } from "./atoms";
25
+ import { LinkDeviceModal, ProfilePagePermissionsList } from "./atoms";
24
26
 
25
27
  import * as S from "./style";
26
28
 
@@ -32,7 +34,20 @@ const PROFILE_TABS = {
32
34
  const PROFILE_TAB_LIST = [PROFILE_TABS.PROFILE, PROFILE_TABS.PERMISSIONS] as const;
33
35
 
34
36
  const Profile = (props: IProps) => {
35
- const { user, updateUser, isSaving, isLoading, getSites, token, roles, sites, setHistoryPush } = props;
37
+ const {
38
+ user,
39
+ updateUser,
40
+ isSaving,
41
+ isLoading,
42
+ getSites,
43
+ token,
44
+ roles,
45
+ sites,
46
+ siteLanguages,
47
+ globalLanguages,
48
+ currentSiteInfo,
49
+ setHistoryPush,
50
+ } = props;
36
51
 
37
52
  if (!user) {
38
53
  throw new Error(`ERROR: User reached Profile with null user`);
@@ -46,15 +61,63 @@ const Profile = (props: IProps) => {
46
61
 
47
62
  const [form, setForm] = useState<IUser>(initForm);
48
63
  const [activeTab, setActiveTab] = useState<(typeof PROFILE_TABS)[keyof typeof PROFILE_TABS]>(PROFILE_TABS.PROFILE);
64
+ const [selectedPages, setSelectedPages] = useState<IPage[]>([]);
65
+ const pagesHeaderRef = useRef<HTMLDivElement>(null);
49
66
  const { isOpen, toggleModal } = useModals(["linkDevice", "password"]);
50
67
  const { isDirty, setIsDirty } = useShouldBeSaved(form);
51
68
 
69
+ const defaultLanguage = currentSiteInfo
70
+ ? siteLanguages.find((lang) => lang.isDefault)
71
+ : globalLanguages.find((lang) => lang.isDefault);
72
+
73
+ const scrollToPagesHeader = () => {
74
+ pagesHeaderRef.current?.scrollIntoView({ behavior: "smooth", block: "start" });
75
+ };
76
+
52
77
  useEffect(() => {
53
78
  if (isUserInit) {
54
79
  getSites();
55
80
  }
56
81
  }, [isUserInit, getSites]);
57
82
 
83
+ // biome-ignore lint/correctness/useExhaustiveDependencies: runs on mount only
84
+ useEffect(() => {
85
+ const fetchPages = async () => {
86
+ if (!user.pagePermissions || user.pagePermissions.length === 0) {
87
+ setSelectedPages([]);
88
+ return;
89
+ }
90
+
91
+ const requests = user.pagePermissions
92
+ .filter((p) => p.pageIds.length > 0)
93
+ .map((pagePermission) =>
94
+ sitesApi
95
+ .getSitePages({
96
+ siteID: pagePermission.siteId,
97
+ page: 1,
98
+ itemsPerPage: 50,
99
+ deleted: false,
100
+ filterPages: pagePermission.pageIds,
101
+ lang: defaultLanguage?.id,
102
+ ignoreLang: true,
103
+ })
104
+ .catch((error) => {
105
+ console.error("Failed to fetch page permissions", error);
106
+ return null;
107
+ }),
108
+ );
109
+
110
+ const responses = await Promise.all(requests);
111
+ const allPages = responses
112
+ .filter((response) => response && isReqOk(response.status))
113
+ .flatMap((response) => response?.data?.items || []);
114
+
115
+ setSelectedPages(allPages);
116
+ };
117
+
118
+ fetchPages();
119
+ }, []);
120
+
58
121
  const { username, name, email, image, company, position, timezone, isSuperAdmin } = form;
59
122
 
60
123
  const handleNameChange = useCallback((value: string) => setForm((prev) => ({ ...prev, name: value })), []);
@@ -203,6 +266,16 @@ const Profile = (props: IProps) => {
203
266
  roles={roles}
204
267
  showRows={false}
205
268
  />
269
+ {selectedPages.length > 0 && (
270
+ <>
271
+ <S.PagesHeader ref={pagesHeaderRef}>ACCESS TO PAGES</S.PagesHeader>
272
+ <ProfilePagePermissionsList
273
+ pages={selectedPages}
274
+ testId="profile-pages-list"
275
+ onPageChange={scrollToPagesHeader}
276
+ />
277
+ </>
278
+ )}
206
279
  </S.UserPermissions>
207
280
  )}
208
281
  </S.Content>
@@ -220,6 +293,9 @@ const mapStateToProps = (state: IRootState) => ({
220
293
  token: state.app.token,
221
294
  roles: state.users.roles,
222
295
  sites: state.sites.allSites,
296
+ siteLanguages: state.sites.currentSiteLanguages,
297
+ globalLanguages: state.app.globalLangs,
298
+ currentSiteInfo: state.sites.currentSiteInfo,
223
299
  });
224
300
 
225
301
  interface IDispatchProps {
@@ -241,6 +317,9 @@ interface IProfileProps {
241
317
  token: string;
242
318
  roles: IRole[];
243
319
  sites: ISite[];
320
+ siteLanguages: ILanguage[];
321
+ globalLanguages: ILanguage[];
322
+ currentSiteInfo: ISite | null;
244
323
  }
245
324
 
246
325
  type IProps = IProfileProps & IDispatchProps;
@@ -79,6 +79,57 @@ const UserPermissions = styled.div`
79
79
  padding-top: ${(p) => p.theme.spacing.s};
80
80
  `;
81
81
 
82
+ const PagesHeader = styled.div`
83
+ ${(p) => p.theme.textStyle.headingXS};
84
+ margin-top: ${(p) => p.theme.spacing.m};
85
+ `;
86
+
87
+ const PageList = styled.ul`
88
+ padding-top: ${(p) => p.theme.spacing.s};
89
+ padding-bottom: ${(p) => p.theme.spacing.m};
90
+ `;
91
+
92
+ const PageListItem = styled.li`
93
+ display: flex;
94
+ width: 100%;
95
+ padding: ${(p) => p.theme.spacing.s};
96
+ background-color: ${(p) => p.theme.color.uiBarBackground};
97
+ border: 1px solid ${(p) => p.theme.color.uiLine};
98
+ border-radius: ${(p) => p.theme.radii.s};
99
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
100
+ `;
101
+
102
+ const PageItemContent = styled.div`
103
+ width: 100%;
104
+ `;
105
+
106
+ const PageItemHeader = styled.div`
107
+ display: flex;
108
+ margin-bottom: ${(p) => p.theme.spacing.xxs};
109
+ align-items: center;
110
+ gap: ${(p) => p.theme.spacing.s};
111
+ `;
112
+
113
+ const PageItemType = styled.div`
114
+ ${(p) => p.theme.textStyle.headingXXS};
115
+ color: ${(p) => p.theme.color.textLowEmphasis};
116
+ `;
117
+
118
+ const PageItemTitle = styled.div`
119
+ ${(p) => p.theme.textStyle.fieldContent};
120
+ color: ${(p) => p.theme.color.textHighEmphasis};
121
+ `;
122
+
123
+ const PageItemSiteTag = styled.div`
124
+ margin-left: auto;
125
+ `;
126
+
127
+ const PaginationWrapper = styled.div`
128
+ display: flex;
129
+ justify-content: flex-end;
130
+ margin-top: ${(p) => p.theme.spacing.s};
131
+ `;
132
+
82
133
  export {
83
134
  ModalContent,
84
135
  QRWrapper,
@@ -94,4 +145,13 @@ export {
94
145
  UserForm,
95
146
  FormColumn,
96
147
  UserPermissions,
148
+ PagesHeader,
149
+ PageList,
150
+ PageListItem,
151
+ PageItemContent,
152
+ PageItemHeader,
153
+ PageItemType,
154
+ PageItemTitle,
155
+ PageItemSiteTag,
156
+ PaginationWrapper,
97
157
  };
@@ -1,7 +1,5 @@
1
- import React from "react";
2
-
3
1
  import { CheckField, Tag } from "@ax/components";
4
- import { ICheck, IRole } from "@ax/types";
2
+ import type { ICheck, IRole } from "@ax/types";
5
3
 
6
4
  import * as S from "./style";
7
5
 
@@ -9,8 +7,8 @@ const RoleItem = (props: IRoleItemProps): JSX.Element => {
9
7
  const { role, onChange, onClick, isChecked = false, isSelected = false, isReadOnly = false } = props;
10
8
 
11
9
  const handleClick = () => {
12
- onClick && onClick(role);
13
- onChange && onChange({ value: role.id, isChecked: !isChecked });
10
+ onClick?.(role);
11
+ onChange?.({ value: role.id, isChecked: !isChecked });
14
12
  };
15
13
 
16
14
  return (
@@ -114,8 +114,8 @@ const SiteItem = (props: ISiteItemProps): JSX.Element => {
114
114
  };
115
115
 
116
116
  export interface ISiteItemProps {
117
- item: { name: string; id: number | string; onChange: (id: number | string) => void };
118
- sites: (string | number)[];
117
+ item: { name: string; id: number | "global" | "all"; onChange: (id: number | "global" | "all") => void };
118
+ sites: ("global" | "all" | number)[];
119
119
  roles?: IRole[];
120
120
  addRoles?(newRoles: any): void;
121
121
  selected?: number[];
@@ -0,0 +1,69 @@
1
+ import { Button, ContentItem } from "@ax/components";
2
+ import { getStructuredDataTitle } from "@ax/helpers";
3
+ import type { IPage } from "@ax/types";
4
+
5
+ import * as S from "./style";
6
+
7
+ const SettingsSection = ({
8
+ heading,
9
+ description,
10
+ actionLabel,
11
+ onActionClick,
12
+ showAction = false,
13
+ children,
14
+ }: ISettingsSectionProps) => (
15
+ <S.SettingsWrapper>
16
+ <S.HeadingWrapper>
17
+ <S.Heading>{heading}</S.Heading>
18
+ {showAction && actionLabel && (
19
+ <Button type="button" buttonStyle="text" onClick={onActionClick} icon="addCircle">
20
+ {actionLabel}
21
+ </Button>
22
+ )}
23
+ </S.HeadingWrapper>
24
+ {children ? (
25
+ <S.SettingContent>{children}</S.SettingContent>
26
+ ) : (
27
+ <S.SettingContent>
28
+ <S.SettingText>{description}</S.SettingText>
29
+ </S.SettingContent>
30
+ )}
31
+ </S.SettingsWrapper>
32
+ );
33
+
34
+ const PagePermissionsList = ({ pages, onManageClick, testId }: IPagePermissionsListProps) => (
35
+ <S.PageList data-testid={testId || "user-pages-list"}>
36
+ {pages.map((page: IPage) => (
37
+ <ContentItem
38
+ key={page.id}
39
+ title={page.title}
40
+ type={getStructuredDataTitle(page.structuredData)}
41
+ date={page.modified}
42
+ />
43
+ ))}
44
+ {pages.length > 0 && (
45
+ <li>
46
+ <Button type="button" buttonStyle="line" onClick={onManageClick}>
47
+ Manage page permissions
48
+ </Button>
49
+ </li>
50
+ )}
51
+ </S.PageList>
52
+ );
53
+
54
+ interface ISettingsSectionProps {
55
+ heading: string;
56
+ description?: string;
57
+ actionLabel?: string;
58
+ onActionClick?: () => void;
59
+ showAction?: boolean;
60
+ children?: React.ReactNode;
61
+ }
62
+
63
+ interface IPagePermissionsListProps {
64
+ pages: IPage[];
65
+ onManageClick: () => void;
66
+ testId?: string;
67
+ }
68
+
69
+ export { SettingsSection, PagePermissionsList };