@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
@@ -8,6 +8,7 @@ import {
8
8
  MainWrapper,
9
9
  Notification,
10
10
  RestoreModal,
11
+ RestrictionsModal,
11
12
  ScheduleModal,
12
13
  } from "@ax/components";
13
14
  import { appActions } from "@ax/containers/App";
@@ -15,7 +16,7 @@ import { dataPacksActions } from "@ax/containers/Settings/DataPacks";
15
16
  import { structuredDataActions } from "@ax/containers/StructuredData";
16
17
  import { RouteLeavingGuard } from "@ax/guards";
17
18
  import { dateToString, getActivatedDataPacksIds, getDefaultTheme } from "@ax/helpers";
18
- import { useIsDirty, useModal } from "@ax/hooks";
19
+ import { useIsDirty, useModals, usePermissionsForPage } from "@ax/hooks";
19
20
  import type {
20
21
  IDataLanguage,
21
22
  IDataPack,
@@ -23,11 +24,16 @@ import type {
23
24
  ILanguage,
24
25
  INotification,
25
26
  IRootState,
27
+ ISchema,
26
28
  ISchemaField,
27
29
  ISite,
30
+ IStructuredData,
31
+ IStructuredDataContent,
32
+ PageAccessGrantsSummary,
28
33
  } from "@ax/types";
29
34
 
30
35
  import { DeleteModal } from "../atoms";
36
+ import { getPermission } from "../utils";
31
37
  import ConnectedField from "./ConnectedField";
32
38
 
33
39
  import * as S from "./style";
@@ -62,16 +68,31 @@ const Form = (props: IProps) => {
62
68
  restoreDataContent,
63
69
  } = props;
64
70
 
71
+ const isPrivateData = currentStructuredData?.private || false;
72
+
73
+ const isAllowedTo = usePermissionsForPage(
74
+ site?.id || "global",
75
+ form?.id || 0,
76
+ {
77
+ publishPages: getPermission("publish", !site, isPrivateData),
78
+ createPages: getPermission("create", !site, isPrivateData),
79
+ deletePages: getPermission("delete", !site, isPrivateData),
80
+ editPages: getPermission("edit", !site, isPrivateData),
81
+ setPagePermissions: site
82
+ ? "general.setPermissionPerSimpleContent"
83
+ : "global.globalData.setPermissionPerSimpleContent",
84
+ },
85
+ form?.accessGrants,
86
+ form?.dataLanguages,
87
+ );
88
+
65
89
  const [isNewStructuredData, setIsNewStructuredData] = useState(!currentStructuredDataId);
66
90
  const [notification, setNotification] = useState<INotification | null>(null);
67
91
  const [scheduleDate, setScheduleDate] = useState({
68
92
  date: dateToString(new Date(), "yyy/MM/dd"),
69
93
  time: "12:00 am",
70
94
  });
71
- const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
72
- const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
73
- const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
74
- const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
95
+ const { isOpen, toggleModal } = useModals(["schedule", "cancelSchedule", "restore", "userAccess", "delete"]);
75
96
  const [isDeleting, setIsDeleting] = useState(false);
76
97
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
77
98
  const { isDirty, resetDirty, setIsDirty } = useIsDirty(form);
@@ -94,10 +115,11 @@ const Form = (props: IProps) => {
94
115
 
95
116
  const activatedDataPacksIds = getActivatedDataPacksIds(activatedDataPacks);
96
117
  const disabledDatapack =
97
- currentStructuredData.local &&
98
- !activatedDataPacksIds.some((pack: string) => currentStructuredData.dataPacks.includes(pack));
118
+ currentStructuredData?.local &&
119
+ !activatedDataPacksIds.some((pack: string) => currentStructuredData!.dataPacks.includes(pack));
99
120
  const isDeleted = !!form?.deleted;
100
- const isDisabled = isDeleted || disabledDatapack;
121
+ const isDisabled = isDeleted || disabledDatapack || !isAllowedTo.editPages;
122
+ const isRestricted = !!(form?.accessGrants?.users.length || form?.accessGrants?.roles.length);
101
123
 
102
124
  const isDataTranslatable = currentStructuredData?.translate;
103
125
  const isScheduled = !!form && !!form.publicationScheduled;
@@ -144,18 +166,19 @@ const Form = (props: IProps) => {
144
166
  );
145
167
  });
