@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
@@ -1,86 +1,85 @@
1
1
  import React, { useState } from "react";
2
2
  import { connect } from "react-redux";
3
- import { ISite, IRootState, IUser } from "@ax/types";
3
+ import { ISite, IRootState, IUser, IRole, ISiteRoles } from "@ax/types";
4
4
  import { appActions } from "@ax/containers/App";
5
5
  import { usersActions } from "@ax/containers/Users";
6
- import { ErrorToast, FieldsBehavior, MainWrapper, SearchField, Notification } from "@ax/components";
6
+ import { ErrorToast, FieldsBehavior, MainWrapper, SearchField, Notification, Button, Modal } from "@ax/components";
7
+ import { useModal } from "@ax/hooks";
8
+
7
9
  import SiteItem from "./SiteItem";
10
+ import RolesModal from "./SiteItem/RolesModal";
11
+ import RoleItem from "./SiteItem/RolesModal/RoleItem";
12
+ import OptionItem from "./OptionItem";
8
13
 
9
14
  import * as S from "./style";
10
15
 
11
- const UserCreate = (props: IProps) => {
12
- const { setHistoryPush, createUser, sites, site, users, updateUser, isSaving } = props;
16
+ const UserCreate = (props: IUserCreateProps) => {
17
+ const { setHistoryPush, createUser, sites, site, users, updateUser, roles, isSaving } = props;
18
+
13
19
  const isSiteView = !!site;
14
- const initState: any = { name: "", email: "", sites: isSiteView ? [site.id] : ["all"] };
15
- const getSortedSitesByName = (sites: any) =>
16
- sites.sort((site1: any, site2: any) => site1.name.localeCompare(site2.name));
20
+
21
+ const initState: IState = { name: "", email: "", roles: [], isSuperAdmin: false };
22
+ const [state, setState] = useState(initState);
23
+
24
+ const getSortedSitesByName = (sites: ISite[]) =>
25
+ sites.sort((site1: ISite, site2: ISite) => site1.name.localeCompare(site2.name));
17
26
  const sortedByNameSites = getSortedSitesByName(sites);
18
27
 
19
28
  const [sitesList, setSiteList] = useState(sortedByNameSites);
20
- const [state, setState] = useState(initState);
21
29
  const [userAlreadyExists, setUserAlreadyExists] = useState(false);
22
- const BASE_URL = isSiteView ? "/sites/users" : "/users";
23
30
 
24
- const handleNameChange = (value: string) => {
25
- setState({ ...state, name: value });
26
- };
31
+ const { isOpen, toggleModal } = useModal(false);
32
+ const { isOpen: isOpenAdvise, toggleModal: toggleAdviseModal } = useModal(false);
27
33
 
28
- const handleEmailChange = (value: string) => {
29
- setState({ ...state, email: value });
30
- };
34
+ const handleNameChange = (value: string) => setState({ ...state, name: value });
35
+
36
+ const handleEmailChange = (value: string) => setState({ ...state, email: value });
31
37
 
32
- const handleCreate = () => {
38
+ const handleCreate = async () => {
33
39
  const { email } = state;
34
40
  const globalUser = users.find((user) => user.email === email);
35
41
  setUserAlreadyExists(false);
36
42
 
43
+ const BASE_URL = isSiteView ? "/sites/users" : "/users";
44
+
45
+ if(isOpenAdvise) {
46
+ toggleAdviseModal();
47
+ }
48
+
37
49
  if (isSiteView && globalUser?.id) {
38
- const { id, sites } = globalUser;
39
- const userAlreadyExists = sites[0] === "all" || sites.includes(site.id);
50
+ const { id, roles } = globalUser;
51
+ const userAlreadyExists = roles.find(
52
+ (siteRole: ISiteRoles) => siteRole.siteId === "all" || siteRole.siteId === site.id
53
+ );
40
54
  if (userAlreadyExists) {
41
55
  setUserAlreadyExists(true);
42
56
  } else {
43
- const form = { ...globalUser, sites: [...sites, site.id] };
44
- updateUser(id, form, false).then(() => {
45
- setHistoryPush(BASE_URL);
46
- });
47
- }
48
- } else {
49
- createUser(state).then((created: boolean) => {
50
- if (created) {
57
+ const form = { ...globalUser, roles: [...roles, ...state.roles] };
58
+ const updated = await updateUser(id, form, false);
59
+ if (updated) {
51
60
  setHistoryPush(BASE_URL);
52
61
  }
53
- });
54
- }
55
- };
56
-
57
- const rightButtonProps = {
58
- label: "Add User",
59
- action: handleCreate,
60
- disabled: isSaving,
61
- };
62
-
63
- const name = state.name.trim() === "" ? "Name" : state.name;
64
-
65
- const selectAllSites = () => {
66
- const { sites } = state;
67
- if (sites.includes("all")) {
68
- const filteredSites = sites.filter((site: number | string) => site !== "all");
69
- const sortedSites = getSortedSitesByName(filteredSites);
70
- setState({ ...state, sites: sortedSites });
62
+ }
71
63
  } else {
72
- setState({ ...state, sites: ["all"] });
64
+ const created = await createUser(state);
65
+ if (created) {
66
+ setHistoryPush(BASE_URL);
67
+ }
73
68
  }
74
69
  };
75
70
 
76
- const selectSite = (id: number) => {
77
- const { sites } = state;
78
- if (sites.includes(id)) {
79
- const filteredSites = sites.filter((site: number | string) => site !== id && site !== "all");
80
- setState({ ...state, sites: filteredSites });
71
+ const selectSite = (id: number | string) => {
72
+ const { roles } = state;
73
+ const hasSite = roles.find((siteRole: ISiteRoles) => siteRole.siteId === id);
74
+ if (hasSite) {
75
+ const filteredRoles = roles.filter(
76
+ (siteRole: ISiteRoles) =>
77
+ (Number.isInteger(id) && siteRole.siteId !== id && siteRole.siteId !== "all") ||
78
+ (!Number.isInteger(id) && siteRole.siteId !== id)
79
+ );
80
+ setState({ ...state, roles: filteredRoles });
81
81
  } else {
82
- const filteredSites: string[] | number[] = [...sites].filter((site) => site !== "all");
83
- setState({ ...state, sites: [...filteredSites, id] });
82
+ setState({ ...state, roles: [...state.roles, { siteId: id, roles: [] }] });
84
83
  }
85
84
  };
86
85
 
@@ -92,92 +91,250 @@ const UserCreate = (props: IProps) => {
92
91
  setSiteList(sortedSites);
93
92
  };
94
93
 
94
+ const handleAddRoles = (newRoles: ISiteRoles) => {
95
+ const oldRoles = state.roles.filter((role: ISiteRoles) => role.siteId !== newRoles.siteId);
96
+ const updatedRoles = newRoles.roles.length > 0 ? [...oldRoles, newRoles] : oldRoles;
97
+ setState({ ...state, roles: updatedRoles });
98
+ };
99
+
100
+ const handleAddSiteRoles = (newRoles: number[]) => {
101
+ if (!site) return;
102
+ const oldRoles = state.roles.filter((role: ISiteRoles) => role.siteId !== site.id);
103
+ const newSiteRoles = { siteId: site.id, roles: newRoles };
104
+ const updatedRoles = newRoles.length > 0 ? [...oldRoles, newSiteRoles] : oldRoles;
105
+ setState({ ...state, roles: updatedRoles });
106
+ };
107
+
108
+ const toggleSuperAdmin = () => setState({ ...state, isSuperAdmin: !state.isSuperAdmin });
109
+
110
+ const rightButtonProps = {
111
+ label: "Add User",
112
+ action: state.roles.length === 0 && !state.isSuperAdmin ? toggleAdviseModal : handleCreate,
113
+ disabled: isSaving,
114
+ };
115
+
116
+ const name = state.name.trim() === "" ? "Name" : state.name;
117
+
118
+ const sitesID = state.roles.map((roleSite: ISiteRoles) => roleSite.siteId);
119
+
120
+ const siteSelectedRoles = isSiteView
121
+ ? state.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site.id)
122
+ : undefined;
123
+
124
+ const rolesAll = state.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "all");
125
+ const allDescription =
126
+ "The user will have access to all existing and future sites depending on the permissions assigned to the role.";
127
+
128
+ const rolesGlobal = state.roles.find((roleSite: ISiteRoles) => roleSite.siteId === "global");
129
+ const globalDescription = "Global data will be accessible to the user based on the permissions assigned to the role.";
130
+
131
+ const mainModalAction = {
132
+ title: "Create User",
133
+ onClick: handleCreate,
134
+ };
135
+
136
+ const secondaryModalAction = { title: "Cancel", onClick: toggleAdviseModal };
137
+
95
138
  return (
96
- <>
97
- <MainWrapper backLink={true} title="New User" rightButton={rightButtonProps}>
98
- <ErrorToast size="l" />
99
- {userAlreadyExists && (
100
- <S.NotificationWrapper>
101
- <Notification type="error" text="User already has access to this site." />
102
- </S.NotificationWrapper>
103
- )}
104
- <S.Wrapper>
105
- <S.SubTitle>User data</S.SubTitle>
106
- <S.NameTitle>{name}</S.NameTitle>
107
- <FieldsBehavior
108
- title="Name"
109
- name="name"
110
- fieldType="TextField"
111
- placeholder="Type a name"
112
- mandatory={true}
113
- value={state.name}
114
- onChange={handleNameChange}
115
- />
116
- <FieldsBehavior
117
- title="Email"
118
- name="email"
119
- fieldType="TextField"
120
- placeholder="Type an email"
121
- mandatory={true}
122
- value={state.email}
123
- onChange={handleEmailChange}
124
- />
125
- {isSiteView ? null : (
126
- <>
127
- <S.SettingsWrapper>
128
- <S.Heading>Sites assigned</S.Heading>
129
- <S.SettingContent>
130
- <S.SettingText>You can give access to one or more sites to this user.</S.SettingText>
131
- </S.SettingContent>
132
- </S.SettingsWrapper>
133
- <S.SelectAllSitesFieldWrapper>
134
- <SiteItem
135
- sites={state.sites}
136
- item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
137
- />
138
- </S.SelectAllSitesFieldWrapper>
139
- <S.SubTitle>Sites list</S.SubTitle>
140
- <S.SearchFieldWrapper>
141
- <SearchField
142
- disabled={state.sites[0] === "all"}
143
- placeholder="Search"
144
- onChange={filterSitesList}
145
- searchOnEnter={false}
146
- />
147
- </S.SearchFieldWrapper>
148
- {sitesList.length > 0 &&
149
- sitesList.map((site: any) => (
139
+ <MainWrapper backLink={true} title="New User" rightButton={rightButtonProps}>
140
+ <ErrorToast size="l" />
141
+ {userAlreadyExists && (
142
+ <S.NotificationWrapper data-testid="user-notification-wrapper">
143
+ <Notification type="error" text="User already has access to this site." />
144
+ </S.NotificationWrapper>
145
+ )}
146
+ <S.Wrapper>
147
+ <S.SubTitle>User data</S.SubTitle>
148
+ <S.NameTitle>{name}</S.NameTitle>
149
+ <FieldsBehavior
150
+ title="Name"
151
+ name="name"
152
+ fieldType="TextField"
153
+ placeholder="Type a name"
154
+ mandatory={true}
155
+ value={state.name}
156
+ onChange={handleNameChange}
157
+ autoComplete="user-name"
158
+ autoFocus
159
+ />
160
+ <FieldsBehavior
161
+ title="Email"
162
+ name="email"
163
+ fieldType="TextField"
164
+ placeholder="Type an email"
165
+ mandatory={true}
166
+ value={state.email}
167
+ onChange={handleEmailChange}
168
+ autoComplete="user-email"
169
+ />
170
+ {isSiteView ? (
171
+ <>
172
+ <S.SettingsWrapper>
173
+ <S.HeadingWrapper>
174
+ <S.Heading>Roles Assigned</S.Heading>
175
+ {(!siteSelectedRoles || siteSelectedRoles.roles.length === 0) && (
176
+ <Button type="button" buttonStyle="text" onClick={toggleModal} icon="addCircle">
177
+ Add Role
178
+ </Button>
179
+ )}
180
+ </S.HeadingWrapper>
181
+ <S.SettingContent>
182
+ <S.SettingText>You can add a one or more roles to this user.</S.SettingText>
183
+ </S.SettingContent>
184
+ </S.SettingsWrapper>
185
+ <S.RoleList data-testid="user-role-list">
186
+ {siteSelectedRoles &&
187
+ siteSelectedRoles.roles.map((siteRoleId: number) => {
188
+ const role = roles.find((role: IRole) => role.id === siteRoleId);
189
+ return role && <RoleItem key={role.name} role={role} isReadOnly={true} />;
190
+ })}
191
+ {siteSelectedRoles && siteSelectedRoles.roles.length > 0 && (
192
+ <Button type="button" buttonStyle="line" onClick={toggleModal}>
193
+ Manage user roles
194
+ </Button>
195
+ )}
196
+ </S.RoleList>
197
+ <RolesModal
198
+ isOpen={isOpen}
199
+ toggleModal={toggleModal}
200
+ roles={roles}
201
+ addRoles={handleAddSiteRoles}
202
+ selectedRoles={siteSelectedRoles ? siteSelectedRoles.roles : []}
203
+ isGlobal={false}
204
+ />
205
+ </>
206
+ ) : (
207
+ <>
208
+ <S.OptionsWrapper>
209
+ <S.Label>User type</S.Label>
210
+ <OptionItem
211
+ name="superadmin"
212
+ title="Super admin"
213
+ description="The user will have full access to all features and their configuration. They will also have access to all sites."
214
+ onChange={toggleSuperAdmin}
215
+ value={state.isSuperAdmin}
216
+ />
217
+ <OptionItem
218
+ name="member"
219
+ title="Member"
220
+ description="You can assign a specific role to this user and give them access to all sites or just some of them."
221
+ onChange={toggleSuperAdmin}
222
+ value={!state.isSuperAdmin}
223
+ />
224
+ <S.HelpText>Show the diference between types & roles in our Documentation</S.HelpText>
225
+ </S.OptionsWrapper>
226
+ {!state.isSuperAdmin && (
227
+ <S.MemberWrapper>
228
+ <S.SettingsWrapper>
229
+ <S.Heading>Global data Permissions</S.Heading>
230
+ <S.SettingContent>
231
+ <S.SettingText>The user can have access to global data with specific role.</S.SettingText>
232
+ </S.SettingContent>
233
+ </S.SettingsWrapper>
234
+ <S.GlobalPermissionsFieldWrapper>
150
235
  <SiteItem
151
- disabled={state.sites[0] === "all"}
152
- sites={state.sites}
153
- key={site.id}
154
- item={{ name: site.name, id: site.id, onChange: () => selectSite(site.id) }}
236
+ sites={sitesID}
237
+ item={{ name: "Global data permissions", id: "global", onChange: selectSite }}
238
+ roles={roles}
239
+ addRoles={handleAddRoles}
240
+ selected={rolesGlobal ? rolesGlobal.roles : []}
241
+ featured={true}
242
+ description={globalDescription}
155
243
  />
156
- ))}
157
- </>
158
- )}
159
- </S.Wrapper>
160
- </MainWrapper>
161
- </>
244
+ </S.GlobalPermissionsFieldWrapper>
245
+ <S.SettingsWrapper>
246
+ <S.Heading>Sites access</S.Heading>
247
+ <S.SettingContent>
248
+ <S.SettingText>
249
+ The user can have access to all sites with specific roles. You can also manually select one or
250
+ more sites.
251
+ </S.SettingText>
252
+ </S.SettingContent>
253
+ </S.SettingsWrapper>
254
+ <S.SearchFieldWrapper>
255
+ <SearchField
256
+ disabled={!!rolesAll}
257
+ placeholder="Search"
258
+ onChange={filterSitesList}
259
+ searchOnEnter={false}
260
+ autoFocus={false}
261
+ />
262
+ </S.SearchFieldWrapper>
263
+ <S.SelectAllSitesFieldWrapper>
264
+ <SiteItem
265
+ sites={sitesID}
266
+ item={{ name: "Access to All Sites", id: "all", onChange: selectSite }}
267
+ roles={roles}
268
+ addRoles={handleAddRoles}
269
+ selected={rolesAll ? rolesAll.roles : []}
270
+ featured={true}
271
+ description={allDescription}
272
+ />
273
+ </S.SelectAllSitesFieldWrapper>
274
+ <S.SiteList data-testid="user-site-list">
275
+ {sitesList.length > 0 &&
276
+ sitesList.map((site: ISite) => {
277
+ const rolesSite = state.roles.find((roleSite: ISiteRoles) => roleSite.siteId === site.id);
278
+ return (
279
+ <SiteItem
280
+ disabled={!!rolesAll}
281
+ sites={sitesID}
282
+ key={site.id}
283
+ item={{ name: site.name, id: site.id, onChange: selectSite }}
284
+ roles={roles}
285
+ addRoles={handleAddRoles}
286
+ selected={rolesSite ? rolesSite.roles : []}
287
+ />
288
+ );
289
+ })}
290
+ </S.SiteList>
291
+ </S.MemberWrapper>
292
+ )}
293
+ </>
294
+ )}
295
+ </S.Wrapper>
296
+ <Modal
297
+ isOpen={isOpenAdvise}
298
+ hide={toggleAdviseModal}
299
+ title="Create User?"
300
+ secondaryAction={secondaryModalAction}
301
+ mainAction={mainModalAction}
302
+ size="S"
303
+ >
304
+ <S.ModalContent>
305
+ Before creating an account, <strong>select the user's permissions and site access</strong>. If you proceed without selecting
306
+ any permissions, the user <strong>won't have access to anything or view any content</strong>.
307
+ </S.ModalContent>
308
+ </Modal>
309
+ </MainWrapper>
162
310
  );
163
311
  };
164
312
 
165
313
  interface IDispatchProps {
166
- setHistoryPush(path: string): any;
167
- createUser(data: { name: string; email: string; sites: any[] }): Promise<boolean>;
168
- updateUser(id: number, data: any, isProfile: boolean): any;
314
+ setHistoryPush(path: string): void;
315
+ createUser(data: { name: string; email: string; roles: ISiteRoles[] }): Promise<boolean>;
316
+ updateUser(id: number, data: IUser, isProfile: boolean): Promise<boolean>;
169
317
  }
170
318
  interface IProfileProps {
171
319
  users: IUser[];
172
320
  sites: ISite[];
173
- site: ISite;
321
+ site: ISite | null;
322
+ roles: IRole[];
174
323
  isSaving: boolean;
175
324
  }
176
325
 
326
+ interface IState {
327
+ name: string;
328
+ email: string;
329
+ roles: ISiteRoles[];
330
+ isSuperAdmin: boolean;
331
+ }
332
+
177
333
  const mapStateToProps = (state: IRootState) => ({
178
334
  users: state.users.users,
179
335
  sites: state.sites.sites,
180
336
  site: state.sites.currentSiteInfo,
337
+ roles: state.users.roles,
181
338
  isSaving: state.app.isSaving,
182
339
  });
183
340
 
@@ -187,6 +344,6 @@ const mapDispatchToProps = {
187
344
  updateUser: usersActions.updateUser,
188
345
  };
189
346
 
190
- type IProps = IProfileProps & IDispatchProps;
347
+ export type IUserCreateProps = IProfileProps & IDispatchProps;
191
348
 
192
349
  export default connect(mapStateToProps, mapDispatchToProps)(UserCreate);
@@ -3,6 +3,7 @@ import styled from "styled-components";
3
3
  const Wrapper = styled.section`
4
4
  width: 720px;
5
5
  margin: ${(p) => `${p.theme.spacing.m} 0 0 ${p.theme.spacing.m}`};
6
+ padding-bottom: ${(p) => p.theme.spacing.m};
6
7
  `;
7
8
 
8
9
  const NameTitle = styled.div`
@@ -17,6 +18,14 @@ const SubTitle = styled.div`
17
18
  margin-bottom: ${(p) => p.theme.spacing.xs};
18
19
  `;
19
20
 
21
+ const HeadingWrapper = styled.div`
22
+ display: flex;
23
+ align-items: center;
24
+ & button {
25
+ margin-left: auto;
26
+ }
27
+ `;
28
+
20
29
  const Heading = styled.div`
21
30
  ${(p) => p.theme.textStyle.headingXS};
22
31
  color: ${(p) => p.theme.color.textHighEmphasis};
@@ -30,7 +39,7 @@ const SettingsWrapper = styled.div`
30
39
  `;
31
40
 
32
41
  const SettingContent = styled.div`
33
- padding-bottom: ${(p) => p.theme.spacing.m};
42
+ padding-bottom: ${(p) => p.theme.spacing.s};
34
43
  `;
35
44
 
36
45
  const SearchFieldWrapper = styled.div`
@@ -39,14 +48,15 @@ const SearchFieldWrapper = styled.div`
39
48
  max-width: calc(${(p) => p.theme.spacing.l} * 7);
40
49
  `;
41
50
 
42
- const SelectAllSitesFieldWrapper = styled.div`
43
- padding-bottom: ${(p) => p.theme.spacing.s};
51
+ const SelectAllSitesFieldWrapper = styled.div``;
52
+
53
+ const GlobalPermissionsFieldWrapper = styled.div`
54
+ padding-bottom: ${(p) => p.theme.spacing.m};
44
55
  `;
45
56
 
46
57
  const SettingText = styled.div`
47
58
  ${(p) => p.theme.textStyle.uiM};
48
59
  color: ${(p) => p.theme.color.textMediumEmphasis};
49
- width: calc(${(p) => p.theme.spacing.l} * 12);
50
60
  `;
51
61
 
52
62
  const FormWrapper = styled.div`
@@ -63,6 +73,54 @@ const NotificationWrapper = styled.div`
63
73
  z-index: 2;
64
74
  `;
65
75
 
76
+ const SiteList = styled.div`
77
+ & > div {
78
+ margin-bottom: 0;
79
+ border-radius: 0;
80
+ border-bottom: none;
81
+
82
+ &:first-child {
83
+ border-radius: ${(p) => `${p.theme.radii.s} ${p.theme.radii.s} 0 0`};
84
+ }
85
+ &:last-child {
86
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
87
+ border-radius: ${(p) => `0 0 ${p.theme.radii.s} ${p.theme.radii.s}`};
88
+ }
89
+ }
90
+ `;
91
+
92
+ const RoleList = styled.div`
93
+ border-bottom: 1px solid ${(p) => p.theme.color.uiLine};
94
+ padding-top: ${(p) => p.theme.spacing.xs};
95
+
96
+ & button {
97
+ margin-top: ${(p) => p.theme.spacing.s};
98
+ margin-bottom: ${(p) => p.theme.spacing.s};
99
+ }
100
+ `;
101
+
102
+ const MemberWrapper = styled.div``;
103
+
104
+ const OptionsWrapper = styled.div`
105
+ padding-bottom: ${(p) => p.theme.spacing.m};
106
+ `;
107
+
108
+ const Label = styled.div`
109
+ ${(p) => p.theme.textStyle.fieldLabel};
110
+ color: ${(p) => p.theme.color.textMediumEmphasis};
111
+ display: block;
112
+ margin-bottom: ${(p) => p.theme.spacing?.xs};
113
+ `;
114
+
115
+ const HelpText = styled.div`
116
+ ${(p) => p.theme.textStyle.uiXS};
117
+ color: ${(p) => p.theme.color?.textMediumEmphasis};
118
+ `;
119
+
120
+ const ModalContent = styled.div`
121
+ padding: ${(p) => p.theme.spacing.m};
122
+ `;
123
+
66
124
  export {
67
125
  Wrapper,
68
126
  NameTitle,
@@ -75,4 +133,13 @@ export {
75
133
  SelectAllSitesFieldWrapper,
76
134
  FormWrapper,
77
135
  NotificationWrapper,
136
+ SiteList,
137
+ GlobalPermissionsFieldWrapper,
138
+ RoleList,
139
+ HeadingWrapper,
140
+ MemberWrapper,
141
+ OptionsWrapper,
142
+ Label,
143
+ HelpText,
144
+ ModalContent,
78
145
  };