@griddo/ax 10.1.96 → 10.2.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 (201) hide show
  1. package/package.json +3 -2
  2. package/src/__mocks__/axios/Roles.ts +10 -0
  3. package/src/__mocks__/axios/UserList.ts +545 -0
  4. package/src/__mocks__/store/GenericStore.ts +25 -0
  5. package/src/__mocks__/store/Roles.ts +1050 -0
  6. package/src/__mocks__/store/SitesList.ts +7 -1
  7. package/src/__mocks__/store/UserList.ts +482 -0
  8. package/src/__mocks__/store/UsersCreate.ts +298 -0
  9. package/src/__tests__/components/Avatar/Avatar.test.tsx +49 -48
  10. package/src/__tests__/components/ConfigPanel/ConfigPanel.test.tsx +2 -0
  11. package/src/__tests__/components/ConfigPanel/Form/Form.test.tsx +2 -0
  12. package/src/__tests__/components/ConfigPanel/GlobalPageForm/GlobalPageForm.test.tsx +25 -0
  13. package/src/__tests__/components/Fields/Button/Button.test.tsx +2 -2
  14. package/src/__tests__/components/Fields/ImageField/ImageField.test.tsx +2 -0
  15. package/src/__tests__/components/Fields/IntegrationsField/IntegrationsField.test.tsx +44 -2
  16. package/src/__tests__/components/Fields/Tooltip/Tooltip.test.tsx +0 -1
  17. package/src/__tests__/components/Gallery/Gallery.test.tsx +4 -0
  18. package/src/__tests__/components/Gallery/GalleryPanel/DetailPanel/DetailPanel.test.tsx +14 -0
  19. package/src/__tests__/components/Gallery/GalleryPanel/GalleryPanel.test.tsx +2 -1
  20. package/src/__tests__/components/Lists/Lists.test.tsx +3 -3
  21. package/src/__tests__/components/Login/Login.test.tsx +1 -1
  22. package/src/__tests__/components/TableFilters/DateFilter/DateFilter.test.tsx +1 -1
  23. package/src/__tests__/components/TableFilters/NameFilter/NameFilter.test.tsx +1 -1
  24. package/src/__tests__/components/TableFilters/RoleFilter/RoleFilter.test.tsx +165 -0
  25. package/src/__tests__/components/TableFilters/StatusFilter/StatusFilter.test.tsx +2 -2
  26. package/src/__tests__/components/TableFilters/UsersFilter/UsersFilter.test.tsx +153 -0
  27. package/src/__tests__/components/Tabs/Tabs.test.tsx +1 -1
  28. package/src/__tests__/modules/Settings/Integrations/Integrations.test.tsx +6 -0
  29. package/src/__tests__/modules/Sites/Sites.test.tsx +2 -1
  30. package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +14 -5
  31. package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +6 -4
  32. package/src/__tests__/modules/Users/Roles/BulkHeader/BulkHeader.test.tsx +158 -0
  33. package/src/__tests__/modules/Users/Roles/Roles.test.tsx +619 -0
  34. package/src/__tests__/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/RoleItem.test.tsx +107 -0
  35. package/src/__tests__/modules/Users/UserCreate/SiteItem/RolesModal/RolesModal.test.tsx +159 -0
  36. package/src/__tests__/modules/Users/UserCreate/SiteItem/SiteItem.test.tsx +175 -0
  37. package/src/__tests__/modules/Users/UserCreate/UserCreate.test.tsx +320 -0
  38. package/src/__tests__/modules/Users/UserList/UserItem/UserItem.test.tsx +417 -0
  39. package/src/__tests__/modules/Users/UserList/UserList.test.tsx +310 -0
  40. package/src/api/index.tsx +2 -0
  41. package/src/api/roles.tsx +77 -0
  42. package/src/api/users.tsx +22 -2
  43. package/src/components/ActionMenu/index.tsx +12 -6
  44. package/src/components/Avatar/index.tsx +5 -3
  45. package/src/components/Avatar/style.tsx +8 -9
  46. package/src/components/BulkSelectionOptions/index.tsx +19 -12
  47. package/src/components/BulkSelectionOptions/style.tsx +6 -11
  48. package/src/components/ConfigPanel/Form/index.tsx +24 -1
  49. package/src/components/ConfigPanel/GlobalPageForm/index.tsx +17 -4
  50. package/src/components/ElementsTooltip/index.tsx +1 -1
  51. package/src/components/Fields/IntegrationsField/index.tsx +5 -6
  52. package/src/components/Fields/RadioField/index.tsx +1 -1
  53. package/src/components/Fields/ReferenceField/AutoPanel/index.tsx +3 -3
  54. package/src/components/Fields/ReferenceField/ItemList/index.tsx +1 -1
  55. package/src/components/Fields/ReferenceField/ManualPanel/index.tsx +3 -3
  56. package/src/components/Fields/TagsField/index.tsx +4 -2
  57. package/src/components/Fields/TextField/index.tsx +3 -0
  58. package/src/components/Fields/UrlField/index.tsx +5 -5
  59. package/src/components/Gallery/GalleryPanel/DetailPanel/index.tsx +42 -15
  60. package/src/components/Gallery/GalleryPanel/GalleryDragAndDrop/index.tsx +1 -1
  61. package/src/components/Gallery/GalleryPanel/index.tsx +20 -5
  62. package/src/components/Gallery/index.tsx +12 -6
  63. package/src/components/Icon/index.tsx +9 -1
  64. package/src/components/MainWrapper/AppBar/atoms.tsx +2 -2
  65. package/src/components/MainWrapper/AppBar/index.tsx +12 -10
  66. package/src/components/MainWrapper/AppBar/style.tsx +2 -1
  67. package/src/components/Modal/style.tsx +2 -2
  68. package/src/components/Nav/index.tsx +12 -2
  69. package/src/components/PageFinder/index.tsx +2 -2
  70. package/src/components/SearchField/index.tsx +3 -8
  71. package/src/components/TableFilters/PermissionsFilter/index.tsx +50 -0
  72. package/src/{modules/Users/UserList/HeaderMenus/Name → components/TableFilters/PermissionsFilter}/style.tsx +6 -2
  73. package/src/components/TableFilters/RoleFilter/index.tsx +61 -0
  74. package/src/components/TableFilters/RoleFilter/style.tsx +28 -0
  75. package/src/components/TableFilters/UsersFilter/index.tsx +55 -0
  76. package/src/components/TableFilters/UsersFilter/style.tsx +31 -0
  77. package/src/components/TableFilters/index.tsx +6 -0
  78. package/src/components/TableList/TableItem/style.tsx +2 -2
  79. package/src/components/TableList/style.tsx +1 -1
  80. package/src/components/index.tsx +7 -1
  81. package/src/containers/App/actions.tsx +3 -3
  82. package/src/containers/PageEditor/actions.tsx +14 -20
  83. package/src/containers/Redirects/actions.tsx +1 -0
  84. package/src/containers/Sites/actions.tsx +22 -14
  85. package/src/containers/Sites/interfaces.tsx +3 -3
  86. package/src/containers/Sites/reducer.tsx +1 -1
  87. package/src/containers/StructuredData/actions.tsx +15 -3
  88. package/src/containers/Users/actions.tsx +160 -26
  89. package/src/containers/Users/constants.tsx +8 -10
  90. package/src/containers/Users/interfaces.tsx +25 -3
  91. package/src/containers/Users/reducer.tsx +24 -15
  92. package/src/guards/index.tsx +2 -1
  93. package/src/guards/restricted/index.tsx +21 -0
  94. package/src/hooks/index.tsx +3 -0
  95. package/src/hooks/users.tsx +38 -0
  96. package/src/modules/App/Routing/NavMenu/NavItem/index.tsx +10 -5
  97. package/src/modules/App/Routing/NavMenu/index.tsx +16 -7
  98. package/src/modules/App/Routing/PrivateRoute/index.tsx +13 -5
  99. package/src/modules/App/Routing/index.tsx +17 -6
  100. package/src/modules/Categories/CategoriesList/BulkHeader/TableHeader/style.tsx +1 -0
  101. package/src/modules/Categories/CategoriesList/BulkHeader/index.tsx +2 -10
  102. package/src/modules/Categories/CategoriesList/CategoryItem/index.tsx +27 -12
  103. package/src/modules/Categories/CategoriesList/CategoryItem/style.tsx +4 -2
  104. package/src/modules/Categories/CategoriesList/index.tsx +27 -8
  105. package/src/modules/Content/BulkHeader/index.tsx +7 -3
  106. package/src/modules/Content/PageImporter/index.tsx +1 -1
  107. package/src/modules/Content/PageItem/index.tsx +45 -31
  108. package/src/modules/Content/PageItem/style.tsx +2 -1
  109. package/src/modules/Content/index.tsx +22 -13
  110. package/src/modules/FramePreview/index.tsx +2 -2
  111. package/src/modules/GlobalEditor/index.tsx +68 -53
  112. package/src/modules/GlobalSettings/index.tsx +2 -0
  113. package/src/modules/Navigation/Defaults/BulkHeader/index.tsx +2 -10
  114. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +9 -11
  115. package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/index.tsx +5 -1
  116. package/src/modules/Navigation/Defaults/DefaultsEditor/index.tsx +10 -5
  117. package/src/modules/Navigation/Defaults/Item/index.tsx +41 -27
  118. package/src/modules/Navigation/Defaults/Item/style.tsx +2 -1
  119. package/src/modules/Navigation/Defaults/index.tsx +29 -10
  120. package/src/modules/Navigation/Menus/List/Table/Header/index.tsx +7 -5
  121. package/src/modules/Navigation/Menus/List/Table/Item/index.tsx +10 -10
  122. package/src/modules/Navigation/Menus/List/Table/Item/style.tsx +2 -1
  123. package/src/modules/Navigation/Menus/List/index.tsx +6 -2
  124. package/src/modules/Navigation/Menus/index.tsx +12 -7
  125. package/src/modules/PageEditor/Editor/index.tsx +5 -1
  126. package/src/modules/PageEditor/PageBrowser/index.tsx +9 -3
  127. package/src/modules/PageEditor/index.tsx +67 -57
  128. package/src/modules/Redirects/index.tsx +97 -98
  129. package/src/modules/Settings/ContentTypes/DataPacks/AddModal/index.tsx +5 -1
  130. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +8 -9
  131. package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +7 -3
  132. package/src/modules/Settings/ContentTypes/DataPacks/Config/index.tsx +5 -1
  133. package/src/modules/Settings/ContentTypes/DataPacks/Item/index.tsx +5 -1
  134. package/src/modules/Settings/Integrations/BulkHeader/index.tsx +2 -17
  135. package/src/modules/Settings/Integrations/IntegrationForm/index.tsx +6 -2
  136. package/src/modules/Settings/Integrations/IntegrationItem/index.tsx +18 -8
  137. package/src/modules/Settings/Integrations/IntegrationItem/style.tsx +6 -3
  138. package/src/modules/Settings/Integrations/index.tsx +32 -7
  139. package/src/modules/Settings/Languages/LanguagePanel/index.tsx +6 -2
  140. package/src/modules/Settings/Languages/Table/Header/index.tsx +4 -2
  141. package/src/modules/Settings/Languages/Table/Item/index.tsx +19 -43
  142. package/src/modules/Settings/Languages/Table/Item/style.tsx +11 -54
  143. package/src/modules/Settings/Languages/index.tsx +2 -2
  144. package/src/modules/Settings/SeoAnalyticsSettings/Analytics/index.tsx +2 -4
  145. package/src/modules/Settings/SeoAnalyticsSettings/index.tsx +4 -2
  146. package/src/modules/Settings/Social/index.tsx +1 -1
  147. package/src/modules/Settings/index.tsx +17 -11
  148. package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +31 -18
  149. package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +21 -12
  150. package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +31 -18
  151. package/src/modules/Sites/SitesList/RecentSiteItem/index.tsx +1 -1
  152. package/src/modules/Sites/SitesList/index.tsx +16 -24
  153. package/src/modules/Sites/index.tsx +7 -3
  154. package/src/modules/StructuredData/Form/index.tsx +1 -1
  155. package/src/modules/StructuredData/StructuredDataList/BulkHeader/index.tsx +8 -5
  156. package/src/modules/StructuredData/StructuredDataList/ContentFilters/index.tsx +8 -5
  157. package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +29 -15
  158. package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +50 -19
  159. package/src/modules/StructuredData/StructuredDataList/index.tsx +9 -6
  160. package/src/modules/Users/Profile/index.tsx +16 -4
  161. package/src/modules/Users/Roles/BulkHeader/TableHeader/index.tsx +52 -0
  162. package/src/modules/Users/Roles/BulkHeader/TableHeader/style.tsx +43 -0
  163. package/src/modules/Users/Roles/BulkHeader/index.tsx +74 -0
  164. package/src/modules/Users/Roles/RoleItem/index.tsx +104 -0
  165. package/src/modules/Users/Roles/RoleItem/style.tsx +127 -0
  166. package/src/modules/Users/Roles/SideModal/index.tsx +81 -0
  167. package/src/modules/Users/Roles/SideModal/style.tsx +132 -0
  168. package/src/modules/Users/Roles/hooks.tsx +78 -0
  169. package/src/modules/Users/Roles/index.tsx +256 -0
  170. package/src/modules/Users/Roles/style.tsx +23 -0
  171. package/src/modules/Users/Roles/utils.tsx +12 -0
  172. package/src/modules/Users/UserCreate/OptionItem/index.tsx +45 -0
  173. package/src/modules/Users/UserCreate/OptionItem/style.tsx +48 -0
  174. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/index.tsx +48 -0
  175. package/src/modules/Users/UserCreate/SiteItem/RolesModal/RoleItem/style.tsx +42 -0
  176. package/src/modules/Users/UserCreate/SiteItem/RolesModal/index.tsx +140 -0
  177. package/src/modules/Users/UserCreate/SiteItem/RolesModal/style.tsx +94 -0
  178. package/src/modules/Users/UserCreate/SiteItem/index.tsx +103 -22
  179. package/src/modules/Users/UserCreate/SiteItem/style.tsx +49 -6
  180. package/src/modules/Users/UserCreate/index.tsx +278 -121
  181. package/src/modules/Users/UserCreate/style.tsx +71 -4
  182. package/src/modules/Users/UserEdit/index.tsx +71 -24
  183. package/src/modules/Users/UserForm/atoms.tsx +40 -8
  184. package/src/modules/Users/UserForm/index.tsx +335 -116
  185. package/src/modules/Users/UserForm/style.tsx +70 -6
  186. package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +61 -31
  187. package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +18 -4
  188. package/src/modules/Users/UserList/BulkHeader/index.tsx +10 -3
  189. package/src/modules/Users/UserList/UserItem/index.tsx +121 -38
  190. package/src/modules/Users/UserList/UserItem/style.tsx +32 -14
  191. package/src/modules/Users/UserList/hooks.tsx +13 -8
  192. package/src/modules/Users/UserList/index.tsx +67 -29
  193. package/src/modules/Users/UserList/utils.tsx +1 -1
  194. package/src/modules/Users/index.tsx +20 -3
  195. package/src/routes/index.tsx +9 -17
  196. package/src/routes/multisite.tsx +73 -8
  197. package/src/routes/site.tsx +96 -10
  198. package/src/types/index.tsx +42 -1
  199. package/tsconfig.paths.json +1 -0
  200. package/src/__tests__/components/Avatar/__snapshots__/Avatar.test.tsx.snap +0 -61
  201. package/src/modules/Users/UserList/HeaderMenus/Name/index.tsx +0 -55
