@griddo/ax 12.6.1 → 12.6.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/index.js +0 -1
- package/package.json +4 -3
- package/src/GlobalStore.tsx +35 -91
- package/src/__tests__/hooks/heartbeat.test.tsx +88 -0
- package/src/components/MainWrapper/index.tsx +2 -14
- package/src/containers/ActivityLog/{thunks.ts → actions.tsx} +89 -104
- package/src/containers/ActivityLog/constants.tsx +6 -0
- package/src/containers/ActivityLog/index.tsx +3 -16
- package/src/containers/ActivityLog/interfaces.tsx +12 -0
- package/src/containers/ActivityLog/reducer.tsx +24 -0
- package/src/containers/Analytics/actions.tsx +109 -0
- package/src/containers/Analytics/constants.tsx +5 -0
- package/src/containers/Analytics/index.tsx +3 -14
- package/src/containers/Analytics/interfaces.tsx +9 -0
- package/src/containers/Analytics/reducer.tsx +28 -0
- package/src/containers/Domains/actions.tsx +58 -0
- package/src/containers/Domains/constants.tsx +5 -0
- package/src/containers/Domains/index.tsx +3 -12
- package/src/containers/Domains/interfaces.tsx +9 -0
- package/src/containers/Domains/reducer.tsx +22 -0
- package/src/containers/Redirects/actions.tsx +190 -0
- package/src/containers/Redirects/constants.tsx +17 -1
- package/src/containers/Redirects/index.tsx +3 -14
- package/src/containers/Redirects/interfaces.tsx +23 -0
- package/src/containers/Redirects/reducer.tsx +35 -0
- package/src/containers/Settings/Languages/actions.tsx +88 -0
- package/src/containers/Settings/Languages/constants.tsx +6 -0
- package/src/containers/Settings/Languages/index.tsx +3 -15
- package/src/containers/Settings/Languages/reducer.tsx +31 -0
- package/src/containers/Settings/Social/actions.tsx +63 -0
- package/src/containers/Settings/Social/constants.tsx +3 -0
- package/src/containers/Settings/Social/index.tsx +3 -11
- package/src/containers/Settings/Social/interfaces.tsx +9 -0
- package/src/containers/Settings/Social/reducer.tsx +18 -0
- package/src/containers/Sites/actions.tsx +2 -6
- package/src/hooks/heartbeat.ts +32 -0
- package/src/hooks/index.tsx +2 -0
- package/src/index.tsx +6 -7
- package/src/modules/ActivityLog/ItemLog/EventItem/index.tsx +27 -13
- package/src/modules/ActivityLog/ItemLogUser/UserItem/EventItem/index.tsx +27 -13
- package/src/modules/ActivityLog/index.tsx +51 -22
- package/src/modules/Analytics/index.tsx +42 -33
- package/src/modules/App/Routing/index.tsx +4 -5
- package/src/modules/FileDrive/index.tsx +1 -1
- package/src/modules/GlobalEditor/index.tsx +6 -8
- package/src/modules/GlobalSettings/Robots/index.tsx +47 -24
- package/src/modules/MediaGallery/index.tsx +1 -1
- package/src/modules/PageEditor/index.tsx +5 -9
- package/src/modules/Redirects/RedirectItem/index.tsx +22 -9
- package/src/modules/Redirects/RedirectPanel/index.tsx +14 -13
- package/src/modules/Redirects/index.tsx +69 -26
- package/src/modules/Settings/ContentTypes/DataPacks/index.tsx +3 -4
- package/src/modules/Settings/Languages/LanguagePanel/Form/ConnectedField/index.tsx +18 -11
- package/src/modules/Settings/Languages/LanguagePanel/Form/index.tsx +9 -7
- package/src/modules/Settings/Languages/LanguagePanel/index.tsx +44 -38
- package/src/modules/Settings/Languages/Table/Header/index.tsx +19 -12
- package/src/modules/Settings/Languages/Table/Item/index.tsx +36 -21
- package/src/modules/Settings/Languages/Table/index.tsx +14 -6
- package/src/modules/Settings/Languages/index.tsx +30 -18
- package/src/modules/Settings/SeoAnalyticsSettings/Analytics/index.tsx +52 -42
- package/src/modules/Settings/Social/index.tsx +12 -17
- package/src/modules/StructuredData/StructuredDataList/index.tsx +2 -3
- package/src/types/index.tsx +6 -5
- package/tsconfig.paths.json +0 -1
- package/src/__tests__/modules/ActivityLog/ActivityLog.uc.test.tsx +0 -132
- package/src/__tests__/modules/Analytics/Analytics.uc.test.tsx +0 -168
- package/src/__tests__/modules/GlobalSettings/Robots/Robots.test.tsx +0 -110
- package/src/__tests__/modules/GlobalSettings/Robots/Robots.uc.test.tsx +0 -125
- package/src/__tests__/modules/Redirects/Redirects.uc.test.tsx +0 -164
- package/src/__tests__/modules/Settings/Languages/Languages.uc.test.tsx +0 -213
- package/src/__tests__/modules/Settings/Social/Social.uc.test.tsx +0 -171
- package/src/__tests__/store/activityLog.test.ts +0 -301
- package/src/__tests__/store/analytics.test.ts +0 -134
- package/src/__tests__/store/domains.test.ts +0 -107
- package/src/__tests__/store/languages.test.ts +0 -129
- package/src/__tests__/store/persist.test.ts +0 -47
- package/src/__tests__/store/redirects.test.ts +0 -313
- package/src/__tests__/store/social.test.ts +0 -165
- package/src/containers/ActivityLog/slice.ts +0 -45
- package/src/containers/Analytics/slice.ts +0 -48
- package/src/containers/Analytics/thunks.ts +0 -103
- package/src/containers/Domains/slice.ts +0 -37
- package/src/containers/Domains/thunks.ts +0 -60
- package/src/containers/Redirects/slice.ts +0 -60
- package/src/containers/Redirects/thunks.ts +0 -196
- package/src/containers/Settings/Languages/slice.ts +0 -48
- package/src/containers/Settings/Languages/thunks.ts +0 -83
- package/src/containers/Settings/Social/slice.ts +0 -40
- package/src/containers/Settings/Social/thunks.ts +0 -68
- package/src/store/hooks.ts +0 -37
- package/src/store/index.ts +0 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
4
|
import {
|
|
4
5
|
EmptyState,
|
|
@@ -13,8 +14,7 @@ import {
|
|
|
13
14
|
import { appActions } from "@ax/containers/App";
|
|
14
15
|
import { redirectsActions } from "@ax/containers/Redirects";
|
|
15
16
|
import { useBulkSelection, useModals, useToast } from "@ax/hooks";
|
|
16
|
-
import {
|
|
17
|
-
import type { IEmptyStateProps, INavItem, IQueryValue, IRedirect } from "@ax/types";
|
|
17
|
+
import type { IEmptyStateProps, INavItem, IQueryValue, IRedirect, IRootState, ISite } from "@ax/types";
|
|
18
18
|
|
|
19
19
|
import { DeleteModal, ImportCheckModal, ImportModal, OverwriteModal } from "./atoms";
|
|
20
20
|
import BulkHeader from "./BulkHeader";
|
|
@@ -26,15 +26,22 @@ import { getSortedListStatus } from "./utils";
|
|
|
26
26
|
import * as S from "./style";
|
|
27
27
|
|
|
28
28
|
const Redirects = (props: IProps): JSX.Element => {
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
29
|
+
const {
|
|
30
|
+
navItems,
|
|
31
|
+
currentNavItem,
|
|
32
|
+
setHistoryPush,
|
|
33
|
+
getRedirects,
|
|
34
|
+
redirects,
|
|
35
|
+
totalItems,
|
|
36
|
+
deleteRedirect,
|
|
37
|
+
addRedirect,
|
|
38
|
+
currentSite,
|
|
39
|
+
importRedirects,
|
|
40
|
+
imports,
|
|
41
|
+
totalImports,
|
|
42
|
+
isLoading,
|
|
43
|
+
isSaving,
|
|
44
|
+
} = props;
|
|
38
45
|
|
|
39
46
|
const itemsPerPage = 50;
|
|
40
47
|
const firstPage = 1;
|
|
@@ -81,7 +88,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
81
88
|
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
82
89
|
useEffect(() => {
|
|
83
90
|
const params = getParams();
|
|
84
|
-
|
|
91
|
+
getRedirects(params, filterQuery);
|
|
85
92
|
if (tableRef.current) {
|
|
86
93
|
tableRef.current.scrollTo(0, 0);
|
|
87
94
|
}
|
|
@@ -118,7 +125,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
118
125
|
} = useBulkSelection(redIds);
|
|
119
126
|
|
|
120
127
|
const bulkDelete = async () => {
|
|
121
|
-
const deleted = await
|
|
128
|
+
const deleted = await deleteRedirect(selectedItems.all);
|
|
122
129
|
if (deleted) {
|
|
123
130
|
toggleToast();
|
|
124
131
|
}
|
|
@@ -184,7 +191,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
184
191
|
};
|
|
185
192
|
|
|
186
193
|
const handleMenuClick = (path: string) => {
|
|
187
|
-
|
|
194
|
+
setHistoryPush(path);
|
|
188
195
|
};
|
|
189
196
|
|
|
190
197
|
const handleModal = () => {
|
|
@@ -195,13 +202,7 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
195
202
|
|
|
196
203
|
const handleAddRedirect = async (force?: boolean) => {
|
|
197
204
|
const toPage = formValues.to.pageId ? formValues.to.pageId : formValues.to.url;
|
|
198
|
-
|
|
199
|
-
// used to be handed: a function in `meta.arg` trips the serializableCheck, and deciding
|
|
200
|
-
// which modal to open was never the thunk's business (docs/RTK-MIGRATION.md §5).
|
|
201
|
-
const { isOriginTaken } = await dispatch(
|
|
202
|
-
redirectsActions.addRedirect({ redirect: { from: formValues.from, to: toPage }, force, filter: filterQuery }),
|
|
203
|
-
).unwrap();
|
|
204
|
-
if (isOriginTaken) toggleModal("overwrite");
|
|
205
|
+
await addRedirect({ from: formValues.from, to: toPage }, () => toggleModal("overwrite"), force, filterQuery);
|
|
205
206
|
isOpen("edit") && toggleModal("edit");
|
|
206
207
|
};
|
|
207
208
|
|
|
@@ -229,14 +230,14 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
229
230
|
|
|
230
231
|
const handleCheckImportData = async (data: { from: string; to: string }[]) => {
|
|
231
232
|
setImportData(data);
|
|
232
|
-
await
|
|
233
|
+
await importRedirects(data, true);
|
|
233
234
|
toggleModal("import");
|
|
234
235
|
setIsUploading(false);
|
|
235
236
|
toggleModal("checkImport");
|
|
236
237
|
};
|
|
237
238
|
|
|
238
239
|
const handleImportData = async () => {
|
|
239
|
-
const imported = await
|
|
240
|
+
const imported = await importRedirects(importData, false);
|
|
240
241
|
if (imported) {
|
|
241
242
|
toggleImportToast();
|
|
242
243
|
}
|
|
@@ -252,7 +253,6 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
252
253
|
<MainWrapper
|
|
253
254
|
backLink={false}
|
|
254
255
|
title="SEO Settings"
|
|
255
|
-
screen="redirects"
|
|
256
256
|
rightButton={rightButtonProps}
|
|
257
257
|
rightLineButton={rightLineButtonProps}
|
|
258
258
|
searchAction={handleSearch}
|
|
@@ -360,9 +360,52 @@ const Redirects = (props: IProps): JSX.Element => {
|
|
|
360
360
|
);
|
|
361
361
|
};
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
364
|
+
redirects: state.redirects.redirects,
|
|
365
|
+
totalItems: state.redirects.totalItems,
|
|
366
|
+
currentSite: state.sites.currentSiteInfo,
|
|
367
|
+
totalImports: state.redirects.totalImports,
|
|
368
|
+
imports: state.redirects.imports,
|
|
369
|
+
isLoading: state.app.isLoading,
|
|
370
|
+
isSaving: state.app.isSaving,
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
const mapDispatchToProps = {
|
|
374
|
+
setHistoryPush: appActions.setHistoryPush,
|
|
375
|
+
getRedirects: redirectsActions.getRedirects,
|
|
376
|
+
deleteRedirect: redirectsActions.deleteRedirect,
|
|
377
|
+
addRedirect: redirectsActions.addRedirect,
|
|
378
|
+
importRedirects: redirectsActions.importRedirects,
|
|
379
|
+
};
|
|
380
|
+
interface IRedirectsProps {
|
|
364
381
|
navItems: INavItem[];
|
|
365
382
|
currentNavItem: INavItem;
|
|
383
|
+
redirects: IRedirect[];
|
|
384
|
+
totalItems: number;
|
|
385
|
+
currentSite: ISite | null;
|
|
386
|
+
totalImports: number;
|
|
387
|
+
isLoading: boolean;
|
|
388
|
+
isSaving: boolean;
|
|
389
|
+
imports: null | {
|
|
390
|
+
error: IRedirect[];
|
|
391
|
+
existing: IRedirect[];
|
|
392
|
+
ok: IRedirect[];
|
|
393
|
+
};
|
|
366
394
|
}
|
|
367
395
|
|
|
368
|
-
|
|
396
|
+
interface IDispatchProps {
|
|
397
|
+
setHistoryPush(path: string, isEditor?: boolean): void;
|
|
398
|
+
getRedirects(params: any, filters: string): void;
|
|
399
|
+
deleteRedirect(redirectID: number[]): Promise<boolean>;
|
|
400
|
+
addRedirect(
|
|
401
|
+
redirect: { from: string; to: string | number },
|
|
402
|
+
errorAction: () => void,
|
|
403
|
+
force?: boolean,
|
|
404
|
+
filter?: string,
|
|
405
|
+
): Promise<void>;
|
|
406
|
+
importRedirects(redirects: { from: string; to: string | number }[], check: boolean): Promise<boolean>;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
type IProps = IRedirectsProps & IDispatchProps;
|
|
410
|
+
|
|
411
|
+
export default connect(mapStateToProps, mapDispatchToProps)(Redirects);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import React, { useCallback, useEffect, useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
-
import { useHistory } from "react-router-dom";
|
|
4
3
|
|
|
5
4
|
import { EmptyState, ErrorToast, Loading, MainWrapper, Modal, Toast } from "@ax/components";
|
|
6
5
|
import { appActions } from "@ax/containers/App";
|
|
@@ -31,11 +30,9 @@ const DataPacks = (props: IProps): JSX.Element => {
|
|
|
31
30
|
addDataPacks,
|
|
32
31
|
deleteSiteDataPack,
|
|
33
32
|
activatedDataPacks,
|
|
33
|
+
routerAction,
|
|
34
34
|
} = props;
|
|
35
35
|
|
|
36
|
-
// Was `state.router.action` from connected-react-router's router slice.
|
|
37
|
-
const { action: routerAction } = useHistory();
|
|
38
|
-
|
|
39
36
|
const isFromPage = selected?.templates && selected.templates.length > 0;
|
|
40
37
|
|
|
41
38
|
const { isDirty, setIsDirty } = useShouldBeSaved(selected, form);
|
|
@@ -223,6 +220,7 @@ interface IProps {
|
|
|
223
220
|
selected: any;
|
|
224
221
|
form: any;
|
|
225
222
|
activatedDataPacks: IDataPack[];
|
|
223
|
+
routerAction: string;
|
|
226
224
|
getSiteDataPacks: (queryParams?: string) => void;
|
|
227
225
|
setSelected: (dataPack?: any) => void;
|
|
228
226
|
updateDataPack: (dataPack?: any) => Promise<boolean>;
|
|
@@ -248,6 +246,7 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
248
246
|
selected: state.dataPacks.selected,
|
|
249
247
|
form: state.dataPacks.configFormData,
|
|
250
248
|
activatedDataPacks: state.dataPacks.activated,
|
|
249
|
+
routerAction: state.router.action,
|
|
251
250
|
});
|
|
252
251
|
|
|
253
252
|
export default connect(mapStateToProps, mapDispatchToProps)(DataPacks);
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import type { IRootState } from "@ax/types";
|
|
4
5
|
import { languagesActions } from "@ax/containers/Settings";
|
|
5
|
-
import {
|
|
6
|
+
import { FieldsBehavior } from "@ax/components";
|
|
6
7
|
|
|
7
8
|
const ConnectedField = (props: IProps) => {
|
|
8
|
-
const { type, name } = props;
|
|
9
|
+
const { form, type, name, updateFormValue } = props;
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
-
// Granular selector: this field only re-renders when its own value changes,
|
|
12
|
-
// not on every change to the form.
|
|
13
|
-
const value = useAppSelector((state) => state.languages.form[name as keyof typeof state.languages.form]);
|
|
11
|
+
const value = form[name];
|
|
14
12
|
|
|
15
13
|
const handleChange = (newValue: any) => {
|
|
16
|
-
|
|
14
|
+
updateFormValue({ [name]: newValue });
|
|
17
15
|
};
|
|
18
16
|
|
|
19
17
|
const fieldProps = {
|
|
@@ -26,6 +24,7 @@ const ConnectedField = (props: IProps) => {
|
|
|
26
24
|
};
|
|
27
25
|
|
|
28
26
|
interface IProps {
|
|
27
|
+
form: any;
|
|
29
28
|
type: string;
|
|
30
29
|
title: string;
|
|
31
30
|
name: string;
|
|
@@ -34,8 +33,16 @@ interface IProps {
|
|
|
34
33
|
helptext?: string;
|
|
35
34
|
options?: any;
|
|
36
35
|
filter?: any;
|
|
36
|
+
updateFormValue: (value: any) => void;
|
|
37
37
|
autoComplete?: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
41
|
+
form: state.languages.form,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const mapDispatchToProps = {
|
|
45
|
+
updateFormValue: languagesActions.updateFormValue,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export default connect(mapStateToProps, mapDispatchToProps)(ConnectedField);
|
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
4
|
+
import type { ILanguage, IRootState } from "@ax/types";
|
|
3
5
|
import { Flag } from "@ax/components";
|
|
4
|
-
import { useAppSelector } from "@ax/store";
|
|
5
|
-
import type { ILanguage } from "@ax/types";
|
|
6
|
-
|
|
7
6
|
import ConnectedField from "./ConnectedField";
|
|
8
7
|
|
|
9
8
|
import * as S from "./style";
|
|
10
9
|
|
|
11
10
|
const Form = (props: IProps): JSX.Element => {
|
|
12
|
-
const { item } = props;
|
|
13
|
-
|
|
14
|
-
const currentSiteLanguages = useAppSelector((state) => state.sites.currentSiteLanguages);
|
|
11
|
+
const { currentSiteLanguages, item } = props;
|
|
15
12
|
|
|
16
13
|
const currentSiteLanguagesIds = currentSiteLanguages.map((e) => e.id);
|
|
17
14
|
|
|
@@ -55,8 +52,13 @@ const Form = (props: IProps): JSX.Element => {
|
|
|
55
52
|
);
|
|
56
53
|
};
|
|
57
54
|
|
|
55
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
56
|
+
currentSiteLanguages: state.sites.currentSiteLanguages,
|
|
57
|
+
});
|
|
58
|
+
|
|
58
59
|
interface IProps {
|
|
59
60
|
item?: ILanguage;
|
|
61
|
+
currentSiteLanguages: ILanguage[];
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
export default Form;
|
|
64
|
+
export default connect(mapStateToProps)(Form);
|
|
@@ -1,34 +1,28 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import type { ILanguage, IRootState, ISiteLanguage } from "@ax/types";
|
|
4
5
|
import { languagesActions } from "@ax/containers/Settings";
|
|
5
|
-
import {
|
|
6
|
-
import type { ILanguage } from "@ax/types";
|
|
6
|
+
import { FloatingPanel, Button } from "@ax/components";
|
|
7
7
|
|
|
8
8
|
import Form from "./Form";
|
|
9
9
|
|
|
10
10
|
import * as S from "./style";
|
|
11
11
|
|
|
12
12
|
const LanguagePanel = (props: IProps): JSX.Element => {
|
|
13
|
-
const { isOpen, toggleModal, item } = props;
|
|
14
|
-
|
|
15
|
-
const dispatch = useAppDispatch();
|
|
16
|
-
const currentSiteID = useAppSelector((state) => state.sites.currentSiteInfo?.id ?? null);
|
|
17
|
-
const languageData = useAppSelector((state) => state.languages.form);
|
|
13
|
+
const { isOpen, toggleModal, createSiteLanguage, updateSiteLanguage, currentSiteID, languageData, item } = props;
|
|
18
14
|
|
|
19
15
|
if (!currentSiteID) {
|
|
20
16
|
throw new Error(`ERROR: User reached Languages with null site info`);
|
|
21
17
|
}
|
|
22
18
|
|
|
23
|
-
// Closes without awaiting the thunk, as the connect version did: a failure
|
|
24
|
-
// still closes the panel and only surfaces through the error toast.
|
|
25
19
|
const addItemAction = () => {
|
|
26
|
-
|
|
20
|
+
createSiteLanguage(currentSiteID, languageData);
|
|
27
21
|
toggleModal();
|
|
28
22
|
};
|
|
29
23
|
|
|
30
24
|
const editItemAction = () => {
|
|
31
|
-
|
|
25
|
+
updateSiteLanguage(currentSiteID, languageData);
|
|
32
26
|
toggleModal();
|
|
33
27
|
};
|
|
34
28
|
|
|
@@ -46,40 +40,52 @@ const LanguagePanel = (props: IProps): JSX.Element => {
|
|
|
46
40
|
|
|
47
41
|
const title = item ? "Update Language" : "New Language";
|
|
48
42
|
|
|
49
|
-
// The panel itself stays mounted — FloatingPanel animates off `isOpen` and
|
|
50
|
-
// unmounting it would drop that animation — but its body only mounts once the
|
|
51
|
-
// panel opens. The Form holds the Language and Domain AsyncSelects, each of
|
|
52
|
-
// which fetches its options on mount, and there is one panel per row plus one
|
|
53
|
-
// in the header: keeping them mounted meant N+1 requests for `/select/domains`
|
|
54
|
-
// on every visit to a screen where the user may open no panel at all. The form
|
|
55
|
-
// state lives in the store (the row dispatches setForm before opening), so
|
|
56
|
-
// mounting late loses nothing.
|
|
57
43
|
return (
|
|
58
44
|
<FloatingPanel title={title} toggleModal={toggleModal} isOpen={isOpen}>
|
|
59
|
-
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</S.Wrapper>
|
|
74
|
-
)}
|
|
45
|
+
<S.Wrapper>
|
|
46
|
+
<Form item={item} />
|
|
47
|
+
<S.Footer>
|
|
48
|
+
{item ? (
|
|
49
|
+
<Button className="button" type="button" onClick={editButton.action} disabled={editButton.disabled}>
|
|
50
|
+
{editButton.label}
|
|
51
|
+
</Button>
|
|
52
|
+
) : (
|
|
53
|
+
<Button className="button" type="button" onClick={addButton.action} disabled={addButton.disabled}>
|
|
54
|
+
{addButton.label}
|
|
55
|
+
</Button>
|
|
56
|
+
)}
|
|
57
|
+
</S.Footer>
|
|
58
|
+
</S.Wrapper>
|
|
75
59
|
</FloatingPanel>
|
|
76
60
|
);
|
|
77
61
|
};
|
|
78
62
|
|
|
79
|
-
|
|
63
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
64
|
+
currentSiteID: state.sites.currentSiteInfo && state.sites.currentSiteInfo.id,
|
|
65
|
+
languageData: state.languages.form,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const mapDispatchToProps = {
|
|
69
|
+
createSiteLanguage: languagesActions.createSiteLanguage,
|
|
70
|
+
updateSiteLanguage: languagesActions.updateSiteLanguage,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
interface IStateProps {
|
|
74
|
+
currentSiteID: number | null;
|
|
75
|
+
languageData: ISiteLanguage;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
interface ILanguagePanelProps {
|
|
80
79
|
item?: ILanguage;
|
|
81
80
|
isOpen: boolean;
|
|
82
81
|
toggleModal(): any;
|
|
83
82
|
}
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
interface IDispatchProps {
|
|
85
|
+
createSiteLanguage(siteID: number, data: ISiteLanguage): void;
|
|
86
|
+
updateSiteLanguage(siteID: number, data: ISiteLanguage): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
type IProps = IDispatchProps & ILanguagePanelProps & IStateProps;
|
|
90
|
+
|
|
91
|
+
export default connect(mapStateToProps, mapDispatchToProps)(LanguagePanel);
|
|
@@ -1,27 +1,26 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
|
-
import { IconAction } from "@ax/components";
|
|
4
|
-
import { languagesActions } from "@ax/containers/Settings";
|
|
5
|
-
import { Restricted } from "@ax/guards";
|
|
6
4
|
import { useModal } from "@ax/hooks";
|
|
7
|
-
import {
|
|
5
|
+
import { languagesActions } from "@ax/containers/Settings";
|
|
8
6
|
import type { ILanguage } from "@ax/types";
|
|
9
|
-
|
|
7
|
+
import { IconAction } from "@ax/components";
|
|
8
|
+
import { Restricted } from "@ax/guards";
|
|
10
9
|
import LanguagePanel from "../../LanguagePanel";
|
|
11
10
|
|
|
12
11
|
import * as S from "./style";
|
|
13
12
|
|
|
14
13
|
const Header = (props: IProps): JSX.Element => {
|
|
15
|
-
const { items } = props;
|
|
16
|
-
|
|
17
|
-
const dispatch = useAppDispatch();
|
|
14
|
+
const { items, resetForm } = props;
|
|
18
15
|
|
|
19
16
|
const title = items.length > 1 ? `${items.length} items` : `${items.length} item`;
|
|
20
17
|
|
|
21
18
|
const { isOpen, toggleModal } = useModal();
|
|
22
19
|
|
|
20
|
+
const resetValues = () => resetForm();
|
|
21
|
+
|
|
23
22
|
const openModal = () => {
|
|
24
|
-
|
|
23
|
+
resetValues();
|
|
25
24
|
toggleModal();
|
|
26
25
|
};
|
|
27
26
|
|
|
@@ -35,9 +34,17 @@ const Header = (props: IProps): JSX.Element => {
|
|
|
35
34
|
</S.Header>
|
|
36
35
|
);
|
|
37
36
|
};
|
|
38
|
-
|
|
39
|
-
interface IProps {
|
|
37
|
+
interface ITableHeaderProps {
|
|
40
38
|
items: ILanguage[];
|
|
41
39
|
}
|
|
40
|
+
interface IDispatchProps {
|
|
41
|
+
resetForm(): void;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
type IProps = ITableHeaderProps & IDispatchProps;
|
|
45
|
+
|
|
46
|
+
const mapDispatchToProps = {
|
|
47
|
+
resetForm: languagesActions.resetForm,
|
|
48
|
+
};
|
|
42
49
|
|
|
43
|
-
export default Header;
|
|
50
|
+
export default connect(null, mapDispatchToProps)(Header);
|
|
@@ -1,32 +1,26 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
4
|
import { ContextMenu, Flag } from "@ax/components";
|
|
4
|
-
import { languagesActions } from "@ax/containers/Settings";
|
|
5
5
|
import { useContextMenu, useModal, usePermission } from "@ax/hooks";
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
8
|
-
|
|
6
|
+
import type { ILanguage, ISiteLanguage, IRootState, ISite } from "@ax/types";
|
|
7
|
+
import { languagesActions } from "@ax/containers/Settings";
|
|
9
8
|
import LanguagePanel from "../../LanguagePanel";
|
|
10
9
|
|
|
11
10
|
import * as S from "./style";
|
|
12
11
|
|
|
13
|
-
const Item = (props:
|
|
14
|
-
const { item } = props;
|
|
12
|
+
const Item = (props: IItemProps): JSX.Element => {
|
|
13
|
+
const { item, resetForm, setForm, deleteSiteLanguage, currentSiteInfo } = props;
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
if (!currentSiteInfo) {
|
|
16
|
+
throw new Error(`ERROR: User reached Site Languages with null site info`);
|
|
17
|
+
}
|
|
18
18
|
|
|
19
19
|
const { isOpen, toggleModal } = useModal();
|
|
20
20
|
const isAllowedToEdit = usePermission("general.editLanguage");
|
|
21
21
|
const isAllowedToDelete = usePermission("general.deleteLanguages");
|
|
22
22
|
const { contextMenu, handleContextMenu, closeContextMenu } = useContextMenu();
|
|
23
23
|
|
|
24
|
-
// After the hooks, not before: bailing out earlier would make the hook order
|
|
25
|
-
// depend on state. Same guard as the connect version, just moved down.
|
|
26
|
-
if (!currentSiteInfo) {
|
|
27
|
-
throw new Error(`ERROR: User reached Site Languages with null site info`);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
24
|
const siteLanguage: ISiteLanguage = {
|
|
31
25
|
language: item.id,
|
|
32
26
|
path: item.path || "",
|
|
@@ -34,8 +28,7 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
34
28
|
isDefault: item.isDefault,
|
|
35
29
|
};
|
|
36
30
|
|
|
37
|
-
const removeItemAction = () =>
|
|
38
|
-
dispatch(languagesActions.deleteSiteLanguage({ siteID: currentSiteInfo.id, languageID: item.id }));
|
|
31
|
+
const removeItemAction = () => deleteSiteLanguage(currentSiteInfo.id, item.id);
|
|
39
32
|
|
|
40
33
|
const menuOptions = isAllowedToDelete
|
|
41
34
|
? [
|
|
@@ -47,9 +40,13 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
47
40
|
]
|
|
48
41
|
: [];
|
|
49
42
|
|
|
43
|
+
const resetValues = () => resetForm();
|
|
44
|
+
|
|
45
|
+
const setItem = (item: ISiteLanguage) => setForm(item);
|
|
46
|
+
|
|
50
47
|
const openModal = () => {
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
resetValues();
|
|
49
|
+
setItem(siteLanguage);
|
|
53
50
|
toggleModal();
|
|
54
51
|
};
|
|
55
52
|
|
|
@@ -71,7 +68,25 @@ const Item = (props: IProps): JSX.Element => {
|
|
|
71
68
|
|
|
72
69
|
interface IProps {
|
|
73
70
|
item: ILanguage;
|
|
71
|
+
currentSiteInfo: ISite | null;
|
|
74
72
|
}
|
|
75
73
|
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
75
|
+
currentSiteInfo: state.sites.currentSiteInfo,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const mapDispatchToProps = {
|
|
79
|
+
resetForm: languagesActions.resetForm,
|
|
80
|
+
setForm: languagesActions.setForm,
|
|
81
|
+
deleteSiteLanguage: languagesActions.deleteSiteLanguage,
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
interface IDispatchProps {
|
|
85
|
+
setForm(item: ISiteLanguage): void;
|
|
86
|
+
resetForm(): void;
|
|
87
|
+
deleteSiteLanguage(siteId: number, languageId: number): void;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
type IItemProps = IProps & IDispatchProps;
|
|
91
|
+
|
|
92
|
+
export default connect(mapStateToProps, mapDispatchToProps)(Item);
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import type { ILanguage } from "@ax/types";
|
|
4
|
+
import type { ILanguage, IRootState } from "@ax/types";
|
|
5
5
|
|
|
6
|
-
import Header from "./Header";
|
|
7
6
|
import Item from "./Item";
|
|
7
|
+
import Header from "./Header";
|
|
8
8
|
|
|
9
9
|
import * as S from "./style";
|
|
10
10
|
|
|
11
|
-
const Table = (): JSX.Element => {
|
|
12
|
-
const items =
|
|
11
|
+
const Table = (props: IStateProps): JSX.Element => {
|
|
12
|
+
const { items } = props;
|
|
13
13
|
|
|
14
14
|
const mapItemList = (menu: any) =>
|
|
15
15
|
menu && menu.map((item: ILanguage, i: number) => <Item key={`${item.label}${i}`} item={item} />);
|
|
@@ -22,4 +22,12 @@ const Table = (): JSX.Element => {
|
|
|
22
22
|
);
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
26
|
+
items: state.sites.currentSiteLanguages,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
interface IStateProps {
|
|
30
|
+
items: ILanguage[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default connect(mapStateToProps)(Table);
|
|
@@ -1,37 +1,30 @@
|
|
|
1
1
|
import React, { useEffect } from "react";
|
|
2
|
+
import { connect } from "react-redux";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import { appActions } from "@ax/containers/App";
|
|
4
|
+
import type { INavItem, IRootState, ISite } from "@ax/types";
|
|
5
5
|
import { sitesActions } from "@ax/containers/Sites";
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
6
|
+
import { appActions } from "@ax/containers/App";
|
|
7
|
+
import { MainWrapper, Loading, ErrorToast, Nav } from "@ax/components";
|
|
8
8
|
|
|
9
9
|
import Table from "./Table";
|
|
10
10
|
|
|
11
11
|
import * as S from "./style";
|
|
12
12
|
|
|
13
13
|
const Languages = (props: IProps): JSX.Element => {
|
|
14
|
-
const { navItems, currentNavItem } = props;
|
|
15
|
-
|
|
16
|
-
const dispatch = useAppDispatch();
|
|
17
|
-
const currentSiteInfo = useAppSelector((state) => state.sites.currentSiteInfo);
|
|
18
|
-
const isLoading = useAppSelector((state) => state.app.isLoading);
|
|
14
|
+
const { isLoading, currentSiteInfo, getSiteLanguages, setHistoryPush, navItems, currentNavItem } = props;
|
|
19
15
|
|
|
20
|
-
// getSiteLanguages and setHistoryPush are still legacy thunks from other areas
|
|
21
|
-
// (sites, app). The RTK dispatch takes them unchanged, which is what makes the
|
|
22
|
-
// area-by-area strangler possible.
|
|
23
16
|
useEffect(() => {
|
|
24
17
|
if (currentSiteInfo) {
|
|
25
|
-
|
|
18
|
+
getSiteLanguages(currentSiteInfo.id);
|
|
26
19
|
}
|
|
27
|
-
}, [currentSiteInfo,
|
|
20
|
+
}, [currentSiteInfo, getSiteLanguages]);
|
|
28
21
|
|
|
29
22
|
const handleMenuClick = (path: string) => {
|
|
30
|
-
|
|
23
|
+
setHistoryPush(path);
|
|
31
24
|
};
|
|
32
25
|
|
|
33
26
|
return (
|
|
34
|
-
<MainWrapper title={`Languages`}
|
|
27
|
+
<MainWrapper title={`Languages`}>
|
|
35
28
|
<S.Wrapper>
|
|
36
29
|
<Nav current={currentNavItem} items={navItems} onClick={handleMenuClick} />
|
|
37
30
|
<S.ContentWrapper>
|
|
@@ -49,9 +42,28 @@ const Languages = (props: IProps): JSX.Element => {
|
|
|
49
42
|
);
|
|
50
43
|
};
|
|
51
44
|
|
|
52
|
-
|
|
45
|
+
const mapStateToProps = (state: IRootState) => ({
|
|
46
|
+
currentSiteInfo: state.sites.currentSiteInfo,
|
|
47
|
+
isLoading: state.app.isLoading,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const mapDispatchToProps = {
|
|
51
|
+
getSiteLanguages: sitesActions.getSiteLanguages,
|
|
52
|
+
setHistoryPush: appActions.setHistoryPush,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
interface IDispatchProps {
|
|
56
|
+
getSiteLanguages(siteID: number): void;
|
|
57
|
+
setHistoryPush(path: string, isEditor?: boolean): void;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface ILanguagesProps {
|
|
61
|
+
currentSiteInfo: ISite | null;
|
|
62
|
+
isLoading: boolean;
|
|
53
63
|
navItems: INavItem[];
|
|
54
64
|
currentNavItem: INavItem;
|
|
55
65
|
}
|
|
56
66
|
|
|
57
|
-
|
|
67
|
+
type IProps = ILanguagesProps & IDispatchProps;
|
|
68
|
+
|
|
69
|
+
export default connect(mapStateToProps, mapDispatchToProps)(Languages);
|