@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,13 +1,34 @@
1
1
  import { useEffect, useState } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
- import { Button, ErrorToast, FieldsBehavior, ProfileImage, SearchField } from "@ax/components";
4
+ import { sites as sitesApi } from "@ax/api";
5
+ import {
6
+ Button,
7
+ ErrorToast,
8
+ FieldsBehavior,
9
+ FloatingPanel,
10
+ PageFinder,
11
+ ProfileImage,
12
+ SearchField,
13
+ } from "@ax/components";
5
14
  import { appActions } from "@ax/containers/App";
6
15
  import { RouteLeavingGuard } from "@ax/guards";
7
- import { useModal } from "@ax/hooks";
8
- import type { IImage, IRole, IRootState, ISite, ISiteRoles, IUser } from "@ax/types";
16
+ import { isReqOk } from "@ax/helpers";
17
+ import { useModals } from "@ax/hooks";
18
+ import type {
19
+ IImage,
20
+ ILanguage,
21
+ IPage,
22
+ IRole,
23
+ IRootState,
24
+ ISite,
25
+ IUser,
26
+ UserPagePermissionEntry,
27
+ UserRoleAssignment,
28
+ } from "@ax/types";
9
29
 
10
30
  import { timezones } from "../../Settings/Globals/constants";
31
+ import { PagePermissionsList, SettingsSection } from "../UserCreate/atoms";
11
32
  import OptionItem from "../UserCreate/OptionItem";
12
33
  import SiteItem from "../UserCreate/SiteItem";
13
34
  import RolesModal from "../UserCreate/SiteItem/RolesModal";
@@ -29,26 +50,30 @@ const UserForm = (props: IProps) => {
29
50
  readOnlySites = false,
30
51
  isSiteView = false,
31
52
  site,
53
+ siteLanguages,
54
+ globalLanguages,
32
55
  } = props;
33
56
 
34
- const { id, username, name, email, image, company, position, timezone, isSuperAdmin } = form;
57
+ const { id, username, name, email, image, company, position, timezone, isSuperAdmin, pagePermissions } = form;
35
58
 
36
59
  const getSortedItemsByName = (items: ISite[]) =>
37
60
  items.sort((item1: ISite, item2: ISite) => item1.name.localeCompare(item2.name));
38
61
 
39
62
  const sortedByNameSites = getSortedItemsByName(sites);
40
63
 
41
- const { isOpen, toggleModal } = useModal(false);
42
- const { isOpen: isOpenRoleModal, toggleModal: toggleRoleModal } = useModal(false);
43
- const { isOpen: isOpenTypeChangeModal, toggleModal: toggleTypeChangeModal } = useModal(false);
64
+ const { isOpen, toggleModal } = useModals(["password", "roles", "typeChange", "pagePermissions"]);
44
65
  const [sitesList, setSiteList] = useState(sortedByNameSites);
45
66
  const [showMore, setShowMore] = useState(false);
67
+ const [selectedPages, setSelectedPages] = useState<IPage[]>([]);
46
68
 
47
69
  const allowedRoutes = ["/profile"];
48
70
  const { isDirty } = shouldBeSaved(user, form);
49
71
 
50
72
  const isSameUser = currentUser && currentUser.id === user.id;
51
73
  const isEditDisabled = !!currentUser && !currentUser.isSuperAdmin && isSiteView && !isSameUser;
74
+ const defaultLanguage = isSiteView
75
+ ? siteLanguages.find((lang) => lang.isDefault)
76
+ : globalLanguages.find((lang) => lang.isDefault);
52
77
 
