@griddo/ax 1.63.5 → 1.64.2
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.
- package/config/griddo-config/cx-polyfills/builder.ssr.js +6 -0
- package/config/griddo-config/cx-polyfills/componentsBundle.js +4 -0
- package/config/{griddo-config.js → griddo-config/index.js} +36 -15
- package/config/griddo-config/ssrHelpers.js +47 -0
- package/config/jest/componentsMock.js +29 -0
- package/config/jest/fileMock.js +1 -0
- package/config/jest/setup.js +5 -0
- package/config/jest/styleMock.js +1 -0
- package/config/jest/test-utils.js +17 -0
- package/config/paths.js +36 -5
- package/config/webpack.config.js +1 -1
- package/config/webpackDevServer.config.js +4 -1
- package/config/webpackSchemas.config.js +4 -1
- package/package.json +33 -59
- package/scripts/build.js +9 -2
- package/src/__mocks__/reducers/analyticsState.tsx +14 -0
- package/src/__mocks__/reducers/pageEditor.tsx +30 -0
- package/src/api/sites.tsx +28 -6
- package/src/api/structuredData.tsx +1 -1
- package/src/api/users.tsx +5 -4
- package/src/components/ActionMenu/style.tsx +2 -0
- package/src/components/Browser/index.tsx +9 -5
- package/src/{modules/Content/PageItem/atoms.tsx → components/CategoryCell/index.tsx} +4 -6
- package/src/components/CategoryCell/style.tsx +11 -0
- package/src/components/Fields/AnalyticsField/PageAnalytics/index.tsx +19 -19
- package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/atoms.tsx +26 -16
- package/src/components/Fields/AnalyticsField/StructuredDataAnalytics/index.tsx +8 -13
- package/src/components/Fields/AnalyticsField/index.test.tsx +100 -0
- package/src/components/Fields/AnalyticsField/index.tsx +9 -2
- package/src/components/Fields/AnalyticsField/utils.tsx +2 -2
- package/src/components/Fields/ArrayFieldGroup/ArrayFieldItem/style.tsx +2 -1
- package/src/components/Fields/CheckField/index.test.tsx +95 -0
- package/src/components/Fields/CheckField/index.tsx +9 -3
- package/src/components/Fields/CheckField/style.tsx +32 -24
- package/src/components/Fields/CheckGroup/index.test.tsx +274 -0
- package/src/components/Fields/CheckGroup/index.tsx +2 -1
- package/src/components/Fields/FileField/FileDragAndDrop/style.tsx +3 -2
- package/src/components/Fields/FileField/style.tsx +2 -1
- package/src/components/Fields/MultiCheckSelect/style.tsx +18 -18
- package/src/components/Fields/NoteField/style.tsx +9 -9
- package/src/components/Fields/ReferenceField/AutoPanel/AutoItem/index.tsx +1 -1
- package/src/components/Fields/Select/style.tsx +41 -37
- package/src/components/Fields/TagField/index.test.tsx +136 -0
- package/src/components/Fields/TagField/index.tsx +8 -12
- package/src/components/Fields/TextArea/index.test.tsx +69 -0
- package/src/components/Fields/TextArea/index.tsx +4 -13
- package/src/components/Fields/TextArea/style.tsx +2 -2
- package/src/components/Fields/TextField/index.test.tsx +144 -0
- package/src/components/Fields/TextField/index.tsx +23 -19
- package/src/components/Fields/TextField/style.tsx +16 -7
- package/src/components/Fields/UniqueCheck/index.test.tsx +43 -0
- package/src/components/Fields/UrlField/utils.tsx +8 -6
- package/src/components/FieldsBehavior/index.tsx +0 -2
- package/src/components/FieldsBehavior/style.tsx +21 -21
- package/src/components/Gallery/GalleryFilters/Orientation/style.tsx +2 -1
- package/src/components/Gallery/GalleryFilters/SortBy/style.tsx +2 -1
- package/src/components/Icon/index.tsx +12 -10
- package/src/components/IconAction/index.tsx +7 -1
- package/src/components/IconAction/style.tsx +10 -10
- package/src/components/SearchField/index.tsx +11 -8
- package/src/components/SearchField/style.tsx +21 -12
- package/src/components/TableFilters/CategoryFilter/index.tsx +1 -1
- package/src/components/TableFilters/CategoryFilter/style.tsx +2 -1
- package/src/components/TableFilters/DateFilter/style.tsx +2 -1
- package/src/components/TableFilters/LiveFilter/index.tsx +2 -2
- package/src/components/TableFilters/LiveFilter/style.tsx +2 -1
- package/src/components/TableFilters/NameFilter/style.tsx +2 -1
- package/src/components/TableFilters/SiteFilter/index.tsx +38 -24
- package/src/components/TableFilters/SiteFilter/style.tsx +2 -1
- package/src/components/TableFilters/StatusFilter/style.tsx +2 -1
- package/src/components/TableFilters/TranslationsFilter/style.tsx +2 -1
- package/src/components/TableFilters/TypeFilter/style.tsx +2 -1
- package/src/components/Tag/index.tsx +9 -7
- package/src/components/Tag/style.tsx +20 -8
- package/src/components/index.tsx +4 -2
- package/src/containers/App/reducer.tsx +0 -2
- package/src/containers/PageEditor/actions.tsx +2 -2
- package/src/containers/Sites/actions.tsx +30 -19
- package/src/containers/Users/actions.tsx +10 -2
- package/src/containers/Users/reducer.tsx +3 -1
- package/src/helpers/fields.tsx +2 -4
- package/src/helpers/index.tsx +3 -0
- package/src/helpers/themes.tsx +9 -0
- package/src/modules/Analytics/GroupPanel/utils.tsx +3 -3
- package/src/modules/App/Routing/NavMenu/index.tsx +13 -12
- package/src/modules/Content/PageItem/index.tsx +31 -9
- package/src/modules/Content/PageItem/style.tsx +0 -7
- package/src/modules/Content/atoms.tsx +78 -0
- package/src/modules/Content/index.tsx +104 -33
- package/src/modules/Content/style.tsx +10 -7
- package/src/modules/GlobalEditor/PageBrowser/index.tsx +0 -4
- package/src/modules/GlobalEditor/index.tsx +3 -3
- package/src/modules/Navigation/Defaults/DefaultsEditor/Editor/DefaultsBrowser/index.tsx +0 -4
- package/src/modules/PageEditor/PageBrowser/index.tsx +0 -4
- package/src/modules/PageEditor/atoms.tsx +74 -0
- package/src/modules/PageEditor/index.tsx +30 -9
- package/src/modules/PageEditor/style.tsx +4 -0
- package/src/modules/PublicPreview/index.tsx +3 -5
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/TemplateBrowser/index.tsx +0 -4
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/TemplateConfig/TemplateEditor/Editor/index.tsx +2 -3
- package/src/modules/Settings/ContentTypes/DataPacks/Config/Form/index.tsx +1 -1
- package/src/modules/Settings/Globals/index.tsx +3 -3
- package/src/modules/StructuredData/Form/index.tsx +2 -4
- package/src/modules/StructuredData/StructuredDataList/BulkHeader/TableHeader/index.tsx +22 -18
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/atoms.tsx +3 -24
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/index.tsx +2 -2
- package/src/modules/StructuredData/StructuredDataList/GlobalPageItem/style.tsx +0 -7
- package/src/modules/StructuredData/StructuredDataList/OptionTable/index.tsx +2 -4
- package/src/modules/StructuredData/StructuredDataList/StructuredDataItem/index.tsx +46 -14
- package/src/modules/StructuredData/StructuredDataList/hooks.tsx +21 -9
- package/src/modules/StructuredData/StructuredDataList/index.tsx +48 -20
- package/src/modules/Users/Profile/index.tsx +12 -7
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +44 -0
- package/src/modules/Users/UserCreate/SiteItem/style.tsx +30 -0
- package/src/modules/Users/UserCreate/index.tsx +120 -10
- package/src/modules/Users/UserCreate/style.tsx +54 -1
- package/src/modules/Users/UserEdit/index.tsx +53 -15
- package/src/modules/Users/UserForm/index.tsx +152 -5
- package/src/modules/Users/UserForm/style.tsx +40 -2
- package/src/modules/Users/UserList/BulkHeader/TableHeader/index.tsx +40 -2
- package/src/modules/Users/UserList/BulkHeader/TableHeader/style.tsx +0 -1
- package/src/modules/Users/UserList/BulkHeader/index.tsx +10 -1
- package/src/modules/Users/UserList/UserItem/index.tsx +70 -15
- package/src/modules/Users/UserList/hooks.tsx +58 -1
- package/src/modules/Users/UserList/index.tsx +80 -34
- package/src/modules/Users/index.tsx +18 -11
- package/src/routes/site.tsx +8 -0
- package/src/types/index.tsx +7 -0
- package/tsconfig.json +2 -0
- package/scripts/test.js +0 -45
|
@@ -1,25 +1,34 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
1
|
+
import React, { useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
4
|
import { appActions } from "@ax/containers/App";
|
|
5
5
|
import { usersActions } from "@ax/containers/Users";
|
|
6
6
|
import { Loading, MainWrapper, Modal } from "@ax/components";
|
|
7
|
-
import { IRootState, IUser } from "@ax/types";
|
|
7
|
+
import { IRootState, ISite, IUser } from "@ax/types";
|
|
8
8
|
import { useModal } 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 } = props;
|
|
14
|
+
const { user, updateUser, isSaving, isLoading, deleteUser, currentUser, setHistoryPush, site, sites } = props;
|
|
15
15
|
|
|
16
16
|
const { timezone } = user;
|
|
17
|
-
const initForm = { ...user };
|
|
17
|
+
const initForm = { ...user, sites: user.sites || [] };
|
|
18
18
|
if (!timezone) initForm.timezone = "Europe/Madrid";
|
|
19
|
-
|
|
19
|
+
const isSiteView = !!site;
|
|
20
|
+
const sitesIds = sites.map((site: ISite) => site.id);
|
|
21
|
+
const usersRoute = isSiteView ? "/sites/users" : "users";
|
|
20
22
|
const [form, setForm] = useState<IUser>(initForm);
|
|
21
23
|
const { isOpen, toggleModal } = useModal();
|
|
22
24
|
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
const { timezone } = user;
|
|
27
|
+
const initForm = { ...user };
|
|
28
|
+
if (!timezone) initForm.timezone = "Europe/Madrid";
|
|
29
|
+
setForm(initForm);
|
|
30
|
+
}, [user]);
|
|
31
|
+
|
|
23
32
|
const handleSave = () => {
|
|
24
33
|
form.id && updateUser(form.id, form, false);
|
|
25
34
|
};
|
|
@@ -35,7 +44,7 @@ const UserEdit = (props: IProps) => {
|
|
|
35
44
|
deleteUser(user.id).then((deleted: boolean) => {
|
|
36
45
|
if (deleted) {
|
|
37
46
|
setForm({ ...form, id: null });
|
|
38
|
-
setHistoryPush(
|
|
47
|
+
setHistoryPush(usersRoute);
|
|
39
48
|
}
|
|
40
49
|
});
|
|
41
50
|
};
|
|
@@ -44,12 +53,30 @@ const UserEdit = (props: IProps) => {
|
|
|
44
53
|
|
|
45
54
|
const rightLineButtonProps = !isSameUser
|
|
46
55
|
? {
|
|
47
|
-
label: "Delete User",
|
|
56
|
+
label: isSiteView ? "Remove user from this site" : "Delete User",
|
|
48
57
|
action: toggleModal,
|
|
49
58
|
}
|
|
50
59
|
: undefined;
|
|
51
60
|
|
|
52
|
-
const
|
|
61
|
+
const getUpdatedSites = (currentUserSiteIds: any) =>
|
|
62
|
+
currentUserSiteIds[0] === "all"
|
|
63
|
+
? sitesIds.filter((id: number) => id !== site.id)
|
|
64
|
+
: currentUserSiteIds.filter((id: number) => id !== site.id);
|
|
65
|
+
|
|
66
|
+
const removeUserFromSite = () => {
|
|
67
|
+
const updatedSites = getUpdatedSites(form.sites);
|
|
68
|
+
const formWithUpdatedSites = { ...form, sites: updatedSites };
|
|
69
|
+
|
|
70
|
+
user.id &&
|
|
71
|
+
updateUser(user.id, formWithUpdatedSites, true).then(() => {
|
|
72
|
+
setHistoryPush(usersRoute);
|
|
73
|
+
});
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
const mainDeleteAction = {
|
|
77
|
+
title: isSiteView ? "Remove user" : "Delete User",
|
|
78
|
+
onClick: isSiteView ? removeUserFromSite : handleDelete,
|
|
79
|
+
};
|
|
53
80
|
const secondaryDeleteAction = { title: "Cancel", onClick: toggleModal };
|
|
54
81
|
|
|
55
82
|
if (isLoading) return <Loading />;
|
|
@@ -59,22 +86,29 @@ const UserEdit = (props: IProps) => {
|
|
|
59
86
|
title="Edit User"
|
|
60
87
|
rightButton={rightButtonProps}
|
|
61
88
|
backLink={true}
|
|
62
|
-
rightLineButton={rightLineButtonProps}
|
|
89
|
+
rightLineButton={isSiteView ? rightLineButtonProps : undefined}
|
|
63
90
|
>
|
|
64
|
-
<UserForm form={form} setForm={setForm} user={user} />
|
|
91
|
+
<UserForm form={form} setForm={setForm} user={user} isSiteView={isSiteView} />
|
|
65
92
|
<Modal
|
|
66
93
|
isOpen={isOpen}
|
|
67
94
|
hide={toggleModal}
|
|
68
|
-
title="Delete User?"
|
|
95
|
+
title={isSiteView ? "Remove user from this site?" : "Delete User?"}
|
|
69
96
|
secondaryAction={secondaryDeleteAction}
|
|
70
97
|
mainAction={mainDeleteAction}
|
|
71
98
|
>
|
|
72
99
|
{isOpen ? (
|
|
73
100
|
<S.ModalContent>
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
101
|
+
{isSiteView ? (
|
|
102
|
+
<p>
|
|
103
|
+
Are you sure you want to remove <strong>{user.email}</strong> from this site? If you remove it, this
|
|
104
|
+
user will no longer have access to this site but it will still be able to log in.
|
|
105
|
+
</p>
|
|
106
|
+
) : (
|
|
107
|
+
<p>
|
|
108
|
+
Are you sure you want to delete <strong>{user.email}</strong>? If you delete it, this user will no
|
|
109
|
+
longer be able to log in.
|
|
110
|
+
</p>
|
|
111
|
+
)}
|
|
78
112
|
<p>
|
|
79
113
|
This action <strong>cannot be undone</strong>.
|
|
80
114
|
</p>
|
|
@@ -87,6 +121,8 @@ const UserEdit = (props: IProps) => {
|
|
|
87
121
|
|
|
88
122
|
const mapStateToProps = (state: IRootState) => ({
|
|
89
123
|
user: state.users.userForm,
|
|
124
|
+
site: state.sites.currentSiteInfo,
|
|
125
|
+
sites: state.sites.sites,
|
|
90
126
|
isSaving: state.app.isSaving,
|
|
91
127
|
isLoading: state.app.isLoading,
|
|
92
128
|
currentUser: state.users.currentUser,
|
|
@@ -106,9 +142,11 @@ const mapDispatchToProps = {
|
|
|
106
142
|
|
|
107
143
|
interface IProfileProps {
|
|
108
144
|
user: IUser;
|
|
145
|
+
site: ISite;
|
|
109
146
|
isSaving: boolean;
|
|
110
147
|
isLoading: boolean;
|
|
111
148
|
currentUser: IUser;
|
|
149
|
+
sites: ISite[];
|
|
112
150
|
}
|
|
113
151
|
|
|
114
152
|
type IProps = IProfileProps & IDispatchProps;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
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 { ErrorToast, FieldsBehavior } from "@ax/components";
|
|
6
|
-
import { IImage, IRootState, IUser } from "@ax/types";
|
|
5
|
+
import { Button, ErrorToast, FieldsBehavior, SearchField } from "@ax/components";
|
|
6
|
+
import { IImage, IRootState, IUser, ISite } from "@ax/types";
|
|
7
7
|
import { useModal } from "@ax/hooks";
|
|
8
8
|
import { RouteLeavingGuard } from "@ax/guards";
|
|
9
9
|
|
|
@@ -11,14 +11,27 @@ import { timezones } from "../../Settings/Globals/constants";
|
|
|
11
11
|
import { PasswordModal } from "./atoms";
|
|
12
12
|
import { shouldBeSaved } from "./helpers";
|
|
13
13
|
|
|
14
|
+
import SiteItem from "../UserCreate/SiteItem";
|
|
15
|
+
|
|
14
16
|
import * as S from "./style";
|
|
15
17
|
|
|
16
18
|
const UserForm = (props: IProps) => {
|
|
17
|
-
const { user, form, setForm, setHistoryPush, currentUser } = props;
|
|
18
|
-
|
|
19
|
+
const { user, form, setForm, setHistoryPush, currentUser, sites, readOnlySites = false, isSiteView = false } = props;
|
|
19
20
|
const { id, username, name, email, image, company, description, timezone } = form;
|
|
20
21
|
|
|
22
|
+
const getSortedItemsByName = (items: any) => items.sort((item1: any, item2: any) => item1.name.localeCompare(item2.name));
|
|
23
|
+
|
|
24
|
+
const sortedByNameSites = getSortedItemsByName(sites);
|
|
25
|
+
|
|
21
26
|
const { isOpen, toggleModal } = useModal(false);
|
|
27
|
+
const [sitesList, setSiteList] = useState(sortedByNameSites);
|
|
28
|
+
const [showMore, setShowMore] = useState(false);
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (!form.sites[0]) {
|
|
32
|
+
setShowMore(true);
|
|
33
|
+
}
|
|
34
|
+
}, [form.sites]);
|
|
22
35
|
|
|
23
36
|
const handleImageChange = (value: IImage) => setForm({ ...form, image: value || null });
|
|
24
37
|
const handleNameChange = (value: string) => setForm({ ...form, name: value });
|
|
@@ -35,16 +48,58 @@ const UserForm = (props: IProps) => {
|
|
|
35
48
|
};
|
|
36
49
|
|
|
37
50
|
const action = (path: string) => setHistoryPush(path);
|
|
51
|
+
|
|
52
|
+
const filterSitesList = (query: string) => {
|
|
53
|
+
const filteredSites = sites.filter((site: any) =>
|
|
54
|
+
site.name.toLowerCase().replace(" ", "").includes(query.toLowerCase().replace(" ", ""))
|
|
55
|
+
);
|
|
56
|
+
const sortedSites = getSortedItemsByName(filteredSites);
|
|
57
|
+
setSiteList(sortedSites);
|
|
58
|
+
};
|
|
59
|
+
|
|
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
|
+
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 });
|
|
75
|
+
} else {
|
|
76
|
+
const filteredSites = [...currentSites].filter((site) => site && site !== "all");
|
|
77
|
+
setForm({ ...form, sites: [...filteredSites, id] });
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
38
81
|
const text = (
|
|
39
82
|
<>
|
|
40
83
|
Some changes <strong>are not saved</strong>.
|
|
41
84
|
</>
|
|
42
85
|
);
|
|
86
|
+
|
|
43
87
|
const allowedRoutes = ["/profile"];
|
|
44
88
|
const { isDirty } = shouldBeSaved(user, form);
|
|
45
89
|
|
|
46
90
|
const isSameUser = currentUser.id === user.id;
|
|
47
91
|
|
|
92
|
+
const ShowMoreButton = () => (
|
|
93
|
+
<Button type="button" buttonStyle="line" onClick={() => setShowMore(!showMore)}>
|
|
94
|
+
{showMore ? "Show less sites" : "Show more sites"}
|
|
95
|
+
</Button>
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
const getSiteName = (id: number | string) => {
|
|
99
|
+
const currentSite = sites.find((site) => site.id === id) || { name: "" };
|
|
100
|
+
return currentSite.name;
|
|
101
|
+
};
|
|
102
|
+
|
|
48
103
|
return (
|
|
49
104
|
<>
|
|
50
105
|
<RouteLeavingGuard when={isDirty} action={action} text={text} allowedRoutes={allowedRoutes} />
|
|
@@ -128,6 +183,94 @@ const UserForm = (props: IProps) => {
|
|
|
128
183
|
onChange={handleTimezoneChange}
|
|
129
184
|
mandatory={true}
|
|
130
185
|
/>
|
|
186
|
+
{isSiteView ? null : (
|
|
187
|
+
<>
|
|
188
|
+
<S.SettingsWrapper>
|
|
189
|
+
<S.Heading>Sites assigned</S.Heading>
|
|
190
|
+
|
|
191
|
+
{form.sites.length > 0 ? (
|
|
192
|
+
<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>
|
|
200
|
+
</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>
|
|
221
|
+
<SiteItem
|
|
222
|
+
readOnly={readOnlySites}
|
|
223
|
+
sites={form.sites}
|
|
224
|
+
item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
|
|
225
|
+
/>
|
|
226
|
+
</S.SelectAllSitesFieldWrapper>
|
|
227
|
+
)}
|
|
228
|
+
{!showMore && !readOnlySites ? <ShowMoreButton /> : null}
|
|
229
|
+
</S.SettingsWrapper>
|
|
230
|
+
{showMore ? (
|
|
231
|
+
<>
|
|
232
|
+
{form.sites[0] !== "all" && (
|
|
233
|
+
<>
|
|
234
|
+
<S.SettingContent>
|
|
235
|
+
<S.SettingText>You can give access to all sites at the same time.</S.SettingText>
|
|
236
|
+
</S.SettingContent>
|
|
237
|
+
<S.SelectAllSitesFieldWrapper>
|
|
238
|
+
<SiteItem
|
|
239
|
+
readOnly={readOnlySites}
|
|
240
|
+
sites={form.sites}
|
|
241
|
+
item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
|
|
242
|
+
/>
|
|
243
|
+
</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) && (
|
|
259
|
+
<SiteItem
|
|
260
|
+
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) }}
|
|
265
|
+
/>
|
|
266
|
+
)
|
|
267
|
+
);
|
|
268
|
+
})}
|
|
269
|
+
{showMore && form.sites.length > 0 && !readOnlySites ? <ShowMoreButton /> : null}
|
|
270
|
+
</>
|
|
271
|
+
) : null}
|
|
272
|
+
</>
|
|
273
|
+
)}
|
|
131
274
|
</S.Wrapper>
|
|
132
275
|
<PasswordModal {...modalProps} />
|
|
133
276
|
</>
|
|
@@ -138,6 +281,7 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
138
281
|
isSaving: state.app.isSaving,
|
|
139
282
|
isLoading: state.app.isLoading,
|
|
140
283
|
currentUser: state.users.currentUser,
|
|
284
|
+
sites: state.sites.sites,
|
|
141
285
|
});
|
|
142
286
|
|
|
143
287
|
interface IDispatchProps {
|
|
@@ -155,6 +299,9 @@ interface IProfileProps {
|
|
|
155
299
|
isLoading: boolean;
|
|
156
300
|
setForm: (form: any) => void;
|
|
157
301
|
currentUser: IUser;
|
|
302
|
+
sites: ISite[];
|
|
303
|
+
readOnlySites?: boolean;
|
|
304
|
+
isSiteView?: boolean;
|
|
158
305
|
}
|
|
159
306
|
|
|
160
307
|
type IProps = IProfileProps & IDispatchProps;
|
|
@@ -22,9 +22,47 @@ const ModalContent = styled.div`
|
|
|
22
22
|
padding: ${p => p.theme.spacing.m};
|
|
23
23
|
`;
|
|
24
24
|
|
|
25
|
+
const SettingsWrapper = styled.div`
|
|
26
|
+
position: relative;
|
|
27
|
+
border-top: 1px solid ${(p) => p.theme.color.uiLine};
|
|
28
|
+
padding-top: ${(p) => p.theme.spacing.m};
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const Heading = styled.div`
|
|
32
|
+
${(p) => p.theme.textStyle.headingXS};
|
|
33
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
34
|
+
padding-bottom: ${(p) => p.theme.spacing.xs};
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
const SettingContent = styled.div`
|
|
38
|
+
padding-bottom: ${(p) => p.theme.spacing.m};
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
const SettingText = styled.div`
|
|
42
|
+
${(p) => p.theme.textStyle.uiM};
|
|
43
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
44
|
+
width: calc(${(p) => p.theme.spacing.l} * 12);
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const SearchFieldWrapper = styled.div`
|
|
48
|
+
padding-top: ${(p) => p.theme.spacing.xs};
|
|
49
|
+
padding-bottom: ${(p) => p.theme.spacing.xs};
|
|
50
|
+
max-width: calc(${(p) => p.theme.spacing.l} * 7);
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const SelectAllSitesFieldWrapper = styled.div`
|
|
54
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
55
|
+
`;
|
|
56
|
+
|
|
25
57
|
export {
|
|
26
58
|
Wrapper,
|
|
27
59
|
NameTitle,
|
|
28
60
|
SubTitle,
|
|
29
|
-
ModalContent
|
|
30
|
-
|
|
61
|
+
ModalContent,
|
|
62
|
+
SettingsWrapper,
|
|
63
|
+
SettingText,
|
|
64
|
+
SettingContent,
|
|
65
|
+
Heading,
|
|
66
|
+
SearchFieldWrapper,
|
|
67
|
+
SelectAllSitesFieldWrapper
|
|
68
|
+
};
|
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
|
|
3
|
-
import { CheckField, TableCounter } from "@ax/components";
|
|
3
|
+
import { CheckField, TableCounter, SiteFilter } from "@ax/components";
|
|
4
|
+
|
|
4
5
|
import Name from "../../HeaderMenus/Name";
|
|
6
|
+
|
|
5
7
|
import * as S from "./style";
|
|
6
8
|
|
|
7
9
|
const TableHeader = (props: IProps) => {
|
|
8
|
-
const {
|
|
10
|
+
const {
|
|
11
|
+
totalItems,
|
|
12
|
+
selectAllItems,
|
|
13
|
+
isScrolling,
|
|
14
|
+
sortItems,
|
|
15
|
+
filterItems,
|
|
16
|
+
sortedListStatus,
|
|
17
|
+
filterValues,
|
|
18
|
+
showSiteFilter = true,
|
|
19
|
+
} = props;
|
|
9
20
|
|
|
10
21
|
return (
|
|
11
22
|
<S.TableHeader isScrolling={isScrolling}>
|
|
@@ -23,6 +34,30 @@ const TableHeader = (props: IProps) => {
|
|
|
23
34
|
<S.NameWrapper>
|
|
24
35
|
<Name sortItems={sortItems} sortedState={sortedListStatus} />
|
|
25
36
|
</S.NameWrapper>
|
|
37
|
+
{showSiteFilter ? (
|
|
38
|
+
<S.NameWrapper>
|
|
39
|
+
<SiteFilter
|
|
40
|
+
filterItems={filterItems}
|
|
41
|
+
value={filterValues}
|
|
42
|
+
pointer="filterSites"
|
|
43
|
+
center={false}
|
|
44
|
+
label="Sites"
|
|
45
|
+
selectAllOption="noFilter"
|
|
46
|
+
filters={[
|
|
47
|
+
{
|
|
48
|
+
name: "noFilter",
|
|
49
|
+
value: "noFilter",
|
|
50
|
+
title: "All",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
name: "all",
|
|
54
|
+
value: "all",
|
|
55
|
+
title: "Access to all sites",
|
|
56
|
+
},
|
|
57
|
+
]}
|
|
58
|
+
/>
|
|
59
|
+
</S.NameWrapper>
|
|
60
|
+
) : null}
|
|
26
61
|
<S.ActionsHeader>
|
|
27
62
|
<TableCounter totalItems={totalItems} />
|
|
28
63
|
</S.ActionsHeader>
|
|
@@ -33,9 +68,12 @@ const TableHeader = (props: IProps) => {
|
|
|
33
68
|
interface IProps {
|
|
34
69
|
totalItems: number;
|
|
35
70
|
isScrolling: boolean;
|
|
71
|
+
showSiteFilter: boolean;
|
|
36
72
|
selectAllItems: () => void;
|
|
73
|
+
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
37
74
|
sortItems: any;
|
|
38
75
|
sortedListStatus: any;
|
|
76
|
+
filterValues: any;
|
|
39
77
|
}
|
|
40
78
|
|
|
41
79
|
export default TableHeader;
|
|
@@ -12,13 +12,16 @@ const BulkHeader = (props: IProps): JSX.Element => {
|
|
|
12
12
|
totalItems,
|
|
13
13
|
isScrolling,
|
|
14
14
|
sortItems,
|
|
15
|
+
filterItems,
|
|
15
16
|
sortedListStatus,
|
|
17
|
+
filterValues,
|
|
18
|
+
isSiteView = false,
|
|
16
19
|
} = props;
|
|
17
20
|
|
|
18
21
|
const bulkActions = [
|
|
19
22
|
{
|
|
20
23
|
icon: "delete",
|
|
21
|
-
text: "delete",
|
|
24
|
+
text: isSiteView ? "Remove from this site" : "delete",
|
|
22
25
|
action: bulkDelete,
|
|
23
26
|
},
|
|
24
27
|
];
|
|
@@ -32,17 +35,21 @@ const BulkHeader = (props: IProps): JSX.Element => {
|
|
|
32
35
|
/>
|
|
33
36
|
) : (
|
|
34
37
|
<TableHeader
|
|
38
|
+
filterValues={filterValues}
|
|
35
39
|
totalItems={totalItems}
|
|
36
40
|
selectAllItems={selectAllItems}
|
|
41
|
+
filterItems={filterItems}
|
|
37
42
|
isScrolling={isScrolling}
|
|
38
43
|
sortItems={sortItems}
|
|
39
44
|
sortedListStatus={sortedListStatus}
|
|
45
|
+
showSiteFilter={!isSiteView}
|
|
40
46
|
/>
|
|
41
47
|
);
|
|
42
48
|
};
|
|
43
49
|
|
|
44
50
|
interface IProps {
|
|
45
51
|
showBulk: boolean;
|
|
52
|
+
isSiteView?: boolean;
|
|
46
53
|
checkState: any;
|
|
47
54
|
bulkDelete: () => void;
|
|
48
55
|
selectItems: () => void;
|
|
@@ -51,6 +58,8 @@ interface IProps {
|
|
|
51
58
|
isScrolling: boolean;
|
|
52
59
|
sortItems: (orderPointer: string, isAscending: boolean) => void;
|
|
53
60
|
sortedListStatus: any;
|
|
61
|
+
filterValues: any;
|
|
62
|
+
filterItems: (filterPointer: string, filtersSelected: string) => void;
|
|
54
63
|
}
|
|
55
64
|
|
|
56
65
|
export default BulkHeader;
|
|
@@ -1,24 +1,36 @@
|
|
|
1
1
|
import React from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { ICheck, IUser } from "@ax/types";
|
|
4
|
+
import { ICheck, IUser, ISite } from "@ax/types";
|
|
5
5
|
import { useModal, useToast } from "@ax/hooks";
|
|
6
6
|
import { getDaysAgo } from "@ax/helpers";
|
|
7
7
|
import { usersActions } from "@ax/containers/Users";
|
|
8
|
-
import { CheckField, Avatar, Modal, Tag, Tooltip, Toast } from "@ax/components";
|
|
8
|
+
import { CheckField, Avatar, Modal, Tag, Tooltip, Toast, ElementsTooltip } from "@ax/components";
|
|
9
9
|
|
|
10
10
|
import * as S from "./style";
|
|
11
11
|
|
|
12
12
|
const UserItem = (props: IProps): JSX.Element => {
|
|
13
|
-
const {
|
|
13
|
+
const {
|
|
14
|
+
user,
|
|
15
|
+
sites,
|
|
16
|
+
siteId,
|
|
17
|
+
isSelected,
|
|
18
|
+
onChange,
|
|
19
|
+
handleClick,
|
|
20
|
+
deleteUser,
|
|
21
|
+
updateUser,
|
|
22
|
+
resendInvitation,
|
|
23
|
+
isSiteView = false,
|
|
24
|
+
} = props;
|
|
14
25
|
|
|
15
26
|
const { isOpen, toggleModal } = useModal();
|
|
16
27
|
const { isVisible, toggleToast, setIsVisible } = useToast();
|
|
17
28
|
|
|
18
29
|
const handleOnChange = (value: ICheck) => onChange(value);
|
|
30
|
+
const sitesIds = sites.map((site: ISite) => site.id);
|
|
19
31
|
|
|
20
32
|
const deleteOption = {
|
|
21
|
-
label: "delete",
|
|
33
|
+
label: isSiteView ? "remove from site" : "delete",
|
|
22
34
|
icon: "delete",
|
|
23
35
|
action: () => toggleModal(),
|
|
24
36
|
};
|
|
@@ -48,7 +60,20 @@ const UserItem = (props: IProps): JSX.Element => {
|
|
|
48
60
|
|
|
49
61
|
const menuOptions = user.status === "invited" ? [resendOption, deleteOption] : [deleteOption];
|
|
50
62
|
|
|
51
|
-
const
|
|
63
|
+
const getUpdatedSites = (currentUserSiteIds: any) =>
|
|
64
|
+
currentUserSiteIds[0] === "all"
|
|
65
|
+
? sitesIds.filter((id: number) => id !== siteId)
|
|
66
|
+
: currentUserSiteIds.filter((id: number) => id !== siteId);
|
|
67
|
+
|
|
68
|
+
const removeUserFromSite = () => {
|
|
69
|
+
const updatedSites = getUpdatedSites(user.sites);
|
|
70
|
+
const updatedUser = { ...user, sites: updatedSites };
|
|
71
|
+
user.id && updateUser(user.id, updatedUser, false, true).then(() => toggleModal());
|
|
72
|
+
};
|
|
73
|
+
const mainDeleteAction = {
|
|
74
|
+
title: isSiteView ? "Remove user" : "Delete user",
|
|
75
|
+
onClick: isSiteView ? removeUserFromSite : handleDeleteUser,
|
|
76
|
+
};
|
|
52
77
|
const secondaryDeleteAction = { title: "Cancel", onClick: toggleModal };
|
|
53
78
|
|
|
54
79
|
const tagColor = user.status === "invited" ? "#FFBB37" : "#AFC628";
|
|
@@ -61,6 +86,17 @@ const UserItem = (props: IProps): JSX.Element => {
|
|
|
61
86
|
message: "Invitation sended",
|
|
62
87
|
};
|
|
63
88
|
|
|
89
|
+
const getSiteNameById = (id: number | string) => {
|
|
90
|
+
if (id === "all") {
|
|
91
|
+
return "ALL SITES";
|
|
92
|
+
} else {
|
|
93
|
+
const currentSite = sites.find((site: ISite) => site.id === id);
|
|
94
|
+
return currentSite ? currentSite?.name : "";
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const getElementsNames = () => user?.sites.map((id: string | number) => getSiteNameById(id));
|
|
99
|
+
|
|
64
100
|
return (
|
|
65
101
|
<>
|
|
66
102
|
<S.UserRow role="rowgroup" selected={isSelected}>
|
|
@@ -77,11 +113,18 @@ const UserItem = (props: IProps): JSX.Element => {
|
|
|
77
113
|
<S.UserUsername>@{user.username}</S.UserUsername>
|
|
78
114
|
</S.UserInfo>
|
|
79
115
|
</S.UserCell>
|
|
80
|
-
|
|
81
|
-
<
|
|
82
|
-
<
|
|
83
|
-
</
|
|
84
|
-
|
|
116
|
+
{isSiteView ? null : (
|
|
117
|
+
<S.UserCell role="cell" onClick={handleClick}>
|
|
118
|
+
{user?.sites?.length > 0 && <ElementsTooltip elements={getElementsNames()} elementsPerRow={3} defaultElements={3} />}
|
|
119
|
+
</S.UserCell>
|
|
120
|
+
)}
|
|
121
|
+
{isSiteView ? null : (
|
|
122
|
+
<S.StatusCell>
|
|
123
|
+
<Tooltip content={tooltipText} hideOnClick>
|
|
124
|
+
<Tag type="status" text={user.status ?? ""} color={tagColor} />
|
|
125
|
+
</Tooltip>
|
|
126
|
+
</S.StatusCell>
|
|
127
|
+
)}
|
|
85
128
|
<S.ActionsCell role="cell">
|
|
86
129
|
<S.StyledActionMenu icon="more" options={menuOptions} tooltip="Actions" />
|
|
87
130
|
</S.ActionsCell>
|
|
@@ -89,16 +132,23 @@ const UserItem = (props: IProps): JSX.Element => {
|
|
|
89
132
|
<Modal
|
|
90
133
|
isOpen={isOpen}
|
|
91
134
|
hide={toggleModal}
|
|
92
|
-
title="Delete User?"
|
|
135
|
+
title={isSiteView ? "Remove user from this site?" : "Delete User?"}
|
|
93
136
|
secondaryAction={secondaryDeleteAction}
|
|
94
137
|
mainAction={mainDeleteAction}
|
|
95
138
|
>
|
|
96
139
|
{isOpen ? (
|
|
97
140
|
<S.ModalContent>
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
141
|
+
{isSiteView ? (
|
|
142
|
+
<p>
|
|
143
|
+
Are you sure you want to remove <strong>{user.email}</strong> from this site? If you remove it, this
|
|
144
|
+
user will no longer have access to this site but it will still be able to log in.
|
|
145
|
+
</p>
|
|
146
|
+
) : (
|
|
147
|
+
<p>
|
|
148
|
+
Are you sure you want to delete <strong>{user.email}</strong>? If you delete it, this user will no
|
|
149
|
+
longer be able to log in.
|
|
150
|
+
</p>
|
|
151
|
+
)}
|
|
102
152
|
<p>
|
|
103
153
|
This action <strong>cannot be undone</strong>.
|
|
104
154
|
</p>
|
|
@@ -112,17 +162,22 @@ const UserItem = (props: IProps): JSX.Element => {
|
|
|
112
162
|
|
|
113
163
|
interface IUserItemProps {
|
|
114
164
|
user: IUser;
|
|
165
|
+
siteId?: number;
|
|
166
|
+
sites: ISite[];
|
|
115
167
|
isSelected: boolean;
|
|
168
|
+
isSiteView?: boolean;
|
|
116
169
|
onChange: (value: ICheck) => void;
|
|
117
170
|
handleClick: (id: number) => void;
|
|
118
171
|
}
|
|
119
172
|
|
|
120
173
|
const mapDispatchToProps = {
|
|
121
174
|
deleteUser: usersActions.deleteUser,
|
|
175
|
+
updateUser: usersActions.updateUser,
|
|
122
176
|
resendInvitation: usersActions.resendInvitation,
|
|
123
177
|
};
|
|
124
178
|
|
|
125
179
|
interface IDispatchProps {
|
|
180
|
+
updateUser(id: number, data: any, isProfile: boolean, isList?: boolean): any;
|
|
126
181
|
deleteUser(id: number): Promise<boolean>;
|
|
127
182
|
resendInvitation(id: number): Promise<boolean>;
|
|
128
183
|
}
|