@@ -2,36 +2,65 @@ import React, { useState, useEffect } from "react";
2
2
  import { connect } from "react-redux";
3
3
 
4
4
  import { appActions } from "@ax/containers/App";
5
- import { Button, ErrorToast, FieldsBehavior, SearchField } from "@ax/components";
6
- import { IImage, IRootState, IUser, ISite } from "@ax/types";
7
- import { useModal } from "@ax/hooks";
5
+ import { Button, ErrorToast, FieldsBehavior, SearchField, Tooltip } from "@ax/components";
6
+ import { IImage, IRootState, IUser, ISite, IRole, ISiteRoles } from "@ax/types";
7
+ import { useModal, usePermission } from "@ax/hooks";
8
8
  import { RouteLeavingGuard } from "@ax/guards";
9
9
 
10
10
  import { timezones } from "../../Settings/Globals/constants";
11
- import { PasswordModal } from "./atoms";
11
+ import { PasswordModal, TypeChangeModal } from "./atoms";
12
12
  import { shouldBeSaved } from "./helpers";
13
13
 
14
14
  import SiteItem from "../UserCreate/SiteItem";
15
+ import RolesModal from "../UserCreate/SiteItem/RolesModal";
16
+ import RoleItem from "../UserCreate/SiteItem/RolesModal/RoleItem";
17
+ import OptionItem from "../UserCreate/OptionItem";
15
18
 