53
78
  useEffect(() => {
54
79
  if (form.roles.length === 0) {
@@ -56,6 +81,43 @@ const UserForm = (props: IProps) => {
56
81
  }
57
82
  }, [form.roles]);
58
83
 
84
+ // biome-ignore lint/correctness/useExhaustiveDependencies: runs on mount only
85
+ useEffect(() => {
86
+ const fetchPages = async () => {
87
+ if (!pagePermissions || pagePermissions.length === 0) {
88
+ setSelectedPages([]);
89
+ return;
90
+ }
91
+
92
+ const siteId: "global" | number = isSiteView ? site!.id : "global";
93
+ const pagePermission = pagePermissions.find((entry) => entry.siteId === siteId);
94
+
95
+ if (pagePermission && pagePermission.pageIds.length > 0) {
96
+ try {
97
+ const params = {
98
+ siteID: siteId,
99
+ page: 1,
100
+ itemsPerPage: 50,
101
+ deleted: false,
102
+ filterPages: pagePermission.pageIds,
103
+ lang: defaultLanguage?.id,
104
+ ignoreLang: true,
105
+ };
106
+ const response = await sitesApi.getSitePages(params);
107
+ if (isReqOk(response.status) && response.data?.items) {
108
+ setSelectedPages(response.data.items);
109
+ }
110
+ } catch (error) {
111
+ console.error("Failed to fetch page permissions", error);
112
+ }
113
+ } else {
114
+ setSelectedPages([]);
115
+ }
116
+ };
117
+
118
+ fetchPages();
119
+ }, []);
120
+
59
121
  const handleImageChange = (value: IImage) => setForm({ ...form, image: value || null });
60
122
  const handleNameChange = (value: string) => setForm({ ...form, name: value });
61
123
  const handleEmailChange = (value: string) => setForm({ ...form, email: value });
@@ -64,16 +126,35 @@ const UserForm = (props: IProps) => {
64
126
  const handlePositionChange = (value: string) => setForm({ ...form, position: value });
65
127
  const handleTimezoneChange = (value: string) => setForm({ ...form, timezone: value });
66
128
 
129
+ const handleSetPage = (pages: IPage | IPage[]) => {
130
+ const pagesArray = Array.isArray(pages) ? pages : [pages];
131
+ const pageIds = pagesArray.map((p) => p.id);
132
+ const siteId: "global" | number = isSiteView ? site!.id : "global";
133
+
134
+ const newEntry: UserPagePermissionEntry = { siteId, pageIds };
135
+
136
+ const currentPagePermissions = pagePermissions || [];
137
+ const existingIndex = currentPagePermissions.findIndex((entry) => entry.siteId === siteId);
138
+ const newPagePermissions: UserPagePermissionEntry[] =
139
+ existingIndex >= 0
140
+ ? currentPagePermissions.map((entry, index) => (index === existingIndex ? newEntry : entry))
141
+ : [...currentPagePermissions, newEntry];
142
+
143
+ setForm({ ...form, pagePermissions: newPagePermissions });
144
+ setSelectedPages(pagesArray);
145
+ toggleModal("pagePermissions");
146
+ };
147
+
67
148
  const toggleSuperAdmin = () => {
68
149
  setForm({ ...form, isSuperAdmin: !form.isSuperAdmin });
69
- toggleTypeChangeModal();
150
+ toggleModal("typeChange");
70
151
  };
71
152
 
72
- const handleAddRoles = (newRoles: ISiteRoles) => {
153
+ const handleAddRoles = (newRoles: UserRoleAssignment) => {
73
154
  const oldRoles =
74
155
  newRoles.siteId === "all"
75
- ? form.roles.filter((role: ISiteRoles) => role.siteId === "global")
76
- : form.roles.filter((role: ISiteRoles) => role.siteId !== newRoles.siteId);
156
+ ? form.roles.filter((role) => role.siteId === "global")
157
+ : form.roles.filter((role) => role.siteId !== newRoles.siteId);
77
158
 
78
159
  const updatedRoles = [...oldRoles, newRoles];
79
160
 
@@ -81,7 +162,7 @@ const UserForm = (props: IProps) => {
81
162
  };
82
163
 
83
164
  const getAllSitesWithRoles = () => {
84
- const siteRoles = form.roles.find((role: ISiteRoles) => role.siteId === "all");
165
+ const siteRoles = form.roles.find((role) => role.siteId === "all");
85
166
  return sites.map((site: ISite) => {
86
167
  return { siteId: site.id, roles: siteRoles ? siteRoles.roles : [] };
87
168
  });
@@ -89,9 +170,9 @@ const UserForm = (props: IProps) => {
89
170
 
90
171
  const handleAddSiteRoles = (newRoles: number[]) => {
91
172
  if (!site) return;
92
- const hasAllSites = form.roles.find((role: ISiteRoles) => role.siteId === "all");
173
+ const hasAllSites = form.roles.find((role) => role.siteId === "all");
93
174
  const userRoles = hasAllSites ? getAllSitesWithRoles() : form.roles;
94
- const oldRoles = userRoles.filter((role: ISiteRoles) => role.siteId !== site.id);
175
+ const oldRoles = userRoles.filter((role) => role.siteId !== site.id);
95
176
  const newSiteRoles = { siteId: site.id, roles: newRoles };
96
177
  const updatedRoles = newRoles.length > 0 ? [...oldRoles, newSiteRoles] : oldRoles;
97
178
  setForm({ ...form, roles: updatedRoles });
@@ -99,13 +180,13 @@ const UserForm = (props: IProps) => {
99
180
 
100
181
  const handleChangeType = (value: string) => {
101
182
  if ((isSuperAdmin && value === "member") || (!isSuperAdmin && value === "superadmin")) {
102
- toggleTypeChangeModal();
183
+ toggleModal("typeChange");
103
184
  }
104
185
  };
105
186
 
106
187
  const modalProps = {
107
- isOpen,
108
- toggleModal,
188
+ isOpen: isOpen("password"),
189
+ toggleModal: () => toggleModal("password"),
109
190
  id,
110
191
  };
111
192
 
@@ -114,11 +195,11 @@ const UserForm = (props: IProps) => {
114
195
  onClick: toggleSuperAdmin,
115
196
  };
116
197
 
117
- const secondaryTypeChangeModalAction = { title: "Cancel", onClick: toggleTypeChangeModal };
198
+ const secondaryTypeChangeModalAction = { title: "Cancel", onClick: () => toggleModal("typeChange") };
118
199
 
119
200
  const modalTypeChangeProps = {
120
- isOpen: isOpenTypeChangeModal,
121
- toggleModal: toggleTypeChangeModal,
201
+ isOpen: isOpen("typeChange"),
202
+ toggleModal: () => toggleModal("typeChange"),
122
203
  email,
123
204
  isSuperAdmin,
124
205
  mainModalAction: mainTypeChangeModalAction,
@@ -135,12 +216,12 @@ const UserForm = (props: IProps) => {
135
216
  setSiteList(sortedSites);
136
217
  };
137
218
 
138
- const selectSite = (id: number | string) => {
219
+ const selectSite = (id: number | "global" | "all") => {
139
220
  const { roles } = form;
140
- const hasSite = roles.find((siteRole: ISiteRoles) => siteRole.siteId === id);
221
+ const hasSite = roles.find((siteRole) => siteRole.siteId === id);
141
222
  if (hasSite) {
142
223
  const filteredRoles = roles.filter(
143
- (siteRole: ISiteRoles) =>
224
+ (siteRole) =>
144
225
  (Number.isInteger(id) && siteRole.siteId !== id && siteRole.siteId !== "all") ||
145
226
  (!Number.isInteger(id) && siteRole.siteId !== id),
146
227
  );
@@ -170,17 +251,17 @@ const UserForm = (props: IProps) => {
170
251
  return currentSite?.name;
171
252
  };
172
253
 
173
- const sitesID = form.roles.map((roleSite: ISiteRoles) => roleSite.siteId);
254
+ const sitesID = form.roles.map((roleSite) => roleSite.siteId);
174
255
 
175
256
  const siteSelectedRoles = isSiteView
176
- ? form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site?.id || roleSite.siteId === "all")
257
+ ? form.roles.find((roleSite) => roleSite.siteId === site?.id || roleSite.siteId === "all")
177
258
  : undefined;
178
259
 
179
- const rolesAll = form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "all");
260
+ const rolesAll = form.roles.find((roleSite) => roleSite.siteId === "all");
180
261
  const allDescription =
181
262
  "The user will have access to all existing and future sites depending on the permissions assigned to the role.";
182
263
 
183
- const rolesGlobal = form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "global");
264
+ const rolesGlobal = form.roles.find((roleSite) => roleSite.siteId === "global");
184
265
  const globalDescription = "Global data will be accessible to the user based on the permissions assigned to the role.";
185
266
 
186
267
  return (
@@ -198,125 +279,126 @@ const UserForm = (props: IProps) => {
198
279
  <S.NameTitle>{user.name}</S.NameTitle>
199
280
  </S.UserName>
200
281
  </S.Header>
201
- <FieldsBehavior
202
- title="Name"
203
- name="name"
204
- fieldType="TextField"
205
- placeholder="Type a name"
206
- mandatory
207
- value={name}
208
- onChange={handleNameChange}
209
- disabled={isEditDisabled}
210
- />
211
- <FieldsBehavior
212
- title="Email"
213
- name="email"
214
- fieldType="TextField"
215
- placeholder="Type an email"
216
- mandatory
217
- value={email}
218
- onChange={handleEmailChange}
219
- disabled={isSameUser || isEditDisabled}
220
- />
221
- <FieldsBehavior
222
- title="Alias"
223
- name="username"
224
- fieldType="TextField"
225
- placeholder="Type an alias"
226
- mandatory
227
- value={username}
228
- onChange={handleUsernameChange}
229
- prefix="@"
230
- disabled={isEditDisabled}
231
- />
232
- <FieldsBehavior
233
- title="Company"
234
- name="company"
235
- fieldType="TextField"
236
- placeholder="Type a company"
237
- value={company || ""}
238
- onChange={handleCompanyChange}
239
- disabled={isEditDisabled}
240
- />
241
- {isSameUser && (
282
+ <S.FieldsWrapper hasBoder={isSiteView}>
242
283
  <FieldsBehavior
243
- title="Password"
244
- name="password"
284
+ title="Name"
285
+ name="name"
245
286
  fieldType="TextField"
246
- inputType="password"
247
- placeholder="Type a password"
287
+ placeholder="Type a name"
248
288
  mandatory
249
- readonly
250
- value="********"
251
- icon="edit"
252
- onClickIcon={toggleModal}
289
+ value={name}
290
+ onChange={handleNameChange}
291
+ disabled={isEditDisabled}
253
292
  />
254
- )}
255
- <FieldsBehavior
256
- title="Job title"
257
- name="position"
258
- fieldType="TextField"
259
- placeholder="Type a job"
260
- value={position || ""}
261
- onChange={handlePositionChange}
262
- disabled={isEditDisabled}
263
- />
264
- <FieldsBehavior
265
- title="Time zone"
266
- name="timezone"
267
- fieldType="Select"
268
- value={timezone}
269
- options={timezones}
270
- onChange={handleTimezoneChange}
271
- mandatory={true}
272
- disabled={isEditDisabled}
273
- />
293
+ <FieldsBehavior
294
+ title="Email"
295
+ name="email"
296
+ fieldType="TextField"
297
+ placeholder="Type an email"
298
+ mandatory
299
+ value={email}
300
+ onChange={handleEmailChange}
301
+ disabled={isSameUser || isEditDisabled}
302
+ />
303
+ <FieldsBehavior
304
+ title="Alias"
305
+ name="username"
306
+ fieldType="TextField"
307
+ placeholder="Type an alias"
308
+ mandatory
309
+ value={username}
310
+ onChange={handleUsernameChange}
311
+ prefix="@"
312
+ disabled={isEditDisabled}
313
+ />
314
+ <FieldsBehavior
315
+ title="Company"
316
+ name="company"
317
+ fieldType="TextField"
318
+ placeholder="Type a company"
319
+ value={company || ""}
320
+ onChange={handleCompanyChange}
321
+ disabled={isEditDisabled}
322
+ />
323
+ {isSameUser && (
324
+ <FieldsBehavior
325
+ title="Password"
326
+ name="password"
327
+ fieldType="TextField"
328
+ inputType="password"
329
+ placeholder="Type a password"
330
+ mandatory
331
+ readonly
332
+ value="********"
333
+ icon="edit"
334
+ onClickIcon={toggleModal}
335
+ />
336
+ )}
337
+ <FieldsBehavior
338
+ title="Job title"
339
+ name="position"
340
+ fieldType="TextField"
341
+ placeholder="Type a job"
342
+ value={position || ""}
343
+ onChange={handlePositionChange}
344
+ disabled={isEditDisabled}
345
+ />
346
+ <FieldsBehavior
347
+ title="Time zone"
348
+ name="timezone"
349
+ fieldType="Select"
350
+ value={timezone}
351
+ options={timezones}
352
+ onChange={handleTimezoneChange}
353
+ mandatory={true}
354
+ disabled={isEditDisabled}
355
+ />
356
+ </S.FieldsWrapper>
274
357
  {isSiteView ? (
275
358
  isSuperAdmin ? (
276
359
  <>
277
- <S.SettingsWrapper>
278
- <S.Heading>Roles assigned</S.Heading>
279
- <S.SettingContent>
280
- <OptionItem
281
- name="superadmin"
282
- title="Super admin"
283
- description="You can manage all aspects of your site, including inviting people and managing their permissions."
284
- onChange={toggleSuperAdmin}
285
- value={isSuperAdmin}
286
- readOnly={readOnlySites}
287
- />
288
- </S.SettingContent>
289
- </S.SettingsWrapper>
360
+ <SettingsSection heading="Roles assigned">
361
+ <OptionItem
362
+ name="superadmin"
363
+ title="Super admin"
364
+ description="You can manage all aspects of your site, including inviting people and managing their permissions."
365
+ onChange={toggleSuperAdmin}
366
+ value={isSuperAdmin}
367
+ readOnly={readOnlySites}
368
+ />
369
+ </SettingsSection>
290
370
  </>
291
371
  ) : (
292
372
  <>
293
- <S.SettingsWrapper>
294
- <S.HeadingWrapper>
295
- <S.Heading>Roles Assigned</S.Heading>
296
- {(!siteSelectedRoles || siteSelectedRoles.roles.length === 0) && (
297
- <Button type="button" buttonStyle="text" onClick={toggleRoleModal} icon="addCircle">
298
- Add Role
299
- </Button>
300
- )}
301
- </S.HeadingWrapper>
302
- <S.SettingContent>
303
- <S.SettingText>You can add a one or more roles to this user.</S.SettingText>
304
- </S.SettingContent>
305
- </S.SettingsWrapper>
373
+ <SettingsSection
374
+ heading="Roles Assigned"
375
+ description="You can add a one or more roles to this user."
376
+ actionLabel="Add Role"
377
+ onActionClick={() => toggleModal("roles")}
378
+ showAction={!siteSelectedRoles || siteSelectedRoles.roles.length === 0}
379
+ />
306
380
  <S.RoleList>
307
381
  {siteSelectedRoles?.roles.map((siteRoleId: number) => {
308
382
  const role = roles.find((role: IRole) => role.id === siteRoleId);
309
383
  return role && <RoleItem key={role.name} role={role} isReadOnly={true} />;
310
384
  })}
311
385
  {siteSelectedRoles && siteSelectedRoles.roles.length > 0 && !isSameUser && (
312
- <Button type="button" buttonStyle="line" onClick={toggleRoleModal}>
386
+ <Button type="button" buttonStyle="line" onClick={() => toggleModal("roles")}>
313
387
  Manage user roles
314
388
  </Button>
315
389
  )}
316
390
  </S.RoleList>
391
+ <SettingsSection
392
+ heading="Page permissions"
393
+ description="You can give access only to a specific page/s to this user."
394
+ actionLabel="Add Page"
395
+ onActionClick={() => toggleModal("pagePermissions")}
396
+ showAction={selectedPages.length === 0}
397
+ />
398
+ <PagePermissionsList pages={selectedPages} onManageClick={() => toggleModal("pagePermissions")} />
317
399
  <RolesModal
318
- isOpen={isOpenRoleModal}
319
- toggleModal={toggleRoleModal}
400
+ isOpen={isOpen("roles")}
401
+ toggleModal={() => toggleModal("roles")}
320
402
  roles={roles}
321
403
  addRoles={handleAddSiteRoles}
322
404
  selectedRoles={siteSelectedRoles ? siteSelectedRoles.roles : []}
@@ -348,28 +430,23 @@ const UserForm = (props: IProps) => {
348
430
  </S.OptionsWrapper>
349
431
  )}
350
432
  {isSuperAdmin && readOnlySites && (
351
- <S.SettingsWrapper>
352
- <S.Heading>Sites & roles assigned</S.Heading>
353
- <S.SettingContent>
354
- <OptionItem
355
- name="superadmin"
356
- title="Super admin"
357
- description="The user will have full access to all features and their configuration. They will also have access to all sites."
358
- onChange={toggleSuperAdmin}
359
- value={isSuperAdmin}
360
- readOnly={readOnlySites}
361
- />
362
- </S.SettingContent>
363
- </S.SettingsWrapper>
433
+ <SettingsSection heading="Sites & roles assigned">
434
+ <OptionItem
435
+ name="superadmin"
436
+ title="Super admin"
437
+ description="The user will have full access to all features and their configuration. They will also have access to all sites."
438
+ onChange={toggleSuperAdmin}
439
+ value={isSuperAdmin}
440
+ readOnly={readOnlySites}
441
+ />
442
+ </SettingsSection>
364
443
  )}
365
444
  {!isSuperAdmin && (
366
445
  <S.MemberWrapper>
367
- <S.SettingsWrapper>
368
- <S.Heading>Global data Permissions</S.Heading>
369
- <S.SettingContent>
370
- <S.SettingText>The user can have access to global data with specific role.</S.SettingText>
371
- </S.SettingContent>
372
- </S.SettingsWrapper>
446
+ <SettingsSection
447
+ heading="Global data Permissions"
448
+ description="The user can have access to global data with specific role."
449
+ />
373
450
  <S.GlobalPermissionsFieldWrapper>
374
451
  <SiteItem
375
452
  sites={sitesID}
@@ -382,6 +459,18 @@ const UserForm = (props: IProps) => {
382
459
  readOnly={readOnlySites}
383
460
  />
384
461
  </S.GlobalPermissionsFieldWrapper>
462
+ {sitesID.includes("global") && (
463
+ <>
464
+ <SettingsSection
465
+ heading="Page permissions"
466
+ description="You can give access only to a specific page/s to this user."
467
+ actionLabel="Add Role"
468
+ onActionClick={() => toggleModal("pagePermissions")}
469
+ showAction={selectedPages.length === 0}
470
+ />
471
+ <PagePermissionsList pages={selectedPages} onManageClick={() => toggleModal("pagePermissions")} />
472
+ </>
473
+ )}
385
474
  <S.SettingsWrapper>
386
475
  <S.Heading>Sites & roles assigned</S.Heading>
387
476
  {form.roles.length > 0 ? (
@@ -398,7 +487,7 @@ const UserForm = (props: IProps) => {
398
487
 
399
488
  <S.SiteList>
400
489
  {!rolesAll &&
401
- form.roles.map((siteRole: ISiteRoles, index: number) => {
490
+ form.roles.map((siteRole, index: number) => {
402
491
  if (siteRole.siteId !== "all" && siteRole.siteId !== "global") {
403
492
  const siteName = getSiteName(siteRole.siteId);
404
493
  if (!siteName) return <></>;
@@ -465,7 +554,7 @@ const UserForm = (props: IProps) => {
465
554
  sitesList.map((site: ISite) => {
466
555
  const rolesSite = form.roles.find((roleSite: any) => roleSite.siteId === site.id);
467
556
  return (
468
- !form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site.id) && (
557
+ !form.roles.find((roleSite) => roleSite.siteId === site.id) && (
469
558
  <SiteItem
470
559
  readOnly={readOnlySites}
471
560
  disabled={!!rolesAll}
@@ -490,6 +579,27 @@ const UserForm = (props: IProps) => {
490
579
  </S.Wrapper>
491
580
  <PasswordModal {...modalProps} />
492
581
  <TypeChangeModal {...modalTypeChangeProps} />
582
+ <FloatingPanel
583
+ title="Page Permissions"
584
+ toggleModal={() => toggleModal("pagePermissions")}
585
+ isOpen={isOpen("pagePermissions")}
586
+ >
587
+ <S.PanelContent>
588
+ {isOpen("pagePermissions") && (
589
+ <PageFinder
590
+ onClick={handleSetPage}
591
+ isOpen={isOpen("pagePermissions")}
592
+ multiple
593
+ globalContent={!isSiteView}
594
+ hideSites
595
+ pages={selectedPages.map((p) => p.id)}
596
+ defaultLang={defaultLanguage?.id}
597
+ alwaysShowButton
598
+ disableTranslations
599
+ />
600
+ )}
601
+ </S.PanelContent>
602
+ </FloatingPanel>
493
603
  </>
494
604
  );
495
605
  };
@@ -500,6 +610,8 @@ const mapStateToProps = (state: IRootState) => ({
500
610
  currentUser: state.users.currentUser,
501
611
  sites: state.sites.allSites,
502
612
  roles: state.users.roles,
613
+ siteLanguages: state.sites.currentSiteLanguages,
614
+ globalLanguages: state.app.globalLangs,
503
615
  });
504
616
 
505
617
  interface IDispatchProps {
@@ -522,6 +634,8 @@ interface IProfileProps {
522
634
  isSiteView?: boolean;
523
635
  roles: IRole[];
524
636
  site: ISite | null;
637
+ siteLanguages: ILanguage[];
638
+ globalLanguages: ILanguage[];
525
639
  }
526
640
 
527
641
  type IProps = IProfileProps & IDispatchProps;
@@ -34,7 +34,6 @@ const ModalContent = styled.div`
34
34
 
35
35
  const SettingsWrapper = styled.div`
36
36
  position: relative;
37
- border-top: 1px solid ${(p) => p.theme.color.uiLine};
38
37
  padding-top: ${(p) => p.theme.spacing.m};
39
38
  padding-bottom: ${(p) => p.theme.spacing.s};
40
39
  `;
@@ -73,6 +72,7 @@ const SelectAllSitesFieldWrapper = styled.div``;
73
72
 
74
73
  const GlobalPermissionsFieldWrapper = styled.div`
75
74
  padding-bottom: ${(p) => p.theme.spacing.m};
75
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
76
76
  `;
77
77
 
78
78
  const SiteList = styled.div`
@@ -106,6 +106,7 @@ const MemberWrapper = styled.div``;
106
106
 
107
107
  const OptionsWrapper = styled.div`
108
108
  padding-bottom: ${(p) => p.theme.spacing.m};
109
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
109
110
  `;
110
111
 
111
112
  const Label = styled.div`
@@ -120,6 +121,15 @@ const HelpText = styled.div`
120
121
  color: ${(p) => p.theme.color?.textMediumEmphasis};
121
122
  `;
122
123
 
124
+ const PanelContent = styled.div`
125
+ padding: ${(p) => p.theme.spacing.m};
126
+ height: 100%;
127
+ `;
128
+
129
+ const FieldsWrapper = styled.div<{ hasBoder: boolean }>`
130
+ border-bottom: ${(p) => (p.hasBoder ? `1px solid ${p.theme.color.uiLine}` : `none`)};
131
+ `;
132
+
123
133
  export {
124
134
  Wrapper,
125
135
  Header,
@@ -142,4 +152,6 @@ export {
142
152
  HelpText,
143
153
  OptionsWrapper,
144
154
  MemberWrapper,
155
+ PanelContent,
156
+ FieldsWrapper,
145
157
  };
@@ -4,7 +4,7 @@ import { Avatar, CheckField, ElementsTooltip, Tag, Toast, Tooltip, TruncatedTool
4
4
  import { usersActions } from "@ax/containers/Users";
5
5
  import { getDaysAgo } from "@ax/helpers";
6
6
  import { useModal, usePermission, useToast } from "@ax/hooks";
7
- import type { ICheck, IRole, IRootState, ISite, ISiteRoles, IUser } from "@ax/types";
7
+ import type { ICheck, IRole, IRootState, ISite, IUser } from "@ax/types";
8
8
 
9
9
  import { DeleteModal } from "../atoms";
10
10
 
@@ -110,8 +110,8 @@ const UserItem = (props: IUserItemProps): JSX.Element => {
110
110
  user.isSuperAdmin
111
111
  ? ["ALL SITES"]
112
112
  : user?.roles
113
- .filter((siteRoles: ISiteRoles) => siteRoles.siteId === "all" || Number.isInteger(siteRoles.siteId))
114
- .map((siteRoles: ISiteRoles) => getSiteNameById(siteRoles.siteId));
113
+ .filter((siteRoles) => siteRoles.siteId === "all" || Number.isInteger(siteRoles.siteId))
114
+ .map((siteRoles) => getSiteNameById(siteRoles.siteId));
115
115
 
116
116
  let rolesIds: number[] = [];
117
117