@griddo/ax 11.7.13 → 11.8.0-rc.0

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 +3 -2
  2. package/src/GlobalStore.tsx +3 -0
  3. package/src/api/index.tsx +4 -0
  4. package/src/api/logs.tsx +97 -0
  5. package/src/api/navigation.tsx +1 -1
  6. package/src/api/pages.tsx +1 -1
  7. package/src/api/schemas.tsx +18 -0
  8. package/src/api/users.tsx +17 -0
  9. package/src/components/ConfigPanel/Form/ConnectedField/NavConnectedField/index.tsx +3 -2
  10. package/src/components/ConfigPanel/Form/index.tsx +3 -1
  11. package/src/components/ConfigPanel/index.tsx +3 -0
  12. package/src/components/Fields/SummaryButton/index.tsx +6 -4
  13. package/src/components/Fields/TimeField/style.tsx +0 -1
  14. package/src/components/Fields/TranslateButton/index.tsx +3 -0
  15. package/src/components/FloatingMenu/index.tsx +25 -1
  16. package/src/components/FloatingMenu/style.tsx +3 -2
  17. package/src/components/LanguageMenu/index.tsx +1 -1
  18. package/src/components/MainWrapper/AppBar/index.tsx +4 -3
  19. package/src/components/RestoreModal/index.tsx +51 -0
  20. package/src/components/RestoreModal/style.tsx +7 -0
  21. package/src/components/SearchField/index.tsx +1 -1
  22. package/src/components/SearchField/style.tsx +4 -4
  23. package/src/components/TableFilters/CheckGroupFilter/index.tsx +42 -5
  24. package/src/components/TableFilters/CheckGroupFilter/style.tsx +8 -1
  25. package/src/components/TableFilters/SiteFilter/index.tsx +26 -57
  26. package/src/components/Tag/index.tsx +6 -3
  27. package/src/components/Tag/style.tsx +2 -2
  28. package/src/components/index.tsx +2 -0
  29. package/src/containers/ActivityLog/actions.tsx +262 -0
  30. package/src/containers/ActivityLog/constants.tsx +6 -0
  31. package/src/containers/ActivityLog/index.tsx +4 -0
  32. package/src/containers/ActivityLog/interfaces.tsx +12 -0
  33. package/src/containers/ActivityLog/reducer.tsx +25 -0
  34. package/src/containers/Navigation/Defaults/actions.tsx +4 -3
  35. package/src/containers/StructuredData/actions.tsx +7 -4
  36. package/src/containers/StructuredData/interfaces.tsx +2 -8
  37. package/src/containers/StructuredData/reducer.tsx +2 -8
  38. package/src/containers/Users/actions.tsx +22 -19
  39. package/src/modules/ActivityLog/DetailModal/index.tsx +108 -0
  40. package/src/modules/ActivityLog/DetailModal/style.tsx +52 -0
  41. package/src/modules/ActivityLog/DownloadModal/index.tsx +104 -0
  42. package/src/modules/ActivityLog/DownloadModal/style.tsx +12 -0
  43. package/src/modules/ActivityLog/ItemGroup/index.tsx +27 -0
  44. package/src/modules/ActivityLog/ItemGroup/style.tsx +39 -0
  45. package/src/modules/ActivityLog/ItemLog/EventItem/index.tsx +167 -0
  46. package/src/modules/ActivityLog/ItemLog/EventItem/style.tsx +79 -0
  47. package/src/modules/ActivityLog/ItemLog/index.tsx +24 -0
  48. package/src/modules/ActivityLog/ItemLogUser/UserItem/EventItem/index.tsx +170 -0
  49. package/src/modules/ActivityLog/ItemLogUser/UserItem/EventItem/style.tsx +79 -0
  50. package/src/modules/ActivityLog/ItemLogUser/UserItem/index.tsx +46 -0
  51. package/src/modules/ActivityLog/ItemLogUser/UserItem/style.tsx +60 -0
  52. package/src/modules/ActivityLog/ItemLogUser/index.tsx +25 -0
  53. package/src/modules/ActivityLog/LogFilters/ContentFilter/index.tsx +79 -0
  54. package/src/modules/ActivityLog/LogFilters/DateFilter/index.tsx +91 -0
  55. package/src/modules/ActivityLog/LogFilters/DateFilter/style.tsx +208 -0
  56. package/src/modules/ActivityLog/LogFilters/EventFilter/index.tsx +80 -0
  57. package/src/modules/ActivityLog/LogFilters/OrderFilter/index.tsx +49 -0
  58. package/src/modules/ActivityLog/LogFilters/OrderFilter/style.tsx +35 -0
  59. package/src/modules/ActivityLog/LogFilters/UserFilter/index.tsx +79 -0
  60. package/src/modules/ActivityLog/TableHeader/index.tsx +72 -0
  61. package/src/modules/ActivityLog/TableHeader/style.tsx +73 -0
  62. package/src/modules/ActivityLog/constants.tsx +10 -0
  63. package/src/modules/ActivityLog/hooks.tsx +53 -0
  64. package/src/modules/ActivityLog/index.tsx +313 -0
  65. package/src/modules/ActivityLog/style.tsx +57 -0
  66. package/src/modules/ActivityLog/utils.tsx +31 -0
  67. package/src/modules/Categories/CategoriesList/index.tsx +1 -1
  68. package/src/modules/Forms/FormEditor/Editor/FormConfigPanel/Form/index.tsx +3 -0
  69. package/src/modules/Forms/FormEditor/Editor/FormConfigPanel/index.tsx +3 -0
  70. package/src/modules/Forms/FormEditor/Editor/index.tsx +5 -2
  71. package/src/modules/Forms/FormEditor/index.tsx +20 -3
  72. package/src/modules/GlobalEditor/Editor/index.tsx +3 -0
  73. package/src/modules/GlobalEditor/index.tsx +48 -9
  74. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +3 -1
  75. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +4 -1
  76. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +45 -6
  77. package/src/modules/PageEditor/Editor/index.tsx +4 -1
  78. package/src/modules/PageEditor/index.tsx +46 -7
  79. package/src/modules/Redirects/BulkHeader/TableHeader/style.tsx +1 -7
  80. package/src/modules/StructuredData/Form/index.tsx +56 -7
  81. package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +2 -2
  82. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +2 -8
  83. package/src/modules/StructuredData/StructuredDataList/hooks.tsx +9 -9
  84. package/src/modules/StructuredData/StructuredDataList/index.tsx +3 -4
  85. package/src/modules/Users/Roles/index.tsx +0 -2
  86. package/src/modules/Users/UserEdit/index.tsx +12 -28
  87. package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +1 -0
  88. package/src/modules/Users/UserList/UserItem/index.tsx +10 -25
  89. package/src/modules/Users/UserList/index.tsx +8 -10
  90. package/src/routes/multisite.tsx +9 -0
  91. package/src/themes/theme.json +2 -1
  92. package/src/types/forms.tsx +1 -0
  93. package/src/types/index.tsx +8 -0
  94. package/src/types/logs.tsx +12 -0
  95. package/src/components/TableFilters/SiteFilter/style.tsx +0 -28