16
19
  import * as S from "./style";
17
20
 
18
21
  const UserForm = (props: IProps) => {
19
- const { user, form, setForm, setHistoryPush, currentUser, sites, readOnlySites = false, isSiteView = false } = props;
20
- const { id, username, name, email, image, company, description, timezone } = form;
22
+ const {
23
+ user,
24
+ form,
25
+ setForm,
26
+ setHistoryPush,
27
+ currentUser,
28
+ sites,
29
+ roles,
30
+ readOnlySites = false,
31
+ isSiteView = false,
32
+ site,
33
+ } = props;
21
34
 
22
- const getSortedItemsByName = (items: any) => items.sort((item1: any, item2: any) => item1.name.localeCompare(item2.name));
35
+ const { id, username, name, email, image, company, description, timezone, isSuperAdmin } = form;
36
+
37
+ const getSortedItemsByName = (items: ISite[]) =>
38
+ items.sort((item1: ISite, item2: ISite) => item1.name.localeCompare(item2.name));
23
39
 
24
40
  const sortedByNameSites = getSortedItemsByName(sites);
25
41
 
26
42
  const { isOpen, toggleModal } = useModal(false);
43
+ const { isOpen: isOpenRoleModal, toggleModal: toggleRoleModal } = useModal(false);
44
+ const { isOpen: isOpenTypeChangeModal, toggleModal: toggleTypeChangeModal } = useModal(false);
27
45
  const [sitesList, setSiteList] = useState(sortedByNameSites);
28
46
  const [showMore, setShowMore] = useState(false);
29
47
 
48
+ const isAllowedToAccessSiteGallery = usePermission("mediaGallery.accessToSiteGallery");
49
+ const isAllowedToAccessGlobalGallery = usePermission("global.mediaGallery.accessToGlobalGallery");
50
+
51
+ const allowedRoutes = ["/profile"];
52
+ const { isDirty } = shouldBeSaved(user, form);
53
+
54
+ const isSameUser = currentUser && currentUser.id === user.id;
55
+ const isEditDisabled = currentUser && !currentUser.isSuperAdmin && isSiteView && !isSameUser;
56
+ const isAvatarDisabled =
57
+ (isSiteView && !isAllowedToAccessSiteGallery) || (!isSiteView && !isAllowedToAccessGlobalGallery);
58
+
30
59
  useEffect(() => {
31
- if (!form.sites[0]) {
60
+ if (form.roles.length === 0) {
32
61
  setShowMore(true);
33
62
  }
34
- }, [form.sites]);
63
+ }, [form.roles]);
35
64
 