146
168
 
147
- const handleSave = async (publish: boolean, scheduleDate?: string | null) => {
169
+ const handleSave = async (publish: boolean, scheduleDate?: string | null, formToSave?: any) => {
170
+ const currentForm = formToSave || form;
148
171
  const validated = publish && !skipReviewOnPublish ? await validateForm(true) : true;
149
172
 
150
173
  if (validated) {
151
- const status = isNewStructuredData ? true : form.draft;
174
+ const status = isNewStructuredData ? true : currentForm.draft;
152
175
 
153
176
  const payload: any = {
154
- ...form,
177
+ ...currentForm,
155
178
  structuredData: currentStructuredData ? currentStructuredData.id : null,
156
179
  draft: publish === true ? false : status,
157
180
  relatedSite: site ? site.id : null,
158
- publicationScheduled: scheduleDate !== undefined ? scheduleDate : form.publicationScheduled,
181
+ publicationScheduled: scheduleDate !== undefined ? scheduleDate : currentForm.publicationScheduled,
159
182
  };
160
183
 
161
184
  let saved = false;
@@ -174,7 +197,7 @@ const Form = (props: IProps) => {
174
197
  return form?.draft ? "Save" : "Save & publish";
175
198
  };
176
199
 
177
- const handleSaveAndPublish = () => (isNewStructuredData || form.draft ? handleSave(false) : handleSave(true));
200
+ const handleSaveAndPublish = () => (isNewStructuredData || form?.draft ? handleSave(false) : handleSave(true));
178
201
 
179
202
  const rightButtonProps = {
180
203
  label: isSaving ? "Saving" : !isDirty ? "Saved" : getSaveLabel(),
@@ -197,7 +220,7 @@ const Form = (props: IProps) => {
197
220
 
198
221
  const currentLanguages = getCurrentLanguages();
199
222
 
200
- const availableLanguages = isDisabled ? currentLanguages : languages;
223
+ const availableLanguages = isDisabled || !isAllowedTo.createPages ? currentLanguages : languages;
201
224
 
202
225
  const setRoute = (path: string) => setHistoryPush(path, true);
203
226
 
@@ -208,28 +231,32 @@ const Form = (props: IProps) => {
208
231
  );
209
232
 
210
233
  const handleClickNotification = () => {
211
- getSiteDataPack(currentStructuredData.id);
212
- setHistoryPush("/sites/settings/content-types", false);
234
+ if (currentStructuredData) {
235
+ getSiteDataPack(currentStructuredData.id);
236
+ setHistoryPush("/sites/settings/content-types", false);
237
+ }
213
238
  };
214
239
 
215
240
  const removeItem = async () => {
241
+ if (!form) return;
216
242
  setIsDeleting(true);
217
243
  const deleted = await deleteStructuredDataContent(form.id);
218
244
  setIsDeleting(false);
219
- toggleDeleteModal();
245
+ toggleModal("delete");
220
246
  if (deleted) {
221
247
  setHistoryPush(path, true);
222
248
  }
223
249
  };
224
250
 
225
251
  const publishItem = async () => {
252
+ if (!form) return;
226
253
  const validated = skipReviewOnPublish ? true : await validateForm(true);
227
254
  if (!validated) return;
228
255
 
229
256
  if (!isNewStructuredData) {
230
- setDataStatus(form.id, "undraft").then((updated: boolean) => {
257
+ await setDataStatus(form.id, "undraft").then(async (updated: boolean) => {
231
258
  if (updated) {
232
- getDataContent(form.id);
259
+ await getDataContent(form.id);
233
260
  }
234
261
  });
235
262
  } else {
@@ -237,12 +264,14 @@ const Form = (props: IProps) => {
237
264
  }
238
265
  };
239
266
 
240
- const unpublishItem = async () =>
241
- setDataStatus(form.id, "draft").then((updated: boolean) => {
267
+ const unpublishItem = async () => {
268
+ if (!form) return;
269
+ await setDataStatus(form.id, "draft").then(async (updated: boolean) => {
242
270
  if (updated) {
243
- getDataContent(form.id);
271
+ await getDataContent(form.id);
244
272
  }
245
273
  });
274
+ };
246
275
 
247
276
  const getPublishButton = (draft: boolean) => {
248
277
  if (draft === true || draft === undefined) {
@@ -266,50 +295,61 @@ const Form = (props: IProps) => {
266
295
  const date = new Date(`${scheduleDate.date} ${scheduleDate.time}`);
267
296
  const dateString = dateToString(date, "dd/MM/yyyy HH:mm:ss");
268
297
  handleSave(false, dateString);
269
- toggleScheduleModal();
298
+ toggleModal("schedule");
270
299
  };
271
300
 
272
301
  const handleCancelSchedulePublication = () => {
273
302
  handleSave(false, null);
274
303
  setScheduleDate({ date: "", time: "12:00 am" });
275
- toggleCancelScheduleModal();
304
+ toggleModal("cancelSchedule");
276
305
  };
277
306
 
278
307
  const downArrowMenu = {
279
308
  displayed: !isDeleted,
280
- button: getPublishButton(form?.draft),
309
+ button: isAllowedTo.publishPages ? getPublishButton(form?.draft ?? false) : undefined,
281
310
  options: [
282
311
  {
283
312
  label: "Review",
284
313
  icon: "question",
285
314
  action: reviewForm,
286
315
  },
287
- {
288
- label: "Delete",
289
- icon: "delete",
290
- action: toggleDeleteModal,
291
- },
292
316
  ],
293
317
  };
294
318
 
295
- if (status === "offline" && !isScheduled) {
319
+ if (isAllowedTo.deletePages) {
320
+ downArrowMenu.options.push({
321
+ label: "Delete",
322
+ icon: "delete",
323
+ action: async () => toggleModal("delete"),
324
+ });
325
+ }
326
+
327
+ if (status === "offline" && !isScheduled && isAllowedTo.publishPages) {
296
328
  downArrowMenu.options.unshift({
297
329
  label: "Schedule",
298
330
  icon: "calendar",
299
- action: toggleScheduleModal,
331
+ action: async () => toggleModal("schedule"),
300
332
  });
301
333
  }
302
334
 
303
- if (isScheduled) {
335
+ if (isScheduled && isAllowedTo.publishPages) {
304
336
  downArrowMenu.options.unshift({
305
337
  label: "Cancel Schedule",
306
338
  icon: "cancelEvent",
307
- action: toggleCancelScheduleModal,
339
+ action: async () => toggleModal("cancelSchedule"),
340
+ });
341
+ }
342
+
343
+ if (isAllowedTo.setPagePermissions) {
344
+ downArrowMenu.options.unshift({
345
+ label: "Page user access",
346
+ icon: "users",
347
+ action: async () => toggleModal("userAccess"),
308
348
  });
309
349
  }
310
350
 
311
351
  const getSelectedDataLanguage = (language: ILanguage): IDataLanguage | undefined =>
312
- form.dataLanguages?.find((dataLang: IDataLanguage) => dataLang.language === language.id);
352
+ form?.dataLanguages?.find((dataLang: IDataLanguage) => dataLang.language === language.id);
313
353
 
314
354
  const handleLanguage = async (language: ILanguage) => {
315
355
  const { locale, id } = language;
@@ -323,7 +363,9 @@ const Form = (props: IProps) => {
323
363
  resetDirty();
324
364
  } else {
325
365
  currentStructuredDataId && (await getDataContent(currentStructuredDataId));
326
- setFormValues({ ...form, canBeTranslated: true });
366
+ if (form) {
367
+ setFormValues({ ...form, canBeTranslated: true } as IStructuredDataContent & Record<string, any>);
368
+ }
327
369
  setIsNewStructuredData(true);
328
370
  }
329
371
  };
@@ -332,29 +374,17 @@ const Form = (props: IProps) => {
332
374
  if (!currentStructuredDataId) return;
333
375
  const isRestored = await restoreDataContent(currentStructuredDataId);
334
376
  if (isRestored) {
335
- isRestoreOpen && toggleRestoreModal();
377
+ isOpen("restore") && toggleModal("restore");
336
378
  await getDataContent(currentStructuredDataId);
337
379
  }
338
380
  };
339
381
 
340
- const mainScheduleModalAction = {
341
- title: "Schedule",
342
- onClick: handleSchedulePublication,
343
- };
344
-
345
- const secondaryScheduleModalAction = {
346
- title: "Cancel",
347
- onClick: toggleScheduleModal,
348
- };
349
-
350
- const mainCancelScheduleModalAction = {
351
- title: "Cancel Schedule",
352
- onClick: handleCancelSchedulePublication,
353
- };
354
-
355
- const secondaryCancelScheduleModalAction = {
356
- title: "Back",
357
- onClick: toggleCancelScheduleModal,
382
+ const handleUpdateAccessGrants = async (accessGrants: PageAccessGrantsSummary) => {
383
+ if (!form) return;
384
+ const updatedForm = { ...form, accessGrants } as IStructuredDataContent & Record<string, any>;
385
+ setFormValues(updatedForm);
386
+ await handleSave(false, undefined, updatedForm);
387
+ toggleModal("userAccess");
358
388
  };
359
389
 
360
390
  return isLoading && !isDeleting ? (
@@ -396,7 +426,7 @@ const Form = (props: IProps) => {
396
426
  type="error"
397
427
  text={deletedNotificationText}
398
428
  btnText="Restore content"
399
- onClick={toggleRestoreModal}
429
+ onClick={() => toggleModal("restore")}
400
430
  />
401
431
  </S.NotificationWrapper>
402
432
  )}
@@ -412,25 +442,33 @@ const Form = (props: IProps) => {
412
442
  </S.NotificationWrapper>
413
443
  )}
414
444
  <ErrorToast size="l" />
415
- <S.Wrapper>{Fields}</S.Wrapper>
445
+ <S.Wrapper>
446
+ {isRestricted && (
447
+ <S.StyledFloatingNote
448
+ message="Only some users can edit this page."
449
+ icon="users"
450
+ $short={true}
451
+ onClick={isAllowedTo.setPagePermissions ? () => toggleModal("userAccess") : undefined}
452
+ />
453
+ )}
454
+ {Fields}
455
+ </S.Wrapper>
416
456
  </MainWrapper>
417
457
  <ScheduleModal
418
- isOpen={isScheduleOpen}
419
- toggleModal={toggleScheduleModal}
420
- mainModalAction={mainScheduleModalAction}
421
- secondaryModalAction={secondaryScheduleModalAction}
458
+ isOpen={isOpen("schedule")}
459
+ toggleModal={() => toggleModal("schedule")}
460
+ onSchedule={handleSchedulePublication}
422
461
  scheduleDate={scheduleDate}
423
462
  setScheduleDate={setScheduleDate}
424
463
  />
425
464
  <CancelScheduleModal
426
- isOpen={isCancelScheduleOpen}
427
- toggleModal={toggleCancelScheduleModal}
428
- mainModalAction={mainCancelScheduleModalAction}
429
- secondaryModalAction={secondaryCancelScheduleModalAction}
465
+ isOpen={isOpen("cancelSchedule")}
466
+ toggleModal={() => toggleModal("cancelSchedule")}
467
+ onCancelSchedule={handleCancelSchedulePublication}
430
468
  />
431
469
  <DeleteModal
432
- isOpen={isDeleteOpen}
433
- toggleModal={toggleDeleteModal}
470
+ isOpen={isOpen("delete")}
471
+ toggleModal={() => toggleModal("delete")}
434
472
  title={title || "this item"}
435
473
  arePagesTranslated={arePagesTranslated}
436
474
  deleteAllVersions={deleteAllVersions}
@@ -439,23 +477,30 @@ const Form = (props: IProps) => {
439
477
  isDeleting={isDeleting}
440
478
  />
441
479
  <RestoreModal
442
- isOpen={isRestoreOpen}
443
- toggleModal={toggleRestoreModal}
480
+ isOpen={isOpen("restore")}
481
+ toggleModal={() => toggleModal("restore")}
444
482
  {...{
445
483
  isChild: false,
446
484
  hasIssues: schemaVersion !== form?.schemaVersionTimestamp,
447
485
  restorePage: handleRestorePage,
448
486
  }}
449
487
  />
488
+ <RestrictionsModal
489
+ isOpen={isOpen("userAccess")}
490
+ toggleModal={() => toggleModal("userAccess")}
491
+ currentAccessGrants={form?.accessGrants}
492
+ onUpdateAccessGrants={handleUpdateAccessGrants}
493
+ isSaving={isSaving}
494
+ />
450
495
  </>
451
496
  );
452
497
  };
453
498
 
454
499
  interface IProps {
455
- schema: any;
456
- form: any | null;
500
+ schema: ISchema;
501
+ form: (IStructuredDataContent & { deleted?: boolean; canBeTranslated?: boolean }) | null;
457
502
  site: ISite | null;
458
- currentStructuredData: any;
503
+ currentStructuredData: IStructuredData | null;
459
504
  isSaving: boolean;
460
505
  isLoading: boolean;
461
506
  siteLanguages: ILanguage[];
@@ -466,8 +511,8 @@ interface IProps {
466
511
  currentStructuredDataId: number | null;
467
512
  skipReviewOnPublish?: boolean;
468
513
  schemaVersion: string;
469
- createStructuredDataContent: (payload: any) => Promise<boolean>;
470
- updateStructuredDataContent: (payload: any) => Promise<boolean>;
514
+ createStructuredDataContent: (payload: IStructuredDataContent & Record<string, any>) => Promise<boolean>;
515
+ updateStructuredDataContent: (payload: IStructuredDataContent & Record<string, any>) => Promise<boolean>;
471
516
  setLanguage(lang: { locale: string; id: number | null }): void;
472
517
  getDataContent(id: number): Promise<void>;
473
518
  setHistoryPush(path: string, isEditor?: boolean): void;
@@ -475,7 +520,7 @@ interface IProps {
475
520
  setDataStatus(id: number, status: string): Promise<boolean>;
476
521
  deleteStructuredDataContent(id: number): Promise<boolean>;
477
522
  validateForm(publish?: boolean): Promise<boolean>;
478
- setFormValues(form: any): Promise<void>;
523
+ setFormValues(form: IStructuredDataContent & Record<string, any>): Promise<void>;
479
524
  setCurrentDataID(currentStructuredDataId: number | null): void;
480
525
  restoreDataContent(dataID: number | number[]): Promise<boolean>;
481
526
  }
@@ -1,3 +1,5 @@
1
+ import { FloatingNote } from "@ax/components";
2
+
1
3
  import styled from "styled-components";
2
4
 
3
5
  const Wrapper = styled.section`
@@ -30,4 +32,8 @@ const ModalFields = styled.div`
30
32
  }
31
33
  `;
32
34
 
33
- export { Wrapper, NotificationWrapper, ModalContent, ModalFields };
35
+ const StyledFloatingNote = styled((props) => <FloatingNote {...props} />)`
36
+ width: 268px;
37
+ `;
38
+
39
+ export { Wrapper, NotificationWrapper, ModalContent, ModalFields, StyledFloatingNote };
@@ -21,7 +21,7 @@ import {
21
21
  getStructuredDataTitle,
22
22
  trimText,
23
23
  } from "@ax/helpers";
24
- import { useModals, usePermissions } from "@ax/hooks";
24
+ import { useModals, usePermissionsForPage } from "@ax/hooks";
25
25
  import type {
26
26
  IAvailableSites,
27
27
  ICheck,
@@ -86,6 +86,7 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
86
86
  publicationScheduled,
87
87
  liveStatus,
88
88
  haveDraftPage,
89
+ accessGrants,
89
90
  } = globalPage;
90
91
 
91
92
  const activeColumns = columns.filter((col) => col.show).map((col) => col.id);
@@ -99,13 +100,19 @@ const GlobalPageItem = (props: IGlobalPageItemProps): JSX.Element => {
99
100
 
100
101
  const API_URL = process.env.GRIDDO_API_URL || process.env.REACT_APP_API_ENDPOINT;
101
102
 
102
- const isAllowedTo = usePermissions({
103
- duplicatePages: "global.globalData.duplicateGlobalData",
104
- publishPages: "global.globalData.publishUnpublishAllGlobalData",
105
- createPages: "global.globalData.createAllGlobalData",
106
- deletePages: "global.globalData.deleteAllGlobalData",
107
- editContentPages: "global.globalData.editAllGlobalData",
108
- });
103
+ const isAllowedTo = usePermissionsForPage(
104
+ "global",
105
+ globalPage.id,
106
+ {
107
+ duplicatePages: "global.globalData.duplicateGlobalData",
108
+ publishPages: "global.globalData.publishUnpublishAllGlobalData",
109
+ createPages: "global.globalData.createAllGlobalData",
110
+ deletePages: "global.globalData.deleteAllGlobalData",
111
+ editContentPages: "global.globalData.editAllGlobalData",
112
+ },
113
+ accessGrants,
114
+ pageLanguages,
115
+ );
109
116
 
110
117
  const publishedTooltip: Record<string, string> = {
111
118
  active: "Live",
@@ -10,7 +10,7 @@ import {
10
10
  getHumanLastModifiedDate,
11
11
  getScheduleFormatDate,
12
12
  } from "@ax/helpers";
13
- import { useModal, usePermissions } from "@ax/hooks";
13
+ import { useModal, usePermissionsForPage } from "@ax/hooks";
14
14
  import type {
15
15
  ICheck,
16
16
  IColumn,
@@ -25,7 +25,7 @@ import type {
25
25
  } from "@ax/types";
26
26
 
27
27
  import { DeleteModal } from "../../atoms";
28
- import { getPermission } from "./utils";
28
+ import { getPermission } from "../../utils";
29
29
 
30
30
  import * as S from "./style";
31
31
 
@@ -61,19 +61,25 @@ const StructuredDataItem = (props: IStructuredDataItemProps): JSX.Element => {
61
61
 
62
62
  const isPrivateData = currentStructuredData?.private || false;
63
63
  const isGlobal = !currentSiteInfo;
64
+ const { dataLanguages, publicationScheduled } = structuredData;
64
65
 
65
- const isAllowedTo = usePermissions({
66
- duplicatePages: getPermission("duplicate", isGlobal, isPrivateData),
67
- publishPages: getPermission("publish", isGlobal, isPrivateData),
68
- createPages: getPermission("create", isGlobal, isPrivateData),
69
- deletePages: getPermission("delete", isGlobal, isPrivateData),
70
- });
66
+ const isAllowedTo = usePermissionsForPage(
67
+ currentSiteInfo?.id || "global",
68
+ structuredData.id,
69
+ {
70
+ duplicatePages: getPermission("duplicate", isGlobal, isPrivateData),
71
+ publishPages: getPermission("publish", isGlobal, isPrivateData),
72
+ createPages: getPermission("create", isGlobal, isPrivateData),
73
+ deletePages: getPermission("delete", isGlobal, isPrivateData),
74
+ },
75
+ structuredData.accessGrants,
76
+ dataLanguages,
77
+ );
71
78
 
72
79
  const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
73
80
  const [deleteAllVersions, setDeleteAllVersions] = useState(false);
74
81
 
75
82
  const { locale } = lang;
76
- const { dataLanguages, publicationScheduled } = structuredData;
77
83
 
78
84
  const isScheduledPub = !!publicationScheduled && structuredData.draft;
79
85
  const activeColumns = columns.filter((col) => col.show).map((col) => col.id);
@@ -1,6 +1,10 @@
1
- const getPermission = (type: "duplicate" | "create" | "delete" | "publish", isGlobal: boolean, isPrivate: boolean) => {
1
+ const getPermission = (
2
+ type: "duplicate" | "create" | "delete" | "publish" | "edit",
3
+ isGlobal: boolean,
4
+ isPrivate: boolean,
5
+ ) => {
2
6
  const permissions: Record<
3
- "duplicate" | "create" | "delete" | "publish",
7
+ "duplicate" | "create" | "delete" | "publish" | "edit",
4
8
  Record<"global" | "site", Record<"default" | "private", string>>
5
9
  > = {
6
10
  duplicate: {
@@ -43,6 +47,16 @@ const getPermission = (type: "duplicate" | "create" | "delete" | "publish", isGl
43
47
  private: "content.publishUnpublishPrivateContentTypes",
44
48
  },
45
49
  },
50
+ edit: {
51
+ global: {
52
+ default: "global.globalData.editAllGlobalData",
53
+ private: "global.globalData.editContentPrivateContentTypes",
54
+ },
55
+ site: {
56
+ default: "content.editContentPages",
57
+ private: "content.editContentPrivateContentTypes",
58
+ },
59
+ },
46
60
  };
47
61
 
48
62
  const privateStr = isPrivate ? "private" : "default";
@@ -1,8 +1,9 @@
1
- import React from "react";
1
+ import { useState } from "react";
2
2
  import QRCode from "react-qr-code";
3
3
 
4
- import { IModal } from "@ax/types";
5
- import { Modal } from "@ax/components";
4
+ import { Modal, Pagination, Tag } from "@ax/components";
5
+ import { getStructuredDataTitle } from "@ax/helpers";
6
+ import type { IModal, IPage } from "@ax/types";
6
7
 
7
8
  import * as S from "./style";
8
9
 
@@ -44,4 +45,61 @@ interface ILinkDeviceModalProps extends IModal {
44
45
  token: string;
45
46
  }
46
47
 
47
- export { LinkDeviceModal };
48
+ const ProfilePagePermissionsList = ({ pages, testId, onPageChange }: IProfilePagePermissionsListProps) => {
49
+ const [currentPage, setCurrentPage] = useState(1);
50
+ const itemsPerPage = 10;
51
+
52
+ const totalPages = Math.ceil(pages.length / itemsPerPage);
53
+ const startIndex = (currentPage - 1) * itemsPerPage;
54
+ const endIndex = startIndex + itemsPerPage;
55
+ const paginatedPages = pages.slice(startIndex, endIndex);
56
+
57
+ const handlePageChange = (page: number) => {
58
+ setCurrentPage(page);
59
+ onPageChange?.();
60
+ };
61
+
62
+ return (
63
+ <>
64
+ <S.PageList data-testid={testId || "profile-pages-list"}>
65
+ {paginatedPages.map((page: IPage) => {
66
+ const siteName = page.site
67
+ ? page.availableSites?.find((site) => site.id === page.site)?.name || ""
68
+ : "Global";
69
+ return (
70
+ <S.PageListItem key={page.id}>
71
+ <S.PageItemContent>
72
+ <S.PageItemHeader>
73
+ <S.PageItemType>{getStructuredDataTitle(page.structuredData) || "Page"}</S.PageItemType>
74
+ <S.PageItemSiteTag>
75
+ <Tag text={siteName} type="square" />
76
+ </S.PageItemSiteTag>
77
+ </S.PageItemHeader>
78
+ <S.PageItemTitle>{page.title}</S.PageItemTitle>
79
+ </S.PageItemContent>
80
+ </S.PageListItem>
81
+ );
82
+ })}
83
+ </S.PageList>
84
+ {totalPages > 1 && (
85
+ <S.PaginationWrapper>
86
+ <Pagination
87
+ size="S"
88
+ currPage={currentPage}
89
+ totalItems={pages.length}
90
+ itemsPerPage={itemsPerPage}
91
+ setPage={handlePageChange}
92
+ />
93
+ </S.PaginationWrapper>
94
+ )}
95
+ </>
96
+ );
97
+ };
98
+
99
+ interface IProfilePagePermissionsListProps {
100
+ pages: IPage[];
101
+ testId?: string;
102
+ onPageChange?: () => void;
103
+ }
104
+
105
+ export { LinkDeviceModal, ProfilePagePermissionsList };