@griddo/ax 1.63.3 → 1.64.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.
- 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 +35 -64
- 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/index.tsx +3 -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/patches/connected-react-router+6.9.2.patch +0 -12
- package/scripts/test.js +0 -45
|
@@ -90,6 +90,8 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
90
90
|
const lastPage = Math.ceil(totalItems / itemsPerPage);
|
|
91
91
|
const isLastItem = page === lastPage && currentSitePages.length === 1;
|
|
92
92
|
|
|
93
|
+
const [structuredDataType, setStructuredDataType] = useState("all");
|
|
94
|
+
|
|
93
95
|
const [isScrolling, setIsScrolling] = useState(false);
|
|
94
96
|
const [deletedItem, setDeletedItem] = useState<number | number[] | null>(null);
|
|
95
97
|
const { isVisible: isDataToast, toggleToast: toggleDataToast, setIsVisible: setIsDataToast } = useToast();
|
|
@@ -102,7 +104,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
102
104
|
const { isOpen: isNewOpen, toggleModal: toggleNewModal } = useModal();
|
|
103
105
|
const { isOpen: isDeleteOpen, toggleModal: toggleDeleteModal } = useModal();
|
|
104
106
|
const { sortedListStatus, setSortedListStatus } = useSortedListStatus();
|
|
105
|
-
const { setFiltersSelection, setFilterQuery, filterValues, resetFilterQuery } = useFilterQuery();
|
|
107
|
+
const { setFiltersSelection, setFilterQuery, filterValues, resetFilterQuery } = useFilterQuery(currentStructuredData);
|
|
106
108
|
const [currentFilterQuery, setCurrentFilterQuery] = useState("");
|
|
107
109
|
const history = useHistory();
|
|
108
110
|
const [isFirstRender, setIsFirstRender] = useState(true);
|
|
@@ -130,20 +132,24 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
130
132
|
live: { title: "Live", show: true },
|
|
131
133
|
status: { title: "Status", show: true },
|
|
132
134
|
translation: { title: "Trans.", show: true },
|
|
133
|
-
seo: { title: "SEO", show: true },
|
|
135
|
+
...(isStructuredDataFromPage && { seo: { title: "SEO", show: true } }),
|
|
134
136
|
};
|
|
135
137
|
|
|
136
138
|
const extraColumns = categoryColumns.reduce((acc: Record<string, IColumn>, cur: any) => {
|
|
137
|
-
acc[cur.key] = { title: cur.title, show:
|
|
139
|
+
acc[cur.key] = { title: cur.title, show: !isStructuredDataFromPage };
|
|
138
140
|
return acc;
|
|
139
141
|
}, {});
|
|
140
142
|
|
|
141
143
|
const allColumns = { type: { title: "Types", show: true }, ...extraColumns, ...defaultColumns };
|
|
142
|
-
const filterColumns = {
|
|
144
|
+
const filterColumns = {
|
|
145
|
+
...(isStructuredDataFromPage && { site: { title: "Site", show: true } }),
|
|
146
|
+
...extraColumns,
|
|
147
|
+
...defaultColumns,
|
|
148
|
+
};
|
|
143
149
|
|
|
144
150
|
const initialColumns = isAllPages ? allColumns : filterColumns;
|
|
145
151
|
|
|
146
|
-
const [columnsState, setColumnsState] = useState(
|
|
152
|
+
const [columnsState, setColumnsState] = useState<Record<string, any>>({ all: allColumns });
|
|
147
153
|
|
|
148
154
|
const {
|
|
149
155
|
resetBulkSelection,
|
|
@@ -186,8 +192,10 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
186
192
|
getGlobalPages(params, currentFilterQuery);
|
|
187
193
|
};
|
|
188
194
|
|
|
195
|
+
const handleGetStructuredData = () => getStructuredData(filter, currentFilterQuery);
|
|
196
|
+
|
|
189
197
|
const handleGetData = () => {
|
|
190
|
-
isStructuredDataFromPage ? handleGetGlobalPages() :
|
|
198
|
+
isStructuredDataFromPage ? handleGetGlobalPages() : handleGetStructuredData();
|
|
191
199
|
};
|
|
192
200
|
|
|
193
201
|
const resetFilter = () => {
|
|
@@ -196,37 +204,52 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
196
204
|
}
|
|
197
205
|
};
|
|
198
206
|
|
|
207
|
+
const changeColumnsState = (updatedColumns: any) => {
|
|
208
|
+
setColumnsState((state) => ({ ...state, [structuredDataType]: updatedColumns }));
|
|
209
|
+
};
|
|
210
|
+
|
|
199
211
|
useLayoutEffect(() => {
|
|
200
212
|
resetFilter();
|
|
201
|
-
handleGetData();
|
|
202
213
|
setIsFirstRender(false);
|
|
203
214
|
resetPageEditor();
|
|
204
215
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
205
216
|
}, []);
|
|
206
217
|
|
|
218
|
+
useEffect(() => {
|
|
219
|
+
const isGlobalData = structuredData.global.some((data) => data.id === currentStructuredData?.id);
|
|
220
|
+
const type = currentStructuredData && isGlobalData ? currentStructuredData.id : "all";
|
|
221
|
+
setStructuredDataType(type);
|
|
222
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
223
|
+
}, [currentStructuredData]);
|
|
224
|
+
|
|
207
225
|
useEffect(() => {
|
|
208
226
|
filterItems("types", "all");
|
|
209
227
|
if (filter === "all-pages" || !isStructuredDataFromPage) {
|
|
210
228
|
filterItems("filterSites", "all");
|
|
211
229
|
}
|
|
212
230
|
unselectAllItems();
|
|
213
|
-
if (filter
|
|
214
|
-
setColumnsState(
|
|
215
|
-
|
|
216
|
-
|
|
231
|
+
if (filter !== "all-pages" && currentStructuredData) {
|
|
232
|
+
setColumnsState((state) => ({
|
|
233
|
+
[currentStructuredData.id]: {
|
|
234
|
+
site: { title: "Site", show: true },
|
|
235
|
+
...extraColumns,
|
|
236
|
+
...initialColumns,
|
|
237
|
+
},
|
|
238
|
+
...state,
|
|
239
|
+
}));
|
|
217
240
|
}
|
|
218
241
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
219
242
|
}, [filter]);
|
|
220
243
|
|
|
221
244
|
useEffect(() => {
|
|
222
|
-
if (!isFirstRender
|
|
245
|
+
if (!isFirstRender) {
|
|
223
246
|
handleGetData();
|
|
224
247
|
}
|
|
225
248
|
if (tableRef.current) {
|
|
226
249
|
tableRef.current.scrollTo(0, 0);
|
|
227
250
|
}
|
|
228
251
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
229
|
-
}, [
|
|
252
|
+
}, [lang.locale, page, searchQuery, filterValues]);
|
|
230
253
|
|
|
231
254
|
useEffect(() => {
|
|
232
255
|
if (wrapperRef.current) {
|
|
@@ -241,7 +264,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
241
264
|
};
|
|
242
265
|
|
|
243
266
|
useEffect(() => {
|
|
244
|
-
if (!isLoading) {
|
|
267
|
+
if (!isFirstRender && !isLoading) {
|
|
245
268
|
const emptyState: IEmptyStateProps = {};
|
|
246
269
|
const isSearching = searchQuery.length > 0;
|
|
247
270
|
if (isSearching) {
|
|
@@ -277,8 +300,6 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
277
300
|
};
|
|
278
301
|
|
|
279
302
|
const handleMenuClick = (dataID: string) => {
|
|
280
|
-
resetFilterQuery();
|
|
281
|
-
setCurrentFilterQuery("");
|
|
282
303
|
setPage(firstPage);
|
|
283
304
|
setSelectedStructuredData(dataID, scope);
|
|
284
305
|
};
|
|
@@ -383,11 +404,14 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
383
404
|
|
|
384
405
|
const filterItems = async (filterPointer: string, filtersSelected: string) => {
|
|
385
406
|
setPage(firstPage);
|
|
407
|
+
if (!isStructuredDataFromPage && filterPointer === "categories") filterPointer = "related";
|
|
386
408
|
const filtersSelection = setFiltersSelection(filterPointer, filtersSelected);
|
|
387
409
|
const filterQuery = setFilterQuery(filtersSelection);
|
|
388
410
|
setCurrentFilterQuery(filterQuery);
|
|
389
411
|
};
|
|
390
412
|
|
|
413
|
+
const currentDataColumnsState = currentStructuredData ? columnsState[structuredDataType] || [] : columnsState["all"];
|
|
414
|
+
|
|
391
415
|
const TableHeader = (
|
|
392
416
|
<BulkHeader
|
|
393
417
|
showBulk={areItemsSelected(dataIds)}
|
|
@@ -404,11 +428,11 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
404
428
|
sortItems={sortItems}
|
|
405
429
|
sortedListStatus={sortedListStatus}
|
|
406
430
|
filterItems={filterItems}
|
|
407
|
-
filterValues={filterValues}
|
|
431
|
+
filterValues={filterValues[structuredDataType]}
|
|
408
432
|
isAllPages={isAllPages}
|
|
409
433
|
categoryColumns={categoryColumns}
|
|
410
|
-
columns={
|
|
411
|
-
setColumns={
|
|
434
|
+
columns={currentDataColumnsState}
|
|
435
|
+
setColumns={changeColumnsState}
|
|
412
436
|
/>
|
|
413
437
|
);
|
|
414
438
|
|
|
@@ -470,6 +494,10 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
470
494
|
setDeletedItem={setDeletedItem}
|
|
471
495
|
isEditable={isDataEditable}
|
|
472
496
|
activatedDataPacks={activatedDataPacks}
|
|
497
|
+
categoryColumns={categoryColumns}
|
|
498
|
+
columns={columnsState}
|
|
499
|
+
categoryColors={categoryColors}
|
|
500
|
+
addCategoryColors={addCategoryColors}
|
|
473
501
|
/>
|
|
474
502
|
);
|
|
475
503
|
});
|
|
@@ -498,7 +526,7 @@ const StructuredDataList = (props: IProps): JSX.Element => {
|
|
|
498
526
|
toggleToast={togglePageToast}
|
|
499
527
|
setDeletedItem={setDeletedItem}
|
|
500
528
|
categoryColumns={categoryColumns}
|
|
501
|
-
columns={
|
|
529
|
+
columns={currentDataColumnsState}
|
|
502
530
|
categoryColors={categoryColors}
|
|
503
531
|
addCategoryColors={addCategoryColors}
|
|
504
532
|
/>
|
|
@@ -3,21 +3,22 @@ import { connect } from "react-redux";
|
|
|
3
3
|
|
|
4
4
|
import { appActions } from "@ax/containers/App";
|
|
5
5
|
import { usersActions } from "@ax/containers/Users";
|
|
6
|
+
import { sitesActions } from "@ax/containers/Sites";
|
|
6
7
|
import { Loading, MainWrapper } from "@ax/components";
|
|
7
8
|
import { IRootState, IUser } from "@ax/types";
|
|
8
9
|
import { useURLSearchParam } from "@ax/hooks";
|
|
10
|
+
|
|
9
11
|
import UserForm from "../UserForm";
|
|
10
12
|
|
|
11
13
|
const Profile = (props: IProps) => {
|
|
12
|
-
const { user, getUser, updateUser, isSaving, isLoading } = props;
|
|
14
|
+
const { user, getUser, updateUser, isSaving, isLoading, getSites, token } = props;
|
|
13
15
|
|
|
14
16
|
const isUserInit = useURLSearchParam("init");
|
|
15
17
|
|
|
16
|
-
const [form, setForm] = useState<
|
|
17
|
-
const { id } = form;
|
|
18
|
+
const [form, setForm] = useState<IUser>({ ...user });
|
|
18
19
|
|
|
19
20
|
useEffect(() => {
|
|
20
|
-
|
|
21
|
+
isUserInit ? getSites(token) : getUser("me");
|
|
21
22
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
22
23
|
}, []);
|
|
23
24
|
|
|
@@ -29,7 +30,7 @@ const Profile = (props: IProps) => {
|
|
|
29
30
|
}, [user]);
|
|
30
31
|
|
|
31
32
|
const handleSave = () => {
|
|
32
|
-
updateUser(id, form, true);
|
|
33
|
+
form.id && updateUser(form.id, form, true, false);
|
|
33
34
|
};
|
|
34
35
|
|
|
35
36
|
const rightButtonProps = {
|
|
@@ -42,7 +43,7 @@ const Profile = (props: IProps) => {
|
|
|
42
43
|
|
|
43
44
|
return (
|
|
44
45
|
<MainWrapper title="My Profile" rightButton={rightButtonProps}>
|
|
45
|
-
<UserForm form={form} setForm={setForm} user={user} />
|
|
46
|
+
<UserForm form={form} setForm={setForm} user={user} readOnlySites={true} />
|
|
46
47
|
</MainWrapper>
|
|
47
48
|
);
|
|
48
49
|
};
|
|
@@ -51,24 +52,28 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
51
52
|
user: state.users.currentUser,
|
|
52
53
|
isSaving: state.app.isSaving,
|
|
53
54
|
isLoading: state.app.isLoading,
|
|
55
|
+
token: state.app.token,
|
|
54
56
|
});
|
|
55
57
|
|
|
56
58
|
interface IDispatchProps {
|
|
57
59
|
setHistoryPush(path: string): any;
|
|
58
60
|
getUser(id: string): any;
|
|
59
|
-
|
|
61
|
+
getSites(token: string): Promise<void>;
|
|
62
|
+
updateUser(id: number, data: any, isProfile: boolean, isList: boolean): any;
|
|
60
63
|
}
|
|
61
64
|
|
|
62
65
|
const mapDispatchToProps = {
|
|
63
66
|
setHistoryPush: appActions.setHistoryPush,
|
|
64
67
|
getUser: usersActions.getUser,
|
|
65
68
|
updateUser: usersActions.updateUser,
|
|
69
|
+
getSites: sitesActions.getSites,
|
|
66
70
|
};
|
|
67
71
|
|
|
68
72
|
interface IProfileProps {
|
|
69
73
|
user: IUser;
|
|
70
74
|
isSaving: boolean;
|
|
71
75
|
isLoading: boolean;
|
|
76
|
+
token: string;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
79
|
type IProps = IProfileProps & IDispatchProps;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
|
|
3
|
+
import { CheckField } from "@ax/components";
|
|
4
|
+
|
|
5
|
+
import * as S from "./style";
|
|
6
|
+
|
|
7
|
+
const SiteItem = (props: IProps) => {
|
|
8
|
+
const { item, sites, disabled = false, readOnly = false } = props;
|
|
9
|
+
const { name, id, onChange } = item;
|
|
10
|
+
const isSelected = sites.includes(id);
|
|
11
|
+
|
|
12
|
+
const onClick = (e: React.MouseEvent) => {
|
|
13
|
+
if (readOnly) return;
|
|
14
|
+
e.preventDefault();
|
|
15
|
+
e.stopPropagation();
|
|
16
|
+
onChange();
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<S.SiteItem
|
|
21
|
+
disabled={disabled}
|
|
22
|
+
role="rowgroup"
|
|
23
|
+
readOnly={readOnly}
|
|
24
|
+
selected={!readOnly && isSelected}
|
|
25
|
+
onClick={onClick}
|
|
26
|
+
>
|
|
27
|
+
{!readOnly ? (
|
|
28
|
+
<S.CheckCell role="cell">
|
|
29
|
+
<CheckField disabled={disabled} name="check" value={id} checked={isSelected} onChange={onChange} />
|
|
30
|
+
</S.CheckCell>
|
|
31
|
+
) : null}
|
|
32
|
+
<S.NameCell disabled={disabled}>{name}</S.NameCell>
|
|
33
|
+
</S.SiteItem>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
interface IProps {
|
|
38
|
+
item: { name: string; id: number | string; onChange: () => void };
|
|
39
|
+
sites: (string | number)[];
|
|
40
|
+
disabled?: boolean;
|
|
41
|
+
readOnly?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default SiteItem;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { Cell, Row } from "@ax/components/TableList/TableItem/style";
|
|
3
|
+
|
|
4
|
+
const CheckCell = styled(Cell)`
|
|
5
|
+
padding-left: ${(p) => p.theme.spacing.m};
|
|
6
|
+
width: 32px;
|
|
7
|
+
label {
|
|
8
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
|
|
12
|
+
const NameCell = styled(Cell)<{ disabled?: boolean }>`
|
|
13
|
+
width: 40%;
|
|
14
|
+
${(p) => p.theme.textStyle.uiL};
|
|
15
|
+
color: ${(p) => (p.disabled ? p.theme.color.interactiveDisabled : p.theme.color.textHighEmphasis)};
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
const SiteItem = styled(Row)<{ disabled?: boolean; readOnly?: boolean }>`
|
|
19
|
+
pointer-events: ${(p) => (p.disabled || p.readOnly ? "none" : "auto")};
|
|
20
|
+
&:hover {
|
|
21
|
+
background-color: ${(p) =>
|
|
22
|
+
p.readOnly
|
|
23
|
+
? p.theme.color.uiBackground02
|
|
24
|
+
: p.selected
|
|
25
|
+
? p.theme.color.overlayPressedPrimary
|
|
26
|
+
: p.theme.color.overlayHoverPrimary};
|
|
27
|
+
}
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
export { CheckCell, NameCell, SiteItem };
|
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
+
import { ISite, IRootState, IUser } from "@ax/types";
|
|
3
4
|
import { appActions } from "@ax/containers/App";
|
|
4
5
|
import { usersActions } from "@ax/containers/Users";
|
|
5
|
-
import { ErrorToast, FieldsBehavior, MainWrapper } from "@ax/components";
|
|
6
|
+
import { ErrorToast, FieldsBehavior, MainWrapper, SearchField, Notification } from "@ax/components";
|
|
7
|
+
import SiteItem from "./SiteItem";
|
|
6
8
|
|
|
7
9
|
import * as S from "./style";
|
|
8
10
|
|
|
9
11
|
const UserCreate = (props: IProps) => {
|
|
10
|
-
const { setHistoryPush, createUser } = props;
|
|
12
|
+
const { setHistoryPush, createUser, sites, site, users, updateUser } = props;
|
|
13
|
+
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));
|
|
17
|
+
const sortedByNameSites = getSortedSitesByName(sites);
|
|
11
18
|
|
|
12
|
-
const
|
|
19
|
+
const [sitesList, setSiteList] = useState(sortedByNameSites);
|
|
13
20
|
const [state, setState] = useState(initState);
|
|
21
|
+
const [userAlreadyExists, setUserAlreadyExists] = useState(false);
|
|
22
|
+
const BASE_URL = isSiteView ? "/sites/users" : "/users";
|
|
14
23
|
|
|
15
24
|
const handleNameChange = (value: string) => {
|
|
16
25
|
setState({ ...state, name: value });
|
|
@@ -21,11 +30,28 @@ const UserCreate = (props: IProps) => {
|
|
|
21
30
|
};
|
|
22
31
|
|
|
23
32
|
const handleCreate = () => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
const { email } = state;
|
|
34
|
+
const globalUser = users.find((user) => user.email === email);
|
|
35
|
+
setUserAlreadyExists(false);
|
|
36
|
+
|
|
37
|
+
if (isSiteView && globalUser?.id) {
|
|
38
|
+
const { id, sites } = globalUser;
|
|
39
|
+
const userAlreadyExists = sites[0] === "all" || sites.includes(site.id);
|
|
40
|
+
if (userAlreadyExists) {
|
|
41
|
+
setUserAlreadyExists(true);
|
|
42
|
+
} else {
|
|
43
|
+
const form = { ...globalUser, sites: [...sites, site.id] };
|
|
44
|
+
updateUser(id, form, false).then(() => {
|
|
45
|
+
setHistoryPush(BASE_URL);
|
|
46
|
+
});
|
|
27
47
|
}
|
|
28
|
-
}
|
|
48
|
+
} else {
|
|
49
|
+
createUser(state).then((created: boolean) => {
|
|
50
|
+
if (created) {
|
|
51
|
+
setHistoryPush(BASE_URL);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
29
55
|
};
|
|
30
56
|
|
|
31
57
|
const rightButtonProps = {
|
|
@@ -35,12 +61,47 @@ const UserCreate = (props: IProps) => {
|
|
|
35
61
|
|
|
36
62
|
const name = state.name.trim() === "" ? "Name" : state.name;
|
|
37
63
|
|
|
64
|
+
const selectAllSites = () => {
|
|
65
|
+
const { sites } = state;
|
|
66
|
+
if (sites.includes("all")) {
|
|
67
|
+
const filteredSites = sites.filter((site: number | string) => site !== "all");
|
|
68
|
+
const sortedSites = getSortedSitesByName(filteredSites);
|
|
69
|
+
setState({ ...state, sites: sortedSites });
|
|
70
|
+
} else {
|
|
71
|
+
setState({ ...state, sites: ["all"] });
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const selectSite = (id: number) => {
|
|
76
|
+
const { sites } = state;
|
|
77
|
+
if (sites.includes(id)) {
|
|
78
|
+
const filteredSites = sites.filter((site: number | string) => site !== id && site !== "all");
|
|
79
|
+
setState({ ...state, sites: filteredSites });
|
|
80
|
+
} else {
|
|
81
|
+
const filteredSites: string[] | number[] = [...sites].filter((site) => site !== "all");
|
|
82
|
+
setState({ ...state, sites: [...filteredSites, id] });
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const filterSitesList = (query: string) => {
|
|
87
|
+
const filteredSites = sites.filter((site) =>
|
|
88
|
+
site.name.toLowerCase().replace(" ", "").includes(query.toLowerCase().replace(" ", ""))
|
|
89
|
+
);
|
|
90
|
+
const sortedSites = getSortedSitesByName(filteredSites);
|
|
91
|
+
setSiteList(sortedSites);
|
|
92
|
+
};
|
|
93
|
+
|
|
38
94
|
return (
|
|
39
95
|
<>
|
|
40
96
|
<MainWrapper backLink={true} title="New User" rightButton={rightButtonProps}>
|
|
41
97
|
<ErrorToast size="l" />
|
|
98
|
+
{userAlreadyExists && (
|
|
99
|
+
<S.NotificationWrapper>
|
|
100
|
+
<Notification type="error" text="User already has access to this site." />
|
|
101
|
+
</S.NotificationWrapper>
|
|
102
|
+
)}
|
|
42
103
|
<S.Wrapper>
|
|
43
|
-
<S.SubTitle>
|
|
104
|
+
<S.SubTitle>User data</S.SubTitle>
|
|
44
105
|
<S.NameTitle>{name}</S.NameTitle>
|
|
45
106
|
<FieldsBehavior
|
|
46
107
|
title="Name"
|
|
@@ -60,20 +121,69 @@ const UserCreate = (props: IProps) => {
|
|
|
60
121
|
value={state.email}
|
|
61
122
|
onChange={handleEmailChange}
|
|
62
123
|
/>
|
|
124
|
+
{isSiteView ? null : (
|
|
125
|
+
<>
|
|
126
|
+
<S.SettingsWrapper>
|
|
127
|
+
<S.Heading>Sites assigned</S.Heading>
|
|
128
|
+
<S.SettingContent>
|
|
129
|
+
<S.SettingText>You can give access to one or more sites to this user.</S.SettingText>
|
|
130
|
+
</S.SettingContent>
|
|
131
|
+
</S.SettingsWrapper>
|
|
132
|
+
<S.SelectAllSitesFieldWrapper>
|
|
133
|
+
<SiteItem
|
|
134
|
+
sites={state.sites}
|
|
135
|
+
item={{ name: "Access to All Sites", id: "all", onChange: selectAllSites }}
|
|
136
|
+
/>
|
|
137
|
+
</S.SelectAllSitesFieldWrapper>
|
|
138
|
+
<S.SubTitle>Sites list</S.SubTitle>
|
|
139
|
+
<S.SearchFieldWrapper>
|
|
140
|
+
<SearchField
|
|
141
|
+
disabled={state.sites[0] === "all"}
|
|
142
|
+
placeholder="Search"
|
|
143
|
+
onChange={filterSitesList}
|
|
144
|
+
searchOnEnter={false}
|
|
145
|
+
/>
|
|
146
|
+
</S.SearchFieldWrapper>
|
|
147
|
+
{sitesList.length > 0 &&
|
|
148
|
+
sitesList.map((site: any) => (
|
|
149
|
+
<SiteItem
|
|
150
|
+
disabled={state.sites[0] === "all"}
|
|
151
|
+
sites={state.sites}
|
|
152
|
+
key={site.id}
|
|
153
|
+
item={{ name: site.name, id: site.id, onChange: () => selectSite(site.id) }}
|
|
154
|
+
/>
|
|
155
|
+
))}
|
|
156
|
+
</>
|
|
157
|
+
)}
|
|
63
158
|
</S.Wrapper>
|
|
64
159
|
</MainWrapper>
|
|
65
160
|
</>
|
|
66
161
|
);
|
|
67
162
|
};
|
|
68
163
|
|
|
69
|
-
interface
|
|
164
|
+
interface IDispatchProps {
|
|
70
165
|
setHistoryPush(path: string): any;
|
|
71
166
|
createUser(data: { name: string; email: string; sites: any[] }): Promise<boolean>;
|
|
167
|
+
updateUser(id: number, data: any, isProfile: boolean): any;
|
|
168
|
+
}
|
|
169
|
+
interface IProfileProps {
|
|
170
|
+
users: IUser[];
|
|
171
|
+
sites: ISite[];
|
|
172
|
+
site: ISite;
|
|
72
173
|
}
|
|
73
174
|
|
|
175
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
176
|
+
users: state.users.users,
|
|
177
|
+
sites: state.sites.sites,
|
|
178
|
+
site: state.sites.currentSiteInfo,
|
|
179
|
+
});
|
|
180
|
+
|
|
74
181
|
const mapDispatchToProps = {
|
|
75
182
|
setHistoryPush: appActions.setHistoryPush,
|
|
76
183
|
createUser: usersActions.createUser,
|
|
184
|
+
updateUser: usersActions.updateUser,
|
|
77
185
|
};
|
|
78
186
|
|
|
79
|
-
|
|
187
|
+
type IProps = IProfileProps & IDispatchProps;
|
|
188
|
+
|
|
189
|
+
export default connect(mapStateToProps, mapDispatchToProps)(UserCreate);
|
|
@@ -17,9 +17,62 @@ const SubTitle = styled.div`
|
|
|
17
17
|
margin-bottom: ${(p) => p.theme.spacing.xs};
|
|
18
18
|
`;
|
|
19
19
|
|
|
20
|
+
const Heading = styled.div`
|
|
21
|
+
${(p) => p.theme.textStyle.headingXS};
|
|
22
|
+
color: ${(p) => p.theme.color.textHighEmphasis};
|
|
23
|
+
padding-bottom: ${(p) => p.theme.spacing.xs};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const SettingsWrapper = styled.div`
|
|
27
|
+
position: relative;
|
|
28
|
+
border-top: 1px solid ${(p) => p.theme.color.uiLine};
|
|
29
|
+
padding-top: ${(p) => p.theme.spacing.m};
|
|
30
|
+
`;
|
|
31
|
+
|
|
32
|
+
const SettingContent = styled.div`
|
|
33
|
+
padding-bottom: ${(p) => p.theme.spacing.m};
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
const SearchFieldWrapper = styled.div`
|
|
37
|
+
padding-top: ${(p) => p.theme.spacing.xs};
|
|
38
|
+
padding-bottom: ${(p) => p.theme.spacing.xs};
|
|
39
|
+
max-width: calc(${(p) => p.theme.spacing.l} * 7);
|
|
40
|
+
`;
|
|
41
|
+
|
|
42
|
+
const SelectAllSitesFieldWrapper = styled.div`
|
|
43
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
44
|
+
`;
|
|
45
|
+
|
|
46
|
+
const SettingText = styled.div`
|
|
47
|
+
${(p) => p.theme.textStyle.uiM};
|
|
48
|
+
color: ${(p) => p.theme.color.textMediumEmphasis};
|
|
49
|
+
width: calc(${(p) => p.theme.spacing.l} * 12);
|
|
50
|
+
`;
|
|
51
|
+
|
|
52
|
+
const FormWrapper = styled.div`
|
|
53
|
+
width: 720px;
|
|
54
|
+
margin: ${(p) => `${p.theme.spacing.m} 0 0 ${p.theme.spacing.m}`};
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const NotificationWrapper = styled.div`
|
|
58
|
+
position: fixed;
|
|
59
|
+
width: 100%;
|
|
60
|
+
top: ${(p) => `calc(${p.theme.spacing.s} * 4)`};
|
|
61
|
+
left: 0;
|
|
62
|
+
right: 0;
|
|
63
|
+
z-index: 2;
|
|
64
|
+
`;
|
|
20
65
|
|
|
21
66
|
export {
|
|
22
67
|
Wrapper,
|
|
23
68
|
NameTitle,
|
|
24
|
-
SubTitle
|
|
69
|
+
SubTitle,
|
|
70
|
+
Heading,
|
|
71
|
+
SettingsWrapper,
|
|
72
|
+
SettingText,
|
|
73
|
+
SettingContent,
|
|
74
|
+
SearchFieldWrapper,
|
|
75
|
+
SelectAllSitesFieldWrapper,
|
|
76
|
+
FormWrapper,
|
|
77
|
+
NotificationWrapper,
|
|
25
78
|
};
|