36
65
  const handleImageChange = (value: IImage) => setForm({ ...form, image: value || null });
37
66
  const handleNameChange = (value: string) => setForm({ ...form, name: value });
@@ -41,40 +70,92 @@ const UserForm = (props: IProps) => {
41
70
  const handleDescriptionChange = (value: string) => setForm({ ...form, description: value });
42
71
  const handleTimezoneChange = (value: string) => setForm({ ...form, timezone: value });
43
72
 
73
+ const toggleSuperAdmin = () => {
74
+ setForm({ ...form, isSuperAdmin: !form.isSuperAdmin });
75
+ toggleTypeChangeModal();
76
+ };
77
+
78
+ const handleAddRoles = (newRoles: ISiteRoles) => {
79
+ const oldRoles =
80
+ newRoles.siteId === "all"
81
+ ? form.roles.filter((role: ISiteRoles) => role.siteId === "global")
82
+ : form.roles.filter((role: ISiteRoles) => role.siteId !== newRoles.siteId);
83
+
84
+ const updatedRoles = [...oldRoles, newRoles];
85
+
86
+ setForm({ ...form, roles: updatedRoles });
87
+ };
88
+
89
+ const getAllSitesWithRoles = () => {
90
+ const siteRoles = form.roles.find((role: ISiteRoles) => role.siteId === "all");
91
+ return sites.map((site: ISite) => {
92
+ return { siteId: site.id, roles: siteRoles ? siteRoles.roles : [] };
93
+ });
94
+ };
95
+
96
+ const handleAddSiteRoles = (newRoles: number[]) => {
97
+ if (!site) return;
98
+ const hasAllSites = form.roles.find((role: ISiteRoles) => role.siteId === "all");
99
+ const userRoles = hasAllSites ? getAllSitesWithRoles() : form.roles;
100
+ const oldRoles = userRoles.filter((role: ISiteRoles) => role.siteId !== site.id);
101
+ const newSiteRoles = { siteId: site.id, roles: newRoles };
102
+ const updatedRoles = newRoles.length > 0 ? [...oldRoles, newSiteRoles] : oldRoles;
103
+ setForm({ ...form, roles: updatedRoles });
104
+ };
105
+
106
+ const handleChangeType = (value: string) => {
107
+ if ((isSuperAdmin && value === "member") || (!isSuperAdmin && value === "superadmin")) {
108
+ toggleTypeChangeModal();
109
+ }
110
+ };
111
+
44
112
  const modalProps = {
45
113
  isOpen,
46
114
  toggleModal,
47
115
  id,
48
116
  };
49
117
 
118
+ const mainTypeChangeModalAction = {
119
+ title: "Change user type",
120
+ onClick: toggleSuperAdmin,
121
+ };
122
+
123
+ const secondaryTypeChangeModalAction = { title: "Cancel", onClick: toggleTypeChangeModal };
124
+
125
+ const modalTypeChangeProps = {
126
+ isOpen: isOpenTypeChangeModal,
127
+ toggleModal: toggleTypeChangeModal,
128
+ email,
129
+ isSuperAdmin,
130
+ mainModalAction: mainTypeChangeModalAction,
131
+ secondaryModalAction: secondaryTypeChangeModalAction,
132
+ };
133
+
50
134
  const action = (path: string) => setHistoryPush(path);
51
135
 
52
136
  const filterSitesList = (query: string) => {
53
- const filteredSites = sites.filter((site: any) =>
137
+ const filteredSites = sites.filter((site: ISite) =>
54
138
  site.name.toLowerCase().replace(" ", "").includes(query.toLowerCase().replace(" ", ""))
55
139
  );
56
140
  const sortedSites = getSortedItemsByName(filteredSites);
57
141
  setSiteList(sortedSites);
58
142
  };
59
143
 
60
- const selectAllSites = () => {
61
- const currentSites = form.sites;
62
- if (currentSites.includes("all")) {
63
- setForm({ ...form, sites: [] });
64
- setShowMore(true);
65
- } else {
66
- setForm({ ...form, sites: ["all"] });
67
- }
68
- };
69
-
70
144
  const selectSite = (id: number | string) => {
71
- const currentSites = form.sites;
72
- if (currentSites.includes(id)) {
73
- const filteredSites = [...currentSites].filter((site) => site && site !== id);
74
- setForm({ ...form, sites: filteredSites });
145
+ const { roles } = form;
146
+ const hasSite = roles.find((siteRole: ISiteRoles) => siteRole.siteId === id);
147
+ if (hasSite) {
148
+ const filteredRoles = roles.filter(
149
+ (siteRole: ISiteRoles) =>
150
+ (Number.isInteger(id) && siteRole.siteId !== id && siteRole.siteId !== "all") ||
151
+ (!Number.isInteger(id) && siteRole.siteId !== id)
152
+ );
153
+ setForm({ ...form, roles: filteredRoles });
154
+ if (id === "all") {
155
+ setShowMore(true);
156
+ }
75
157
  } else {
76
- const filteredSites = [...currentSites].filter((site) => site && site !== "all");
77
- setForm({ ...form, sites: [...filteredSites, id] });
158
+ setForm({ ...form, roles: [...form.roles, { siteId: id, roles: [] }] });
78
159
  }
79
160
  };
80
161
 
@@ -84,14 +165,9 @@ const UserForm = (props: IProps) => {
84
165
  </>
85
166
  );
86
167
 
87
- const allowedRoutes = ["/profile"];
88
- const { isDirty } = shouldBeSaved(user, form);
89
-
90
- const isSameUser = currentUser.id === user.id;
91
-
92
168
  const ShowMoreButton = () => (
93
169
  <Button type="button" buttonStyle="line" onClick={() => setShowMore(!showMore)}>
94
- {showMore ? "Show less sites" : "Show more sites"}
170
+ {showMore ? "Show less sites & roles" : "Show more sites & roles"}
95
171
  </Button>
96
172
  );
97
173
 
@@ -100,6 +176,32 @@ const UserForm = (props: IProps) => {
100
176
  return currentSite.name;
101
177
  };
102
178
 
179
+ const sitesID = form.roles.map((roleSite: ISiteRoles) => roleSite.siteId);
180
+
181
+ const siteSelectedRoles = isSiteView
182
+ ? form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site?.id || roleSite.siteId === "all")
183
+ : undefined;
184
+
185
+ const rolesAll = form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "all");
186
+ const allDescription =
187
+ "The user will have access to all existing and future sites depending on the permissions assigned to the role.";
188
+
189
+ const rolesGlobal = form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "global");
190
+ const globalDescription = "Global data will be accessible to the user based on the permissions assigned to the role.";
191
+
192
+ const AvatarField = () => (
193
+ <FieldsBehavior
194
+ title="Avatar profile"
195
+ name="image"
196
+ value={image}
197
+ fieldType="ImageField"
198
+ onChange={handleImageChange}
199
+ helptext="Customize how people see you."
200
+ disabled={isEditDisabled || isAvatarDisabled}
201
+ site={site}
202
+ />
203
+ );
204
+
103
205
  return (
104
206
  <>
105
207
  <RouteLeavingGuard when={isDirty} action={action} text={text} allowedRoutes={allowedRoutes} />
@@ -107,14 +209,13 @@ const UserForm = (props: IProps) => {
107
209
  <S.Wrapper>
108
210
  <S.SubTitle>USER DATA</S.SubTitle>
109
211
  <S.NameTitle>{user.name}</S.NameTitle>
110
- <FieldsBehavior
111
- title="Avatar profile"
112
- name="image"
113
- value={image}
114
- fieldType="ImageField"
115
- onChange={handleImageChange}
116
- helptext="Customize how people see you."
117
- />
212
+ {isAvatarDisabled ? (
213
+ <Tooltip content="You don't have the permissions to access the images gallery." top={10}>
214
+ <AvatarField />
215
+ </Tooltip>
216
+ ) : (
217
+ <AvatarField />
218
+ )}
118
219
  <FieldsBehavior
119
220
  title="Name"
120
221
  name="name"
@@ -123,6 +224,7 @@ const UserForm = (props: IProps) => {
123
224
  mandatory
124
225
  value={name}
125
226
  onChange={handleNameChange}
227
+ disabled={isEditDisabled}
126
228
  />
127
229
  <FieldsBehavior
128
230
  title="Email"
@@ -132,7 +234,7 @@ const UserForm = (props: IProps) => {
132
234
  mandatory
133
235
  value={email}
134
236
  onChange={handleEmailChange}
135
- disabled={isSameUser}
237
+ disabled={isSameUser || isEditDisabled}
136
238
  />
137
239
  <FieldsBehavior
138
240
  title="Alias"
@@ -143,14 +245,16 @@ const UserForm = (props: IProps) => {
143
245
  value={username}
144
246
  onChange={handleUsernameChange}
145
247
  prefix="@"
248
+ disabled={isEditDisabled}
146
249
  />
147
250
  <FieldsBehavior
148
251
  title="Company"
149
252
  name="company"
150
253
  fieldType="TextField"
151
254
  placeholder="Type a company"
152
- value={company}
255
+ value={company || ""}
153
256
  onChange={handleCompanyChange}
257
+ disabled={isEditDisabled}
154
258
  />
155
259
  {isSameUser && (
156
260
  <FieldsBehavior
@@ -171,8 +275,9 @@ const UserForm = (props: IProps) => {
171
275
  name="description"
172
276
  fieldType="TextField"
173
277
  placeholder="Type a description"
174
- value={description}
278
+ value={description || ""}
175
279
  onChange={handleDescriptionChange}
280
+ disabled={isEditDisabled}
176
281
  />
177
282
  <FieldsBehavior
178
283
  title="Time zone"
@@ -182,97 +287,208 @@ const UserForm = (props: IProps) => {
182
287
  options={timezones}
183
288
  onChange={handleTimezoneChange}
184
289
  mandatory={true}
290
+ disabled={isEditDisabled}
185
291
  />
186
- {isSiteView ? null : (
292
+ {isSiteView ? (
187
293
  <>
188
294
  <S.SettingsWrapper>
189
- <S.Heading>Sites assigned</S.Heading>
190
-
191
- {form.sites.length > 0 ? (
295
+ <S.HeadingWrapper>
296
+ <S.Heading>Roles Assigned</S.Heading>
297
+ {(!siteSelectedRoles || siteSelectedRoles.roles.length === 0) && (
298
+ <Button type="button" buttonStyle="text" onClick={toggleRoleModal} icon="addCircle">
299
+ Add Role
300
+ </Button>
301
+ )}
302
+ </S.HeadingWrapper>
303
+ <S.SettingContent>
304
+ <S.SettingText>You can add a one or more roles to this user.</S.SettingText>
305
+ </S.SettingContent>
306
+ </S.SettingsWrapper>
307
+ <S.RoleList>
308
+ {siteSelectedRoles &&
309
+ siteSelectedRoles.roles.map((siteRoleId: number) => {
310
+ const role = roles.find((role: IRole) => role.id === siteRoleId);
311
+ return role && <RoleItem key={role.name} role={role} isReadOnly={true} />;
312
+ })}
313
+ {siteSelectedRoles && siteSelectedRoles.roles.length > 0 && (
314
+ <Button type="button" buttonStyle="line" onClick={toggleRoleModal}>
315
+ Manage user roles
316
+ </Button>
317
+ )}
318
+ </S.RoleList>
319
+ <RolesModal
320
+ isOpen={isOpenRoleModal}
321
+ toggleModal={toggleRoleModal}
322
+ roles={roles}
323
+ addRoles={handleAddSiteRoles}
324
+ selectedRoles={siteSelectedRoles ? siteSelectedRoles.roles : []}
325
+ allSites={siteSelectedRoles && siteSelectedRoles.siteId === "all"}
326
+ isGlobal={false}
327
+ />
328
+ </>
329
+ ) : (
330
+ <>
331
+ {!readOnlySites && (
332
+ <S.OptionsWrapper>
333
+ <S.Label>User type</S.Label>
334
+ <OptionItem
335
+ name="superadmin"
336
+ title="Super admin"
337
+ description="The user will have full access to all features and their configuration. They will also have access to all sites."
338
+ onChange={handleChangeType}
339
+ value={isSuperAdmin}
340
+ />
341
+ <OptionItem
342
+ name="member"
343
+ title="Member"
344
+ description="You can assign a specific role to this user and give them access to all sites or just some of them."
345
+ onChange={handleChangeType}
346
+ value={!isSuperAdmin}
347
+ />
348
+ <S.HelpText>Show the diference between types & roles in our Documentation</S.HelpText>
349
+ </S.OptionsWrapper>
350
+ )}
351
+ {isSuperAdmin && readOnlySites && (
352
+ <S.SettingsWrapper>
353
+ <S.Heading>Sites & roles assigned</S.Heading>
192
354
  <S.SettingContent>
193
- <S.SettingText>
194
- {readOnlySites
195
- ? "You have access to this sites."
196
- : form.sites[0] !== "all"
197
- ? "You can give access to one or more sites to this user."
198
- : "You can give access to all sites at the same time."}
199
- </S.SettingText>
355
+ <OptionItem
356
+ name="superadmin"
357
+ title="Super admin"
358
+ description="The user will have full access to all features and their configuration. They will also have access to all sites."
359
+ onChange={toggleSuperAdmin}
360
+ value={isSuperAdmin}
361
+ readOnly={readOnlySites}
362
+ />
200
363
  </S.SettingContent>
201
- ) : null}
202
-
203
- {form.sites[0] !== "all" &&
204
- form.sites.map((id: number | string, index: number) => {
205
- if (id && id !== "all") {
206
- return (
207
- <SiteItem
208
- readOnly={readOnlySites}
209
- disabled={form.sites[0] === "all"}
210
- sites={form.sites}
211
- key={`${index}${getSiteName(id)}`}
212
- item={{ name: getSiteName(id), id, onChange: () => selectSite(id) }}
213
- />
214
- );
215
- } else {
216
- return null;
217
- }
218
- })}
219
- {form.sites[0] === "all" && (
220
- <S.SelectAllSitesFieldWrapper>
364
+ </S.SettingsWrapper>
365
+ )}
366
+ {!isSuperAdmin && (
367
+ <S.MemberWrapper>
368
+ <S.SettingsWrapper>
369
+ <S.Heading>Global data Permissions</S.Heading>
370
+ <S.SettingContent>
371
+ <S.SettingText>The user can have access to global data with specific role.</S.SettingText>
372
+ </S.SettingContent>
373
+ </S.SettingsWrapper>
374
+ <S.GlobalPermissionsFieldWrapper>
221
375
  <SiteItem
376
+ sites={sitesID}
377
+ item={{ name: "Global data permissions", id: "global", onChange: selectSite }}
378
+ roles={roles}
379
+ addRoles={handleAddRoles}
380
+ selected={rolesGlobal ? rolesGlobal.roles : []}
381
+ featured={true}
382
+ description={globalDescription}
222
383
  readOnly={readOnlySites}
223
- sites={form.sites}
224
- item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
225
384
  />
226
- </S.SelectAllSitesFieldWrapper>
227
- )}
228
- {!showMore && !readOnlySites ? <ShowMoreButton /> : null}
229
- </S.SettingsWrapper>
230
- {showMore ? (
231
- <>
232
- {form.sites[0] !== "all" && (
233
- <>
385
+ </S.GlobalPermissionsFieldWrapper>
386
+ <S.SettingsWrapper>
387
+ <S.Heading>Sites & roles assigned</S.Heading>
388
+ {form.roles.length > 0 ? (
234
389
  <S.SettingContent>
235
- <S.SettingText>You can give access to all sites at the same time.</S.SettingText>
390
+ <S.SettingText>
391
+ {readOnlySites
392
+ ? "You have access to this sites and this specific roles for each one."
393
+ : !rolesAll
394
+ ? "You can give access to one or more sites to this user and asigned specific roles."
395
+ : "You can give access to all sites at the same time."}
396
+ </S.SettingText>
236
397
  </S.SettingContent>
398
+ ) : null}
399
+
400
+ <S.SiteList>
401
+ {!rolesAll &&
402
+ form.roles.map((siteRole: ISiteRoles, index: number) => {
403
+ if (siteRole.siteId !== "all" && siteRole.siteId !== "global") {
404
+ return (
405
+ <SiteItem
406
+ readOnly={readOnlySites}
407
+ disabled={!!rolesAll}
408
+ sites={sitesID}
409
+ key={`${index}${getSiteName(siteRole.siteId)}`}
410
+ item={{ name: getSiteName(siteRole.siteId), id: siteRole.siteId, onChange: selectSite }}
411
+ selected={siteRole.roles}
412
+ roles={roles}
413
+ addRoles={handleAddRoles}
414
+ />
415
+ );
416
+ } else {
417
+ return null;
418
+ }
419
+ })}
420
+ </S.SiteList>
421
+ {!!rolesAll && (
237
422
  <S.SelectAllSitesFieldWrapper>
238
423
  <SiteItem
239
424
  readOnly={readOnlySites}
240
- sites={form.sites}
241
- item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
425
+ sites={sitesID}
426
+ item={{ name: "Access to All Sites", id: "all", onChange: selectSite }}
427
+ featured={true}
428
+ description={allDescription}
429
+ roles={roles}
430
+ selected={rolesAll ? rolesAll.roles : []}
431
+ addRoles={handleAddRoles}
242
432
  />
243
433
  </S.SelectAllSitesFieldWrapper>
244
- </>
245
- )}
246
- <S.SubTitle>Sites list</S.SubTitle>
247
- <S.SearchFieldWrapper>
248
- <SearchField
249
- disabled={form.sites[0] === "all"}
250
- placeholder="Search"
251
- onChange={filterSitesList}
252
- searchOnEnter={false}
253
- />
254
- </S.SearchFieldWrapper>
255
- {sitesList.length > 0 &&
256
- sitesList.map((site: ISite) => {
257
- return (
258
- !form.sites.includes(site.id) && (
434
+ )}
435
+ {!showMore && !readOnlySites ? <ShowMoreButton /> : null}
436
+ </S.SettingsWrapper>
437
+ {showMore ? (
438
+ <>
439
+ <S.SubTitle>Sites list</S.SubTitle>
440
+ <S.SearchFieldWrapper>
441
+ <SearchField
442
+ disabled={!!rolesAll}
443
+ placeholder="Search"
444
+ onChange={filterSitesList}
445
+ searchOnEnter={false}
446
+ />
447
+ </S.SearchFieldWrapper>
448
+ {!rolesAll && (
449
+ <S.SelectAllSitesFieldWrapper>
259
450
  <SiteItem
260
451
  readOnly={readOnlySites}
261
- disabled={form.sites[0] === "all"}
262
- sites={form.sites}
263
- key={site.id}
264
- item={{ name: site.name, id: site.id, onChange: () => selectSite(site.id) }}
452
+ sites={sitesID}
453
+ item={{ name: "Access to All Sites", id: "all", onChange: selectSite }}
454
+ featured={true}
455
+ description={allDescription}
456
+ roles={roles}
457
+ selected={[]}
458
+ addRoles={handleAddRoles}
265
459
  />
266
- )
267
- );
268
- })}
269
- {showMore && form.sites.length > 0 && !readOnlySites ? <ShowMoreButton /> : null}
270
- </>
271
- ) : null}
460
+ </S.SelectAllSitesFieldWrapper>
461
+ )}
462
+ <S.SiteList>
463
+ {sitesList.length > 0 &&
464
+ sitesList.map((site: ISite) => {
465
+ const rolesSite = form.roles.find((roleSite: any) => roleSite.siteId === site.id);
466
+ return (
467
+ !form.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site.id) && (
468
+ <SiteItem
469
+ readOnly={readOnlySites}
470
+ disabled={!!rolesAll}
471
+ sites={sitesID}
472
+ key={site.id}
473
+ item={{ name: site.name, id: site.id, onChange: selectSite }}
474
+ selected={rolesSite ? rolesSite.roles : []}
475
+ roles={roles}
476
+ addRoles={handleAddRoles}
477
+ />
478
+ )
479
+ );
480
+ })}
481
+ </S.SiteList>
482
+ {showMore && form.roles.length > 0 && !readOnlySites ? <ShowMoreButton /> : null}
483
+ </>
484
+ ) : null}
485
+ </S.MemberWrapper>
486
+ )}
272
487
  </>
273
488
  )}
274
489
  </S.Wrapper>
275
490
  <PasswordModal {...modalProps} />
491
+ <TypeChangeModal {...modalTypeChangeProps} />
276
492
  </>
277
493
  );
278
494
  };
@@ -282,10 +498,11 @@ const mapStateToProps = (state: IRootState) => ({
282
498
  isLoading: state.app.isLoading,
283
499
  currentUser: state.users.currentUser,
284
500
  sites: state.sites.sites,
501
+ roles: state.users.roles,
285
502
  });
286
503
 
287
504
  interface IDispatchProps {
288
- setHistoryPush(path: string): any;
505
+ setHistoryPush(path: string): void;
289
506
  }
290
507
 
291
508
  const mapDispatchToProps = {
@@ -294,14 +511,16 @@ const mapDispatchToProps = {
294
511
 
295
512
  interface IProfileProps {
296
513
  user: IUser;
297
- form: any;
514
+ form: IUser;
298
515
  isSaving: boolean;
299
516
  isLoading: boolean;
300
- setForm: (form: any) => void;
301
- currentUser: IUser;
517
+ setForm: (form: IUser) => void;
518
+ currentUser: IUser | null;
302
519
  sites: ISite[];
303
520
  readOnlySites?: boolean;
304
521
  isSiteView?: boolean;
522
+ roles: IRole[];
523
+ site: ISite | null;
305
524
  }
306
525
 
307
526
  type IProps = IProfileProps & IDispatchProps;