@@ -12,7 +12,15 @@ import {
12
12
  ISite,
13
13
  } from "@ax/types";
14
14
  import { structuredDataActions } from "@ax/containers/StructuredData";
15
- import { MainWrapper, ErrorToast, Notification, Loading, CancelScheduleModal, ScheduleModal } from "@ax/components";
15
+ import {
16
+ MainWrapper,
17
+ ErrorToast,
18
+ Notification,
19
+ Loading,
20
+ CancelScheduleModal,
21
+ ScheduleModal,
22
+ RestoreModal,
23
+ } from "@ax/components";
16
24
  import { dateToString, getActivatedDataPacksIds, getDefaultTheme } from "@ax/helpers";
17
25
  import { appActions } from "@ax/containers/App";
18
26
  import { RouteLeavingGuard } from "@ax/guards";
@@ -50,6 +58,8 @@ const Form = (props: IProps) => {
50
58
  skipReviewOnPublish,
51
59
  setFormValues,
52
60
  setCurrentDataID,
61
+ schemaVersion,
62
+ restoreDataContent,
53
63
  } = props;
54
64
 
55
65
  const [isNewStructuredData, setIsNewStructuredData] = useState(!currentStructuredDataId);
@@ -57,6 +67,7 @@ const Form = (props: IProps) => {
57
67
  const [scheduleDate, setScheduleDate] = useState({ date: dateToString(new Date(), "yyy/MM/dd"), time: "12:00 am" });
58
68
  const { isOpen: isScheduleOpen, toggleModal: toggleScheduleModal } = useModal();
59
69
  const { isOpen: isCancelScheduleOpen, toggleModal: toggleCancelScheduleModal } = useModal();
70
+ const { isOpen: isRestoreOpen, toggleModal: toggleRestoreModal } = useModal();
60
71
  const { isDirty, resetDirty, setIsDirty } = useIsDirty(form);
61
72
  const path = site ? "/sites/pages" : "/data";
62
73
 
@@ -75,9 +86,12 @@ const Form = (props: IProps) => {
75
86
  ];
76
87
 
77
88
  const activatedDataPacksIds = getActivatedDataPacksIds(activatedDataPacks);
78
- const isDisabled =
89
+ const disabledDatapack =
79
90
  currentStructuredData.local &&
80
91
  !activatedDataPacksIds.some((pack: string) => currentStructuredData.dataPacks.includes(pack));
92
+ const isDeleted = !!form?.deleted;
93
+ const isDisabled = isDeleted || disabledDatapack;
94
+
81
95
  const isDataTranslatable = currentStructuredData && currentStructuredData.translate;
82
96
  const isScheduled = !!form && !!form.publicationScheduled;
83
97
  const status = isScheduled
@@ -88,6 +102,12 @@ const Form = (props: IProps) => {
88
102
 
89
103
  let title = "";
90
104
 
105
+ const packNotificationText =
106
+ "This content is part of disabled content type package. To edit it, you must first activate it.";
107
+
108
+ const deletedNotificationText =
109
+ "This content type has been deleted and can’t be edited. It will be permanently removed from the trash after 30 days.";
110
+
91
111
  const theme = getDefaultTheme();
92
112
 
93
113
  useEffect(() => {
@@ -194,9 +214,6 @@ const Form = (props: IProps) => {
194
214
  </>
195
215
  );
196
216
 
197
- const packNotificationText =
198
- "This content is part of disabled content type package. To edit it, you must first activate it.";
199
-
200
217
  const handleClickNotification = () => {
201
218
  getSiteDataPack(currentStructuredData.id);
202
219
  setHistoryPush("/sites/settings/content-types", false);
@@ -264,7 +281,7 @@ const Form = (props: IProps) => {
264
281
  };
265
282
 
266
283
  const downArrowMenu = {
267
- displayed: true,
284
+ displayed: !isDeleted,
268
285
  button: getPublishButton(form?.draft),
269
286
  options: [
270
287
  {
@@ -316,6 +333,15 @@ const Form = (props: IProps) => {
316
333
  }
317
334
  };
318
335
 
336
+ const handleRestorePage = async () => {
337
+ if (!currentStructuredDataId) return;
338
+ const isRestored = await restoreDataContent(currentStructuredDataId);
339
+ if (isRestored) {
340
+ isRestoreOpen && toggleRestoreModal();
341
+ await getDataContent(currentStructuredDataId);
342
+ }
343
+ };
344
+
319
345
  const mainScheduleModalAction = {
320
346
  title: "Schedule",
321
347
  onClick: handleSchedulePublication,
@@ -352,7 +378,7 @@ const Form = (props: IProps) => {
352
378
  errors={errors}
353
379
  scheduledPublication={form?.publicationScheduled}
354
380
  >
355
- {isDisabled && (
381
+ {disabledDatapack && (
356
382
  <S.NotificationWrapper>
357
383
  <Notification
358
384
  type="error"
@@ -362,6 +388,16 @@ const Form = (props: IProps) => {
362
388
  />
363
389
  </S.NotificationWrapper>
364
390
  )}
391
+ {isDeleted && (
392
+ <S.NotificationWrapper>
393
+ <Notification
394
+ type="error"
395
+ text={deletedNotificationText}
396
+ btnText="Restore content"
397
+ onClick={toggleRestoreModal}
398
+ />
399
+ </S.NotificationWrapper>
400
+ )}
365
401
  {notification && (
366
402
  <S.NotificationWrapper>
367
403
  <Notification
@@ -390,6 +426,15 @@ const Form = (props: IProps) => {
390
426
  mainModalAction={mainCancelScheduleModalAction}
391
427
  secondaryModalAction={secondaryCancelScheduleModalAction}
392
428
  />
429
+ <RestoreModal
430
+ isOpen={isRestoreOpen}
431
+ toggleModal={toggleRestoreModal}
432
+ {...{
433
+ isChild: false,
434
+ hasIssues: schemaVersion !== form?.schemaVersionTimestamp,
435
+ restorePage: handleRestorePage,
436
+ }}
437
+ />
393
438
  </>
394
439
  );
395
440
  };
@@ -408,6 +453,7 @@ interface IProps {
408
453
  errors: IErrorItem[];
409
454
  currentStructuredDataId: number | null;
410
455
  skipReviewOnPublish?: boolean;
456
+ schemaVersion: string;
411
457
  createStructuredDataContent: (payload: any) => Promise<boolean>;
412
458
  updateStructuredDataContent: (payload: any) => Promise<boolean>;
413
459
  setLanguage(lang: { locale: string; id: number | null }): void;
@@ -419,6 +465,7 @@ interface IProps {
419
465
  validateForm(publish?: boolean): Promise<boolean>;
420
466
  setFormValues(form: any): Promise<void>;
421
467
  setCurrentDataID(currentStructuredDataId: number | null): void;
468
+ restoreDataContent(dataID: number | number[]): Promise<boolean>;
422
469
  }
423
470
 
424
471
  const mapStateToProps = (state: IRootState) => ({
@@ -435,6 +482,7 @@ const mapStateToProps = (state: IRootState) => ({
435
482
  activatedDataPacks: state.dataPacks.activated,
436
483
  errors: state.structuredData.errors,
437
484
  skipReviewOnPublish: state.app.globalSettings.skipReviewOnPublish,
485
+ schemaVersion: state.structuredData.schemaVersion,
438
486
  });
439
487
 
440
488
  const mapDispatchToProps = {
@@ -449,6 +497,7 @@ const mapDispatchToProps = {
449
497
  validateForm: structuredDataActions.validateForm,
450
498
  setFormValues: structuredDataActions.setFormValues,
451
499
  setCurrentDataID: structuredDataActions.setCurrentDataID,
500
+ restoreDataContent: structuredDataActions.restoreStructuredDataContent,
452
501
  };
453
502
 
454
503
  export default connect(mapStateToProps, mapDispatchToProps)(Form);
@@ -12,7 +12,7 @@ import {
12
12
  TranslationsFilter,
13
13
  Tooltip,
14
14
  } from "@ax/components";
15
- import { IColumn, IQueryValue, ISchemaField, IStructuredDataQueryValues } from "@ax/types";
15
+ import { IColumn, IQueryValue, ISchemaField } from "@ax/types";
16
16
  import { getGlobalPageTypes } from "@ax/helpers";
17
17
 
18
18
  import * as S from "./style";
@@ -152,7 +152,7 @@ interface IProps {
152
152
  filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
153
153
  sortedListStatus: { isAscending: boolean; sortedByDate: boolean; sortedByTitle: boolean; sortedByURL: boolean };
154
154
  isAllPages: boolean;
155
- filterValues: IStructuredDataQueryValues;
155
+ filterValues: Record<string, IQueryValue[]>;
156
156
  checkState: Record<string, boolean>;
157
157
  categoryColumns: ISchemaField[];
158
158
  columns: IColumn[];
@@ -1,12 +1,6 @@
1
1
  import React from "react";
2
2
  import { BulkSelectionOptions } from "@ax/components";
3
- import {
4
- IColumn,
5
- IQueryValue,
6
- ISchemaField,
7
- IStructuredDataQueryValues,
8
- IStructuredDataSortedInitialState,
9
- } from "@ax/types";
3
+ import { IColumn, IQueryValue, ISchemaField, IStructuredDataSortedInitialState } from "@ax/types";
10
4
  import { usePermission } from "@ax/hooks";
11
5
  import TableHeader from "./TableHeader";
12
6
 
@@ -108,7 +102,7 @@ interface IProps {
108
102
  filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
109
103
  sortedListStatus: IStructuredDataSortedInitialState;
110
104
  isAllPages: boolean;
111
- filterValues: IStructuredDataQueryValues;
105
+ filterValues: Record<string, IQueryValue[]>;
112
106
  categoryColumns: ISchemaField[];
113
107
  columns: IColumn[];
114
108
  setColumns: (columns: IColumn[]) => void;
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useState } from "react";
2
- import { IQueryValue, IStructuredData, IStructuredDataQueryValues, IStructuredDataSortedInitialState } from "@ax/types";
2
+ import { IQueryValue, IStructuredData, IStructuredDataSortedInitialState } from "@ax/types";
3
3
 
4
4
  const useSortedListStatus = (): IUseSortedListStatus => {
5
5
  const sortedInitialState: IStructuredDataSortedInitialState = {
@@ -19,10 +19,10 @@ const useSortedListStatus = (): IUseSortedListStatus => {
19
19
 
20
20
  const useFilterQuery = (
21
21
  currentStructuredData: IStructuredData | null,
22
- values: Record<string, IStructuredDataQueryValues> | null
22
+ values: Record<string, Record<string, IQueryValue[]>> | null
23
23
  ): IUseFilterQuery => {
24
24
  const structuredDataType = currentStructuredData ? currentStructuredData.id : "all";
25
- const initialQueryValues: IStructuredDataQueryValues = {
25
+ const initialQueryValues: Record<string, IQueryValue[]> = {
26
26
  types: [{ value: "all", label: "All" }],
27
27
  translated: [{ value: "all", label: "All" }],
28
28
  liveStatus: [{ value: "all", label: "All" }],
@@ -32,7 +32,7 @@ const useFilterQuery = (
32
32
  related: [{ value: "all", label: "All" }],
33
33
  };
34
34
 
35
- const getFilterQuery = (filterValues: IStructuredDataQueryValues): string => {
35
+ const getFilterQuery = (filterValues: Record<string, IQueryValue[]>): string => {
36
36
  const { types, translated, liveStatus, order, filterSites, categories, related } = filterValues;
37
37
  let filterQuery = "";
38
38
 
@@ -45,11 +45,11 @@ const useFilterQuery = (
45
45
  return !stringValues.length
46
46
  ? filterQuery
47
47
  : filterQuery.length
48
- ? filterQuery.concat(`&${pointer}=${stringValues}`)
49
- : `&${pointer}=${stringValues}`;
48
+ ? filterQuery.concat(`&${pointer}=${stringValues}`)
49
+ : `&${pointer}=${stringValues}`;
50
50
  };
51
51
 
52
- const isNotInitialValue = (pointer: keyof IStructuredDataQueryValues) => {
52
+ const isNotInitialValue = (pointer: keyof Record<string, IQueryValue[]>) => {
53
53
  return filterValues[pointer] && initialQueryValues[pointer] !== filterValues[pointer];
54
54
  };
55
55
 
@@ -65,7 +65,7 @@ const useFilterQuery = (
65
65
  };
66
66
 
67
67
  const initState = values ? values : { all: initialQueryValues };
68
- const [state, setState] = useState<Record<string, IStructuredDataQueryValues>>(initState);
68
+ const [state, setState] = useState<Record<string, Record<string, IQueryValue[]>>>(initState);
69
69
 
70
70
  const initQuery = values && values[structuredDataType] ? getFilterQuery(values[structuredDataType]) : "";
71
71
  const [query, setQuery] = useState(initQuery);
@@ -118,7 +118,7 @@ interface IUseSortedListStatus {
118
118
  interface IUseFilterQuery {
119
119
  setFiltersSelection(pointer: string, filter: IQueryValue[], isAscendent?: boolean): void;
120
120
  resetFilterQuery(): void;
121
- filterValues: Record<string, IStructuredDataQueryValues>;
121
+ filterValues: Record<string, Record<string, IQueryValue[]>>;
122
122
  query: string;
123
123
  }
124
124
 
@@ -16,7 +16,6 @@ import {
16
16
  IPage,
17
17
  ICheck,
18
18
  IColumn,
19
- IStructuredDataQueryValues,
20
19
  IQueryValue,
21
20
  ISchemaField,
22
21
  IExportDataParams,
@@ -780,7 +779,7 @@ interface IDispatchProps {
780
779
  getStructuredDataContents(params: IGetStructuredDataParams): Promise<void>;
781
780
  setSelectedStructuredData(id: string, scope: string): void;
782
781
  deleteDataContent(dataID: number[]): Promise<boolean>;
783
- restoreDataContent(dataID: number | number[]): void;
782
+ restoreDataContent(dataID: number | number[]): Promise<boolean>;
784
783
  setDataStatus(dataID: number[], status: string): void;
785
784
  setFilter(filter: string | null): void;
786
785
  setCurrentPageID(currentPageID: number | null): ISetCurrentPageIDAction;
@@ -795,7 +794,7 @@ interface IDispatchProps {
795
794
  getAnalytics(siteId?: number | null): Promise<void>;
796
795
  setCurrentDataID(id: number | null): void;
797
796
  resetCurrentSiteErrorPages: () => Promise<void>;
798
- setContentFilters(contentFilters: Record<string, IStructuredDataQueryValues> | null): void;
797
+ setContentFilters(contentFilters: Record<string, Record<string, IQueryValue[]>> | null): void;
799
798
  checkUserSession(): Promise<void>;
800
799
  updateCurrentSearch(query: string): Promise<void>;
801
800
  resetCurrentData(): Promise<void>;
@@ -817,7 +816,7 @@ interface ICategoriesProps {
817
816
  currentSitePages: IPage[];
818
817
  currentSiteErrorPages: number[];
819
818
  skipReviewOnPublish?: boolean;
820
- contentFilters: Record<string, IStructuredDataQueryValues> | null;
819
+ contentFilters: Record<string, Record<string, IQueryValue[]>> | null;
821
820
  currentSearch: string;
822
821
  }
823
822
 
@@ -232,13 +232,11 @@ const mapStateToProps = (state: IRootState) => ({
232
232
  const mapDispatchToProps = {
233
233
  getRoles: usersActions.getRoles,
234
234
  activateRoles: usersActions.activateRoles,
235
- resetUserData: usersActions.resetUserData,
236
235
  setHistoryPush: appActions.setHistoryPush,
237
236
  };
238
237
 
239
238
  interface IDispatchProps {
240
239
  getRoles(params?: IGetRoles): Promise<void>;
241
- resetUserData(): void;
242
240
  setHistoryPush(route: string): void;
243
241
  activateRoles(params: IActivateRole): Promise<boolean>;
244
242
  }
@@ -4,20 +4,20 @@ import { connect } from "react-redux";
4
4
  import { appActions } from "@ax/containers/App";
5
5
  import { usersActions } from "@ax/containers/Users";
6
6
  import { Loading, MainWrapper, Modal, Toast } from "@ax/components";
7
- import { IRootState, ISite, ISiteRoles, IUser } from "@ax/types";
7
+ import { IRootState, ISite, IUser } from "@ax/types";
8
8
  import { useModal, useShouldBeSaved, useToast } from "@ax/hooks";
9
9
  import UserForm from "../UserForm";
10
10
 
11
11
  import * as S from "./style";
12
12
 
13
13
  const UserEdit = (props: IProps) => {
14
- const { user, updateUser, isSaving, isLoading, deleteUser, currentUser, setHistoryPush, site, sites } = props;
14
+ const { user, updateUser, isSaving, isLoading, deleteUser, currentUser, setHistoryPush, site, removeUserFromSite } =
15
+ props;
15
16
 
16
17
  const { timezone } = user;
17
18
  const initForm = { ...user };
18
19
  if (!timezone) initForm.timezone = "Europe/Madrid";
19
20
  const isSiteView = !!site;
20
- const sitesIds = sites.map((site: ISite) => site.id);
21
21
  const usersRoute = isSiteView ? "/sites/users" : "users";
22
22
  const [form, setForm] = useState<IUser>(initForm);
23
23
  const { isOpen, toggleModal } = useModal();
@@ -71,27 +71,10 @@ const UserEdit = (props: IProps) => {
71
71
  }
72
72
  : undefined;
73
73
 
74
- const getUpdatedSites = () => {
75
- const hasAll = form.roles.find((siteRole: ISiteRoles) => siteRole.siteId === "all");
76
- if (hasAll) {
77
- const oldRoleSites = form.roles.filter((siteRole: ISiteRoles) => siteRole.siteId !== "all");
78
- const roleSites = sitesIds
79
- .filter((siteId: number) => siteId !== site?.id)
80
- .map((siteId: number) => {
81
- return { siteId, roles: hasAll.roles };
82
- });
83
- return [...oldRoleSites, ...roleSites];
84
- } else {
85
- return form.roles.filter((siteRole: ISiteRoles) => siteRole.siteId !== site?.id);
86
- }
87
- };
88
-
89
- const removeUserFromSite = async () => {
90
- if (!user.id) return;
91
- const updatedSites = getUpdatedSites();
92
- const formWithUpdatedSites = { ...form, roles: updatedSites };
74
+ const handleRemoveUserFromSite = async () => {
75
+ if (!user.id || !site) return;
93
76
 
94
- const updated = await updateUser(user.id, formWithUpdatedSites, false, false);
77
+ const updated = await removeUserFromSite(user.id, site.id);
95
78
  if (updated) {
96
79
  setHistoryPush(usersRoute);
97
80
  }
@@ -99,7 +82,7 @@ const UserEdit = (props: IProps) => {
99
82
 
100
83
  const mainDeleteAction = {
101
84
  title: isSiteView ? "Remove user" : "Delete User",
102
- onClick: isSiteView ? removeUserFromSite : handleDelete,
85
+ onClick: isSiteView ? handleRemoveUserFromSite : handleDelete,
103
86
  };
104
87
 
105
88
  const secondaryDeleteAction = { title: "Cancel", onClick: toggleModal };
@@ -161,8 +144,9 @@ const UserEdit = (props: IProps) => {
161
144
  size="S"
162
145
  >
163
146
  <S.ModalContent>
164
- Before editing an user, <strong>select the user's permissions and site access</strong>. If you proceed without
165
- selecting any permissions, the user <strong>won't have access to anything or view any content</strong>.
147
+ Before editing an user, <strong>select the user&apos;s permissions and site access</strong>. If you proceed
148
+ without selecting any permissions, the user{" "}
149
+ <strong>won&apos;t have access to anything or view any content</strong>.
166
150
  </S.ModalContent>
167
151
  </Modal>
168
152
  {isVisible && <Toast {...toastProps} />}
@@ -173,7 +157,6 @@ const UserEdit = (props: IProps) => {
173
157
  const mapStateToProps = (state: IRootState) => ({
174
158
  user: state.users.userForm,
175
159
  site: state.sites.currentSiteInfo,
176
- sites: state.sites.sites,
177
160
  isSaving: state.app.isSaving,
178
161
  isLoading: state.app.isLoading,
179
162
  currentUser: state.users.currentUser,
@@ -183,12 +166,14 @@ interface IDispatchProps {
183
166
  setHistoryPush(path: string): void;
184
167
  updateUser(id: number, data: IUser, isProfile: boolean, isList?: boolean): Promise<boolean>;
185
168
  deleteUser(id: number): Promise<boolean>;
169
+ removeUserFromSite(userID: number, siteID: number): Promise<boolean>;
186
170
  }
187
171
 
188
172
  const mapDispatchToProps = {
189
173
  setHistoryPush: appActions.setHistoryPush,
190
174
  updateUser: usersActions.updateUser,
191
175
  deleteUser: usersActions.deleteUser,
176
+ removeUserFromSite: usersActions.removeUserFromSite,
192
177
  };
193
178
 
194
179
  interface IProfileProps {
@@ -197,7 +182,6 @@ interface IProfileProps {
197
182
  isSaving: boolean;
198
183
  isLoading: boolean;
199
184
  currentUser: IUser | null;
200
- sites: ISite[];
201
185
  }
202
186
 
203
187
  type IProps = IProfileProps & IDispatchProps;
@@ -27,6 +27,7 @@ const ActionsHeader = styled(Header)`
27
27
  const SiteWrapper = styled.div`
28
28
  width: 210px;
29
29
  position: relative;
30
+ padding-left: ${(p) => p.theme.spacing.s};
30
31
  `;
31
32
 
32
33
  const RolesWrapper = styled.div<{ isSite: boolean }>`
@@ -18,12 +18,13 @@ const UserItem = (props: IUserItemProps): JSX.Element => {
18
18
  onChange,
19
19
  onClick,
20
20
  deleteUser,
21
- updateUser,
22
21
  resendInvitation,
23
22
  isSiteView = false,
24
23
  roles,
25
24
  hoverCheck,
26
25
  toggleDeleteToast,
26
+ removeUserFromSite,
27
+ getUsers,
27
28
  } = props;
28
29
 
29
30
  const { isOpen, toggleModal } = useModal();
@@ -36,8 +37,6 @@ const UserItem = (props: IUserItemProps): JSX.Element => {
36
37
 
37
38
  const handleOnChange = (value: ICheck) => onChange(value);
38
39
 
39
- const sitesIds = sites.map((site: ISite) => site.id);
40
-
41
40
  const handleDeleteUser = async () => {
42
41
  const deleted = user.id ? await deleteUser(user.id) : false;
43
42
  if (deleted) {
@@ -78,35 +77,20 @@ const UserItem = (props: IUserItemProps): JSX.Element => {
78
77
 
79
78
  const menuOptions = user.status === "invited" ? [resendOption, deleteOption] : [deleteOption];
80
79
 
81
- const getUpdatedSites = () => {
82
- const hasAll = user.roles.find((siteRole: ISiteRoles) => siteRole.siteId === "all");
83
- if (hasAll) {
84
- const oldRoleSites = user.roles.filter((siteRole: ISiteRoles) => siteRole.siteId !== "all");
85
- const roleSites = sitesIds
86
- .filter((site: number) => site !== siteId)
87
- .map((siteId: number) => {
88
- return { siteId, roles: hasAll.roles };
89
- });
90
- return [...oldRoleSites, ...roleSites];
91
- } else {
92
- return user.roles.filter((siteRole: ISiteRoles) => siteRole.siteId !== siteId);
93
- }
94
- };
80
+ const handleRemoveUserFromSite = async () => {
81
+ if (!user.id || !siteId) return;
95
82
 
96
- const removeUserFromSite = async () => {
97
- if (!user.id) return;
98
- const updatedSites = getUpdatedSites();
99
- const updatedUser = { ...user, roles: updatedSites };
100
- const updated = await updateUser(user.id, updatedUser, false, true);
83
+ const updated = await removeUserFromSite(user.id, siteId);
101
84
  if (updated) {
102
85
  toggleModal();
103
86
  toggleDeleteToast();
87
+ getUsers();
104
88
  }
105
89
  };
106
90
 
107
91
  const mainDeleteAction = {
108
92
  title: isSiteView ? "Remove user" : "Delete user",
109
- onClick: isSiteView ? removeUserFromSite : handleDeleteUser,
93
+ onClick: isSiteView ? handleRemoveUserFromSite : handleDeleteUser,
110
94
  };
111
95
 
112
96
  const secondaryDeleteAction = { title: "Cancel", onClick: toggleModal };
@@ -253,18 +237,19 @@ interface IProps {
253
237
  onChange: (value: ICheck) => void;
254
238
  onClick: (id: number) => void;
255
239
  toggleDeleteToast: () => void;
240
+ getUsers: () => void;
256
241
  }
257
242
 
258
243
  const mapDispatchToProps = {
259
244
  deleteUser: usersActions.deleteUser,
260
- updateUser: usersActions.updateUser,
261
245
  resendInvitation: usersActions.resendInvitation,
246
+ removeUserFromSite: usersActions.removeUserFromSite,
262
247
  };
263
248
 
264
249
  interface IDispatchProps {
265
- updateUser(id: number, data: any, isProfile: boolean, isList?: boolean): Promise<boolean>;
266
250
  deleteUser(id: number): Promise<boolean>;
267
251
  resendInvitation(id: number): Promise<boolean>;
252
+ removeUserFromSite(userID: number, siteID: number): Promise<boolean>;
268
253
  }
269
254
 
270
255
  export type IUserItemProps = IProps & IDispatchProps;
@@ -35,7 +35,6 @@ const UserList = (props: IUserListProps): JSX.Element => {
35
35
  currentNavItem,
36
36
  setHistoryPush,
37
37
  getUser,
38
- resetUserData,
39
38
  sites,
40
39
  deleteUser,
41
40
  removeUsersBulk,
@@ -77,13 +76,14 @@ const UserList = (props: IUserListProps): JSX.Element => {
77
76
  setHoverCheck,
78
77
  } = useBulkSelection(usersIds);
79
78
 
79
+ const handleGetUsers = async () => {
80
+ const query = searchQuery ? (filterQuery ? `&query=${searchQuery}` : `?query=${searchQuery}`) : "";
81
+ const currentFilterQuerySite =
82
+ isSiteView && filterQuery ? `${filterQuery}&site=${currentSiteInfo.id}` : filterQuery;
83
+ await getUsers({ filterQuery: currentFilterQuerySite, query }, currentSiteInfo?.id);
84
+ };
85
+
80
86
  useEffect(() => {
81
- const handleGetUsers = async () => {
82
- const query = searchQuery ? (filterQuery ? `&query=${searchQuery}` : `?query=${searchQuery}`) : "";
83
- const currentFilterQuerySite =
84
- isSiteView && filterQuery ? `${filterQuery}&site=${currentSiteInfo.id}` : filterQuery;
85
- await getUsers({ filterQuery: currentFilterQuerySite, query }, currentSiteInfo?.id);
86
- };
87
87
  handleGetUsers();
88
88
  // eslint-disable-next-line react-hooks/exhaustive-deps
89
89
  }, [filterQuery, searchQuery]);
@@ -175,7 +175,6 @@ const UserList = (props: IUserListProps): JSX.Element => {
175
175
 
176
176
  const handleClick = (id: number | null) => async () => {
177
177
  if (id) {
178
- resetUserData();
179
178
  await getUser(id);
180
179
  setHistoryPush(`${BASE_URL}/edit`);
181
180
  }
@@ -269,6 +268,7 @@ const UserList = (props: IUserListProps): JSX.Element => {
269
268
  roles={roles}
270
269
  toggleDeleteToast={handleToggleToast}
271
270
  hoverCheck={checkState.hoverCheck}
271
+ getUsers={handleGetUsers}
272
272
  />
273
273
  );
274
274
  })
@@ -313,7 +313,6 @@ const mapStateToProps = (state: IRootState) => ({
313
313
  const mapDispatchToProps = {
314
314
  getUsers: usersActions.getUsers,
315
315
  getUser: usersActions.getUser,
316
- resetUserData: usersActions.resetUserData,
317
316
  setHistoryPush: appActions.setHistoryPush,
318
317
  deleteUser: usersActions.deleteUser,
319
318
  removeUsersBulk: sitesActions.removeUsersBulk,
@@ -324,7 +323,6 @@ const mapDispatchToProps = {
324
323
  interface IDispatchProps {
325
324
  getUsers(params: any, siteID?: number | null): Promise<void>;
326
325
  getUser(id: number): Promise<void>;
327
- resetUserData(): void;
328
326
  setHistoryPush(route: string): void;
329
327
  deleteUser(id: number[]): Promise<boolean>;
330
328
  removeUsersBulk(siteId: number, users: number[]): void;
@@ -15,6 +15,7 @@ import FormList from "../modules/Forms/FormList";
15
15
  import FormEditor from "../modules/Forms/FormEditor";
16
16
  import FormCategoriesList from "../modules/Forms/FormCategoriesList";
17
17
  import MediaGallery from "../modules/MediaGallery";
18
+ import ActivityLog from "../modules/ActivityLog";
18
19
 
19
20
  export default [
20
21
  {
@@ -214,4 +215,12 @@ export default [
214
215
  hideNav: true,
215
216
  permission: ["global.forms.createForms", "global.forms.editForms"],
216
217
  },
218
+ {
219
+ path: "/log",
220
+ component: ActivityLog,
221
+ name: "Log",
222
+ showInNav: true,
223
+ icon: "log",
224
+ permission: "global.accessToActivityLog",
225
+ },
217
226
  ];
@@ -2,7 +2,8 @@
2
2
  "id": "cms-theme",
3
3
  "radii": {
4
4
  "xs": "2px",
5
- "s": "4px"
5
+ "s": "4px",
6
+ "m": "8px"
6
7
  },
7
8
  "spacing": {
8
9
  "xxs": "4px",
@@ -17,6 +17,7 @@ export type FormContent = {
17
17
  originalLanguage: number;
18
18
  editorID: number;
19
19
  formInUse: { page: number[] } | null;
20
+ deleted: boolean;
20
21
  };
21
22
 
22
23
  export type FormLanguage = { id: number; site: number | null; language: number };
@@ -14,6 +14,7 @@ import { IAnalyticsState } from "@ax/containers/Analytics/reducer";
14
14
  import { IIntegrationsState } from "@ax/containers/Integrations/reducer";
15
15
  import { IFileDriveState } from "@ax/containers/FileDrive/reducer";
16
16
  import { IFormsState } from "@ax/containers/Forms/reducer";
17
+ import { IActivityLogState } from "@ax/containers/ActivityLog/reducer";
17
18
 
18
19
  import {
19
20
  GetFormsParams,
@@ -40,6 +41,8 @@ import {
40
41
  IImageFolder,
41
42
  } from "./files";
42
43
 
44
+ import { ILogItemsByDay, ILogItemsByUser, ILogContent } from "./logs";
45
+
43
46
  export interface IBreadcrumbItem {
44
47
  editorID: number;
45
48
  component: string;
@@ -123,6 +126,7 @@ export interface IAPIPage {
123
126
  originalLanguage: number;
124
127
  language: number;
125
128
  publicationScheduled: string | null;
129
+ schemaVersionTimestamp: string;
126
130
  }
127
131
 
128
132
  export interface IPage extends IAPIPage {
@@ -253,6 +257,7 @@ export interface IRootState {
253
257
  integrations: IIntegrationsState;
254
258
  fileDrive: IFileDriveState;
255
259
  forms: IFormsState;
260
+ activityLog: IActivityLogState;
256
261
  }
257
262
 
258
263
  export interface IStyledProps {
@@ -549,6 +554,7 @@ export interface IStructuredDataContent {
549
554
  fromPage: boolean;
550
555
  dataPacks: string[];
551
556
  publicationScheduled: string | null;
557
+ schemaVersionTimestamp: string;
552
558
  }
553
559
 
554
560
  export interface IStructuredDataCategory {
@@ -1137,3 +1143,5 @@ export type {
1137
1143
  IImage,
1138
1144
  IImageFolder,
1139
1145
  };
1146
+
1147
+ export type { ILogItemsByDay, ILogItemsByUser, ILogContent };
@@ -0,0 +1,12 @@
1
+ import { LogActivityGroupedDayDTO, LogActivityGroupedUserDTO, PaginationResponse } from "@griddo/api-types";
2
+
3
+ export type ILogItemsByUser = Omit<PaginationResponse<LogActivityGroupedUserDTO<LogActivityGroupedUserDTO[]>>, "page">;
4
+
5
+ export type ILogItemsByDay = Omit<PaginationResponse<LogActivityGroupedDayDTO>, "page">;
6
+
7
+ export type ILogContent = {
8
+ id: number;
9
+ title: string | undefined;
10
+ contentType: string | undefined;
11
+ type: string;
12
+ };