@griddo/ax 11.14.1 → 11.14.2-rc.1
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/jest/reactEasyCropMock.js +15 -0
- package/config/jest/reactTimezoneMock.js +13 -0
- package/package.json +221 -219
- package/public/img/welcome.svg +127 -0
- package/src/__tests__/components/Browser/Browser.test.tsx +27 -51
- package/src/__tests__/components/CategoryCell/CategoryCell.test.tsx +10 -5
- package/src/__tests__/components/ElementsTooltip/ElementsTooltip.test.tsx +27 -14
- package/src/__tests__/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/ErrorItem.test.tsx +2 -0
- package/src/__tests__/components/HeadingsPreviewModal/HeadingsPreviewModal.utils.test.tsx +138 -1
- package/src/__tests__/components/ImageDragAndDrop/CropStep/CropStep.test.tsx +84 -0
- package/src/__tests__/components/ImageDragAndDrop/ImageDragAndDrop.test.tsx +169 -0
- package/src/__tests__/components/ProfileImage/ProfileImage.test.tsx +120 -0
- package/src/__tests__/components/ResizePanel/ResizePanel.test.tsx +8 -0
- package/src/__tests__/components/UserRolesAndSites/RoleItem/RoleItem.test.tsx +190 -0
- package/src/__tests__/components/UserRolesAndSites/UserRolesAndSites.test.tsx +471 -0
- package/src/__tests__/modules/FramePreview/HeadingsOverlay/HeadingsOverlay.test.tsx +15 -2
- package/src/__tests__/modules/Sites/Sites.test.tsx +68 -224
- package/src/__tests__/modules/Sites/SitesList/ListView/BulkHeader/BulkHeader.test.tsx +21 -17
- package/src/__tests__/modules/Sites/SitesList/SitesList.test.tsx +65 -565
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/DataStep/DataStep.test.tsx +109 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/FinalStep/FinalStep.test.tsx +157 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/CropView.test.tsx +51 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/ImageStep.test.tsx +70 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/UploadView.test.tsx +92 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/TimezoneStep/TimezoneStep.test.tsx +94 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeModal.test.tsx +78 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/WelcomeStep/WelcomeStep.test.tsx +39 -0
- package/src/__tests__/modules/Sites/SitesList/WelcomeModal/utils.test.ts +55 -0
- package/src/api/sites.tsx +4 -4
- package/src/components/Avatar/index.tsx +26 -5
- package/src/components/Avatar/style.tsx +20 -10
- package/src/components/Browser/index.tsx +7 -1
- package/src/components/ConfigPanel/index.tsx +5 -4
- package/src/components/ElementsTooltip/index.tsx +96 -34
- package/src/components/ElementsTooltip/style.tsx +12 -1
- package/src/components/Fields/FileField/index.tsx +16 -17
- package/src/components/Fields/HeadingField/index.tsx +1 -1
- package/src/components/Fields/ImageField/index.tsx +9 -38
- package/src/components/Fields/ImageField/style.tsx +12 -1
- package/src/components/Fields/ToggleField/index.tsx +1 -1
- package/src/components/Fields/Wysiwyg/index.tsx +25 -20
- package/src/components/FileGallery/GalleryPanel/index.tsx +15 -7
- package/src/components/FileGallery/index.tsx +33 -28
- package/src/components/Gallery/GalleryPanel/index.tsx +5 -16
- package/src/components/Gallery/index.tsx +0 -2
- package/src/components/HeadingsPreviewModal/ErrorsBanner/ErrorItem/index.tsx +11 -2
- package/src/components/HeadingsPreviewModal/ErrorsBanner/index.tsx +21 -3
- package/src/components/HeadingsPreviewModal/ErrorsBanner/style.tsx +2 -2
- package/src/components/HeadingsPreviewModal/index.tsx +13 -3
- package/src/components/HeadingsPreviewModal/style.tsx +18 -0
- package/src/components/HeadingsPreviewModal/utils.tsx +31 -3
- package/src/components/Image/index.tsx +2 -2
- package/src/components/ImageDragAndDrop/CropStep/index.tsx +95 -0
- package/src/components/ImageDragAndDrop/CropStep/style.tsx +101 -0
- package/src/{modules/MediaGallery → components}/ImageDragAndDrop/index.tsx +103 -40
- package/src/{modules/MediaGallery → components}/ImageDragAndDrop/style.tsx +14 -2
- package/src/components/ProfileImage/index.tsx +55 -0
- package/src/components/ProfileImage/style.tsx +58 -0
- package/src/components/ResizePanel/ResizeHandle/index.tsx +44 -6
- package/src/components/ResizePanel/ResizeHandle/style.tsx +7 -0
- package/src/components/ResizePanel/index.tsx +25 -4
- package/src/components/Tabs/style.tsx +1 -1
- package/src/components/Tag/index.tsx +0 -1
- package/src/components/UserRolesAndSites/RoleItem/index.tsx +42 -0
- package/src/components/UserRolesAndSites/RoleItem/style.tsx +29 -0
- package/src/components/UserRolesAndSites/index.tsx +102 -0
- package/src/components/UserRolesAndSites/style.tsx +67 -0
- package/src/components/index.tsx +6 -0
- package/src/constants/index.ts +13 -1
- package/src/containers/App/actions.tsx +8 -1
- package/src/containers/Sites/actions.tsx +26 -0
- package/src/containers/Sites/constants.tsx +1 -0
- package/src/containers/Sites/interfaces.tsx +6 -0
- package/src/containers/Sites/reducer.tsx +5 -1
- package/src/containers/Users/reducer.tsx +6 -5
- package/src/guards/routeLeaving/index.tsx +9 -11
- package/src/helpers/images.tsx +50 -3
- package/src/helpers/index.tsx +2 -1
- package/src/hooks/forms.tsx +45 -48
- package/src/hooks/modals.tsx +4 -3
- package/src/modules/ActivityLog/ItemLogUser/UserItem/index.tsx +1 -1
- package/src/modules/App/Routing/Logout/index.tsx +3 -5
- package/src/modules/App/Routing/NavMenu/NavItem/index.tsx +73 -52
- package/src/modules/App/Routing/NavMenu/NavItem/style.tsx +21 -7
- package/src/modules/App/Routing/NavMenu/index.tsx +59 -54
- package/src/modules/App/Routing/NavMenu/style.tsx +13 -11
- package/src/modules/CreatePass/index.tsx +1 -1
- package/src/modules/FileDrive/FileDragAndDrop/index.tsx +11 -8
- package/src/modules/FileDrive/FileModal/index.tsx +8 -9
- package/src/modules/FileDrive/index.tsx +1 -18
- package/src/modules/Forms/FormEditor/index.tsx +1 -1
- package/src/modules/FramePreview/HeadingsOverlay/index.tsx +22 -11
- package/src/modules/FramePreview/HeadingsOverlay/style.tsx +1 -1
- package/src/modules/MediaGallery/ImageModal/index.tsx +1 -5
- package/src/modules/MediaGallery/index.tsx +1 -3
- package/src/modules/Settings/Globals/constants.tsx +942 -106
- package/src/modules/Sites/SitesList/AllSitesHeader/index.tsx +33 -0
- package/src/modules/Sites/SitesList/AllSitesHeader/style.tsx +35 -0
- package/src/modules/Sites/SitesList/GridView/GridHeaderFilter/index.tsx +5 -5
- package/src/modules/Sites/SitesList/GridView/GridSiteItem/index.tsx +23 -119
- package/src/modules/Sites/SitesList/ListView/BulkHeader/TableHeader/index.tsx +4 -4
- package/src/modules/Sites/SitesList/ListView/BulkHeader/index.tsx +4 -3
- package/src/modules/Sites/SitesList/ListView/ListSiteItem/index.tsx +23 -120
- package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/index.tsx +4 -5
- package/src/modules/Sites/SitesList/RecentSites/index.tsx +49 -0
- package/src/modules/Sites/SitesList/RecentSites/style.tsx +92 -0
- package/src/modules/Sites/SitesList/SiteModal/index.tsx +8 -7
- package/src/modules/Sites/SitesList/WelcomeModal/DataStep/index.tsx +72 -0
- package/src/modules/Sites/SitesList/WelcomeModal/DataStep/style.tsx +59 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/constants.tsx +78 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/index.tsx +78 -0
- package/src/modules/Sites/SitesList/WelcomeModal/FinalStep/style.tsx +141 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/index.tsx +93 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/CropView/style.tsx +77 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/index.tsx +100 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/UploadView/style.tsx +94 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/index.tsx +44 -0
- package/src/modules/Sites/SitesList/WelcomeModal/ImageStep/style.tsx +31 -0
- package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/index.tsx +51 -0
- package/src/modules/Sites/SitesList/WelcomeModal/TimezoneStep/style.tsx +52 -0
- package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/index.tsx +40 -0
- package/src/modules/Sites/SitesList/WelcomeModal/WelcomeStep/style.tsx +53 -0
- package/src/modules/Sites/SitesList/WelcomeModal/index.tsx +215 -0
- package/src/modules/Sites/SitesList/WelcomeModal/style.tsx +12 -0
- package/src/modules/Sites/SitesList/WelcomeModal/utils.ts +26 -0
- package/src/modules/Sites/SitesList/atoms.tsx +4 -4
- package/src/modules/Sites/SitesList/hooks.tsx +149 -16
- package/src/modules/Sites/SitesList/index.tsx +127 -125
- package/src/modules/Sites/SitesList/style.tsx +1 -117
- package/src/modules/Sites/SitesList/utils.tsx +9 -2
- package/src/modules/Sites/index.tsx +19 -8
- package/src/modules/Users/Profile/index.tsx +169 -31
- package/src/modules/Users/Profile/style.tsx +81 -1
- package/src/modules/Users/Roles/RoleItem/index.tsx +2 -2
- package/src/modules/Users/UserCreate/SiteItem/index.tsx +11 -14
- package/src/modules/Users/UserForm/atoms.tsx +3 -3
- package/src/modules/Users/UserForm/index.tsx +25 -29
- package/src/modules/Users/UserForm/style.tsx +15 -2
- package/src/modules/Users/UserList/UserItem/index.tsx +4 -4
- package/src/routes/index.tsx +1 -0
- package/src/types/index.tsx +2 -0
- /package/src/modules/Sites/SitesList/{RecentSiteItem → RecentSites/RecentSiteItem}/style.tsx +0 -0
package/src/hooks/forms.tsx
CHANGED
|
@@ -2,7 +2,6 @@ import { memo, useCallback, useEffect, useRef, useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
import { cleanPageValues } from "@ax/forms";
|
|
4
4
|
import { deepClone, isEmptyObj } from "@ax/helpers";
|
|
5
|
-
import type { FormContent, IUser } from "@ax/types";
|
|
6
5
|
|
|
7
6
|
import isEqual from "lodash.isequal";
|
|
8
7
|
|
|
@@ -197,60 +196,58 @@ const useIsDirty = (
|
|
|
197
196
|
return { isDirty, setIsDirty, resetDirty };
|
|
198
197
|
};
|
|
199
198
|
|
|
200
|
-
const
|
|
201
|
-
|
|
199
|
+
const useShouldBeSaved = (form: Record<string, any> | null | undefined, debug = DEBUG_DIRTY) => {
|
|
200
|
+
const [isDirty, setIsDirtyState] = useState(false);
|
|
201
|
+
const originalRef = useRef<Record<string, any>>();
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const formRef = useRef();
|
|
214
|
-
|
|
215
|
-
const stringValue = form && JSON.stringify(form);
|
|
203
|
+
// Intercept setIsDirty to update baseline when saved
|
|
204
|
+
const setIsDirty = useCallback(
|
|
205
|
+
(value: boolean) => {
|
|
206
|
+
setIsDirtyState(value);
|
|
207
|
+
if (value === false && form) {
|
|
208
|
+
originalRef.current = deepClone(form);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
[form],
|
|
212
|
+
);
|
|
216
213
|
|
|
217
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO fix this
|
|
218
214
|
useEffect(() => {
|
|
219
|
-
if (!
|
|
220
|
-
|
|
215
|
+
if (!form) {
|
|
216
|
+
if (debug) console.log("[useShouldBeSaved] form=null → isDirty=false");
|
|
217
|
+
setIsDirtyState(false);
|
|
218
|
+
return;
|
|
221
219
|
}
|
|
222
|
-
if (form !== null) {
|
|
223
|
-
const { cleanUpdatedValues, cleanOriginalValues } = cleanModified(formRef.current, form);
|
|
224
|
-
const isFormDirty =
|
|
225
|
-
cleanOriginalValues.id && !cleanUpdatedValues.id ? false : !isEqual(cleanUpdatedValues, cleanOriginalValues);
|
|
226
220
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
console.log("Stored ref (cleaned):", cleanUpdatedValues);
|
|
233
|
-
console.log("Current (cleaned):", cleanOriginalValues);
|
|
234
|
-
if (cleanOriginalValues.id && !cleanUpdatedValues.id) {
|
|
235
|
-
console.log("Suppressed: original has id, ref does not.");
|
|
236
|
-
}
|
|
237
|
-
const diffs = getChangedKeys(
|
|
238
|
-
cleanUpdatedValues as Record<string, unknown>,
|
|
239
|
-
cleanOriginalValues as Record<string, unknown>,
|
|
240
|
-
);
|
|
241
|
-
if (diffs.length > 0) {
|
|
242
|
-
console.table(diffs);
|
|
243
|
-
}
|
|
244
|
-
console.groupEnd();
|
|
245
|
-
}
|
|
221
|
+
// Initialize baseline on first render
|
|
222
|
+
if (!originalRef.current) {
|
|
223
|
+
originalRef.current = deepClone(form);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
246
226
|
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
|
|
227
|
+
// Compare against the baseline (original)
|
|
228
|
+
const original = { ...originalRef.current } as Record<string, unknown>;
|
|
229
|
+
const current = { ...form } as Record<string, unknown>;
|
|
230
|
+
delete original.modified;
|
|
231
|
+
delete current.modified;
|
|
232
|
+
|
|
233
|
+
const hasChanges = !isEqual(original, current);
|
|
234
|
+
|
|
235
|
+
if (debug) {
|
|
236
|
+
console.groupCollapsed(
|
|
237
|
+
`[useShouldBeSaved] isDirty: %c${hasChanges}`,
|
|
238
|
+
hasChanges ? "color: red; font-weight: bold" : "color: green",
|
|
239
|
+
);
|
|
240
|
+
console.log("Original (cleaned):", original);
|
|
241
|
+
console.log("Current (cleaned):", current);
|
|
242
|
+
const diffs = getChangedKeys(original as Record<string, unknown>, current as Record<string, unknown>);
|
|
243
|
+
if (diffs.length > 0) {
|
|
244
|
+
console.table(diffs);
|
|
245
|
+
}
|
|
246
|
+
console.groupEnd();
|
|
252
247
|
}
|
|
253
|
-
|
|
248
|
+
|
|
249
|
+
setIsDirtyState(hasChanges);
|
|
250
|
+
}, [form, debug]);
|
|
254
251
|
|
|
255
252
|
return { isDirty, setIsDirty };
|
|
256
253
|
};
|
package/src/hooks/modals.tsx
CHANGED
|
@@ -2,9 +2,10 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
2
2
|
|
|
3
3
|
const useModal = (initialState?: boolean, bodyBlock = true) => {
|
|
4
4
|
const [isOpen, setIsOpen] = useState(initialState || false);
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
|
|
6
|
+
const toggleModal = useCallback(() => {
|
|
7
|
+
setIsOpen((prev) => !prev);
|
|
8
|
+
}, []);
|
|
8
9
|
|
|
9
10
|
useEffect(() => {
|
|
10
11
|
if (isOpen && bodyBlock) {
|
|
@@ -19,7 +19,7 @@ const UserItem = (props: IUserItemProps) => {
|
|
|
19
19
|
<S.Item>
|
|
20
20
|
<S.User>
|
|
21
21
|
<S.AvatarWrapper>
|
|
22
|
-
<Avatar image={image} name={name || ""} size=
|
|
22
|
+
<Avatar image={image} name={name || ""} size={24} />
|
|
23
23
|
</S.AvatarWrapper>
|
|
24
24
|
<S.Name>{name}</S.Name>
|
|
25
25
|
{!isOpen && <S.CounterWrapper>{logActivity.length} events created</S.CounterWrapper>}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import { ILogoutAction } from "@ax/containers/App/interfaces";
|
|
5
4
|
import { appActions } from "@ax/containers/App";
|
|
5
|
+
import type { ILogoutAction } from "@ax/containers/App/interfaces";
|
|
6
6
|
|
|
7
7
|
const Logout = (props: IProps) => {
|
|
8
8
|
const { logout, setHistoryPush } = props;
|
|
9
9
|
|
|
10
10
|
useEffect(() => {
|
|
11
|
+
logout();
|
|
11
12
|
setHistoryPush("/login");
|
|
12
|
-
return () => {
|
|
13
|
-
logout();
|
|
14
|
-
};
|
|
15
13
|
}, [logout, setHistoryPush]);
|
|
16
14
|
|
|
17
15
|
return <></>;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import React, { useContext } from "react";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Avatar, Icon } from "@ax/components";
|
|
4
4
|
import { Restricted } from "@ax/guards";
|
|
5
|
-
import {
|
|
5
|
+
import type { IRouter } from "@ax/routes";
|
|
6
6
|
|
|
7
|
-
import NavSubItem from "./NavSubItem";
|
|
8
7
|
import { NavContext } from "./../context";
|
|
8
|
+
import NavSubItem from "./NavSubItem";
|
|
9
9
|
|
|
10
10
|
import * as S from "./style";
|
|
11
11
|
|
|
12
12
|
const NavItem = (props: IProps) => {
|
|
13
13
|
const {
|
|
14
|
-
route: { component, path, name, icon, routesGroups, onClick, url, target = "_blank" },
|
|
14
|
+
route: { component, path, name, icon, routesGroups, onClick, url, target = "_blank", avatar },
|
|
15
15
|
location,
|
|
16
16
|
type,
|
|
17
17
|
isOpened,
|
|
@@ -20,89 +20,110 @@ const NavItem = (props: IProps) => {
|
|
|
20
20
|
|
|
21
21
|
const { state, toggleSubmenu } = useContext(NavContext)!;
|
|
22
22
|
|
|
23
|
-
const isSelected = location
|
|
23
|
+
const isSelected = location ? location.pathname === path || location.pathname.includes(path) : false;
|
|
24
24
|
const isSelectedClass = isSelected ? "selected" : "";
|
|
25
|
+
const hasImage = avatar ? "withImage" : "";
|
|
25
26
|
|
|
26
27
|
const hasSubmenu = routesGroups && routesGroups.length > 0;
|
|
28
|
+
const isSubmenuOpen = !!state[name];
|
|
27
29
|
|
|
28
30
|
const handleClick = () => {
|
|
29
31
|
if (onClick) {
|
|
30
32
|
onClick();
|
|
31
33
|
} else if (url) {
|
|
32
34
|
window.open(url, target);
|
|
33
|
-
} else if (component && location.pathname !== path) {
|
|
35
|
+
} else if (component && location && location.pathname !== path) {
|
|
34
36
|
setHistoryPush(path);
|
|
35
37
|
} else if (isOpened && hasSubmenu) {
|
|
36
38
|
toggleSubmenu(name);
|
|
37
39
|
}
|
|
38
40
|
};
|
|
39
41
|
|
|
40
|
-
const subMenuIcon =
|
|
42
|
+
const subMenuIcon = isSubmenuOpen ? "UpArrow" : "DownArrow";
|
|
41
43
|
|
|
42
44
|
const DropdownClass = isOpened ? "inline" : "floating";
|
|
43
45
|
|
|
44
46
|
return (
|
|
45
|
-
<S.Item key={name} type={type} isOpened={isOpened} isSubmenuOpen={
|
|
46
|
-
<S.NavLink onClick={handleClick} className={isSelectedClass}>
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
<S.Item key={name} type={type} isOpened={isOpened} isSubmenuOpen={isSubmenuOpen} active={isSelected}>
|
|
48
|
+
<S.NavLink onClick={handleClick} className={`${isSelectedClass} ${hasImage}`}>
|
|
49
|
+
{avatar ? (
|
|
50
|
+
<S.AvatarWithBorder>
|
|
51
|
+
<Avatar image={avatar.image} name={avatar.name} size={32} background bold />
|
|
52
|
+
</S.AvatarWithBorder>
|
|
53
|
+
) : (
|
|
54
|
+
<S.Icon>{icon && <Icon name={icon} size="24px" />}</S.Icon>
|
|
55
|
+
)}
|
|
56
|
+
<S.LinkName active={isSelected || isSubmenuOpen}>{name}</S.LinkName>
|
|
49
57
|
{hasSubmenu && isOpened && (
|
|
50
58
|
<S.Arrow>
|
|
51
59
|
<Icon name={subMenuIcon} />
|
|
52
60
|
</S.Arrow>
|
|
53
61
|
)}
|
|
54
62
|
</S.NavLink>
|
|
55
|
-
<S.Dropdown isSubmenu={hasSubmenu} isSubmenuOpen={
|
|
63
|
+
<S.Dropdown isSubmenu={hasSubmenu} isSubmenuOpen={isSubmenuOpen} className={DropdownClass} type={type}>
|
|
56
64
|
<S.Heading onClick={handleClick}>
|
|
57
65
|
<div>{name}</div>
|
|
58
66
|
</S.Heading>
|
|
59
|
-
{routesGroups
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
</React.Fragment>
|
|
97
|
-
))}
|
|
67
|
+
{routesGroups?.map((subGroup: IRouteGroup, groupI: number) => (
|
|
68
|
+
<React.Fragment key={`${subGroup.name}-${groupI}`}>
|
|
69
|
+
{subGroup.routes && (
|
|
70
|
+
<>
|
|
71
|
+
{subGroup.routes.map((subLink: ISubRoute, subI: number) => {
|
|
72
|
+
const goToSubLink = () => {
|
|
73
|
+
if (location && location.pathname !== subLink.path) {
|
|
74
|
+
const isEditor = !!subLink.isEditor;
|
|
75
|
+
setHistoryPush(subLink.path, isEditor);
|
|
76
|
+
}
|
|
77
|
+
if (subLink.setDataAction && subLink.item) {
|
|
78
|
+
subLink.setDataAction(subLink.item.id);
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const isSelectedSubItem = location ? location.pathname === subLink.path : false;
|
|
83
|
+
|
|
84
|
+
const navSubItem = (
|
|
85
|
+
<NavSubItem key={`${subLink.path}-${subI}`} active={isSelectedSubItem} type={type}>
|
|
86
|
+
<S.NavLink onClick={goToSubLink}>
|
|
87
|
+
<S.LinkName active={isSelected}>{subLink.name}</S.LinkName>
|
|
88
|
+
</S.NavLink>
|
|
89
|
+
</NavSubItem>
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
return subLink.permission ? (
|
|
93
|
+
<Restricted to={subLink.permission} key={`${subLink.path}-${subI}`}>
|
|
94
|
+
{navSubItem}
|
|
95
|
+
</Restricted>
|
|
96
|
+
) : (
|
|
97
|
+
navSubItem
|
|
98
|
+
);
|
|
99
|
+
})}
|
|
100
|
+
</>
|
|
101
|
+
)}
|
|
102
|
+
</React.Fragment>
|
|
103
|
+
))}
|
|
98
104
|
</S.Dropdown>
|
|
99
105
|
</S.Item>
|
|
100
106
|
);
|
|
101
107
|
};
|
|
102
108
|
|
|
109
|
+
interface ISubRoute {
|
|
110
|
+
path: string;
|
|
111
|
+
component?: any;
|
|
112
|
+
name: string;
|
|
113
|
+
permission?: string;
|
|
114
|
+
isEditor?: boolean;
|
|
115
|
+
setDataAction?: (id: string) => void;
|
|
116
|
+
item?: { id: string };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
interface IRouteGroup {
|
|
120
|
+
name: string;
|
|
121
|
+
routes: ISubRoute[];
|
|
122
|
+
}
|
|
123
|
+
|
|
103
124
|
interface IProps {
|
|
104
125
|
route: IRouter;
|
|
105
|
-
location?:
|
|
126
|
+
location?: Location;
|
|
106
127
|
type: string;
|
|
107
128
|
isOpened: boolean;
|
|
108
129
|
setHistoryPush(path: string, isEditor?: boolean): void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import styled from "styled-components";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const Heading = styled.li`
|
|
4
4
|
${(p) => p.theme.textStyle.headingXXS};
|
|
5
5
|
width: 100%;
|
|
6
6
|
height: 100%;
|
|
@@ -8,7 +8,7 @@ export const Heading = styled.li`
|
|
|
8
8
|
align-items: center;
|
|
9
9
|
`;
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
const Dropdown = styled.ul<{ isSubmenu?: boolean; isSubmenuOpen: boolean; type: string }>`
|
|
12
12
|
background-color: ${(p) =>
|
|
13
13
|
p.type === "multisite" ? p.theme.color.uiMainMenuBackgroundDark : p.theme.color.uiSiteMenuDark};
|
|
14
14
|
color: ${(p) => p.theme.color.textHighEmphasisInverse};
|
|
@@ -54,7 +54,7 @@ export const Dropdown = styled.ul<{ isSubmenu?: boolean; isSubmenuOpen: boolean;
|
|
|
54
54
|
}
|
|
55
55
|
`;
|
|
56
56
|
|
|
57
|
-
|
|
57
|
+
const Arrow = styled.div`
|
|
58
58
|
margin-left: auto;
|
|
59
59
|
height: ${(p) => p.theme.spacing.m};
|
|
60
60
|
width: ${(p) => p.theme.spacing.m};
|
|
@@ -64,13 +64,13 @@ export const Arrow = styled.div`
|
|
|
64
64
|
}
|
|
65
65
|
`;
|
|
66
66
|
|
|
67
|
-
|
|
67
|
+
const LinkName = styled.span<{ active: boolean }>`
|
|
68
68
|
white-space: nowrap;
|
|
69
69
|
overflow: hidden;
|
|
70
70
|
color: ${(p) => (p.active ? p.theme.color.textHighEmphasisInverse : p.theme.color.textMediumEmphasisInverse)};
|
|
71
71
|
`;
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
const Item = styled.li<{ type: string; isOpened: boolean; isSubmenuOpen: boolean; active: boolean }>`
|
|
74
74
|
position: relative;
|
|
75
75
|
margin-left: ${(p) => (p.isSubmenuOpen ? "0" : p.theme.spacing.s)};
|
|
76
76
|
margin-right: ${(p) => (p.isOpened && !p.isSubmenuOpen ? p.theme.spacing.s : "0")};
|
|
@@ -155,11 +155,16 @@ export const Item = styled.li<{ type: string; isOpened: boolean; isSubmenuOpen:
|
|
|
155
155
|
}
|
|
156
156
|
`;
|
|
157
157
|
|
|
158
|
-
|
|
158
|
+
const NavLink = styled.a`
|
|
159
159
|
display: flex;
|
|
160
|
+
|
|
161
|
+
&.withImage {
|
|
162
|
+
padding: 2px;
|
|
163
|
+
margin-bottom: ${(p) => p.theme.spacing.s};
|
|
164
|
+
}
|
|
160
165
|
`;
|
|
161
166
|
|
|
162
|
-
|
|
167
|
+
const Icon = styled.div`
|
|
163
168
|
display: flex;
|
|
164
169
|
margin-right: ${(p) => p.theme.spacing.s};
|
|
165
170
|
height: ${(p) => p.theme.spacing.m};
|
|
@@ -169,3 +174,12 @@ export const Icon = styled.div`
|
|
|
169
174
|
fill: ${(p) => p.theme.color.textMediumEmphasisInverse};
|
|
170
175
|
}
|
|
171
176
|
`;
|
|
177
|
+
|
|
178
|
+
const AvatarWithBorder = styled.div`
|
|
179
|
+
display: flex;
|
|
180
|
+
border: 2px solid;
|
|
181
|
+
border-color: ${(p) => p.theme.color.textHighEmphasisInverse};
|
|
182
|
+
border-radius: 50%;
|
|
183
|
+
`;
|
|
184
|
+
|
|
185
|
+
export { Heading, Dropdown, Arrow, LinkName, Item, NavLink, Icon, AvatarWithBorder };
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useState } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
|
-
import { RouteComponentProps, withRouter } from "react-router-dom";
|
|
4
|
-
import { version } from "./../../../../../package.json";
|
|
5
|
-
|
|
6
|
-
import { IRouter, multisite, site } from "@ax/routes";
|
|
3
|
+
import { type RouteComponentProps, withRouter } from "react-router-dom";
|
|
7
4
|
|
|
8
|
-
import { ILanguage, IRootState, ISite, IStructuredData } from "@ax/types";
|
|
9
|
-
import { IGlobalSettings } from "@ax/containers/App/reducer";
|
|
10
|
-
import { appActions } from "@ax/containers/App";
|
|
11
5
|
import { Icon, Tag } from "@ax/components";
|
|
6
|
+
import { appActions } from "@ax/containers/App";
|
|
7
|
+
import type { IGlobalSettings } from "@ax/containers/App/reducer";
|
|
12
8
|
import { Restricted } from "@ax/guards";
|
|
9
|
+
import { type IRouter, multisite, site } from "@ax/routes";
|
|
10
|
+
import type { ILanguage, IRootState, ISite, IStructuredData, IUser } from "@ax/types";
|
|
13
11
|
|
|
14
|
-
import
|
|
12
|
+
import { version } from "./../../../../../package.json";
|
|
15
13
|
import { NavProvider } from "./context";
|
|
14
|
+
import NavItem from "./NavItem";
|
|
16
15
|
|
|
17
16
|
import * as S from "./style";
|
|
18
17
|
|
|
@@ -20,13 +19,14 @@ const NavMenu = (props: IProps) => {
|
|
|
20
19
|
const {
|
|
21
20
|
location,
|
|
22
21
|
setHistoryPush,
|
|
23
|
-
|
|
22
|
+
logoutAndNavigate,
|
|
24
23
|
currentSiteInfo,
|
|
25
24
|
siteLanguages,
|
|
26
25
|
lang,
|
|
27
26
|
categories,
|
|
28
27
|
globalSettings,
|
|
29
28
|
structuredData,
|
|
29
|
+
currentUser,
|
|
30
30
|
} = props;
|
|
31
31
|
|
|
32
32
|
const { useForms } = globalSettings;
|
|
@@ -48,9 +48,8 @@ const NavMenu = (props: IProps) => {
|
|
|
48
48
|
setIsOpened(!isOpened);
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
const siteLogo = currentSiteInfo
|
|
52
|
-
const siteLogoMini =
|
|
53
|
-
currentSiteInfo && currentSiteInfo.smallAvatar ? currentSiteInfo.smallAvatar : logoMiniPlaceholder;
|
|
51
|
+
const siteLogo = currentSiteInfo?.bigAvatar ? currentSiteInfo.bigAvatar : logoPlaceholder;
|
|
52
|
+
const siteLogoMini = currentSiteInfo?.smallAvatar ? currentSiteInfo.smallAvatar : logoMiniPlaceholder;
|
|
54
53
|
|
|
55
54
|
const sitesPath = "/sites/";
|
|
56
55
|
const isSite =
|
|
@@ -59,7 +58,7 @@ const NavMenu = (props: IProps) => {
|
|
|
59
58
|
|
|
60
59
|
const goToPublishedSite = () => {
|
|
61
60
|
const language = siteLanguages.find((l) => l.id === lang.id);
|
|
62
|
-
if (language
|
|
61
|
+
if (language?.home) {
|
|
63
62
|
const urlHome = `${language.home}${language.home.endsWith("/") ? "" : "/"}`;
|
|
64
63
|
window.open(urlHome, "_blank");
|
|
65
64
|
}
|
|
@@ -74,10 +73,10 @@ const NavMenu = (props: IProps) => {
|
|
|
74
73
|
setHistoryPush(profileRoute);
|
|
75
74
|
};
|
|
76
75
|
|
|
77
|
-
const
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
);
|
|
76
|
+
const filterRoutes = (routes: IRouter[]) => routes.filter((route) => useForms || !route.path.includes("/forms"));
|
|
77
|
+
|
|
78
|
+
const filteredSiteRoutes = filterRoutes(site);
|
|
79
|
+
const filteredMultisiteRoutes = filterRoutes(multisite);
|
|
81
80
|
|
|
82
81
|
const config: IConfig = isSite
|
|
83
82
|
? {
|
|
@@ -114,7 +113,7 @@ const NavMenu = (props: IProps) => {
|
|
|
114
113
|
name: "Logout",
|
|
115
114
|
icon: "Power",
|
|
116
115
|
path: "",
|
|
117
|
-
onClick:
|
|
116
|
+
onClick: logoutAndNavigate,
|
|
118
117
|
};
|
|
119
118
|
|
|
120
119
|
const profileRoute = {
|
|
@@ -123,6 +122,7 @@ const NavMenu = (props: IProps) => {
|
|
|
123
122
|
icon: "User",
|
|
124
123
|
path: isSite ? "/site/profile" : "/profile",
|
|
125
124
|
onClick: goToProfile,
|
|
125
|
+
avatar: currentUser ? { image: currentUser.image?.url, name: currentUser.name } : undefined,
|
|
126
126
|
};
|
|
127
127
|
|
|
128
128
|
const toggleIcon = isOpened ? "Collapsed" : "Extend";
|
|
@@ -140,15 +140,21 @@ const NavMenu = (props: IProps) => {
|
|
|
140
140
|
|
|
141
141
|
if (isSite) {
|
|
142
142
|
const isSiteCategoriesAvailable = !!categories.site.length;
|
|
143
|
-
|
|
144
|
-
|
|
143
|
+
config.routes = config.routes.map((route) =>
|
|
144
|
+
route.path === "/sites/categories" ? { ...route, showInNav: isSiteCategoriesAvailable } : route,
|
|
145
|
+
);
|
|
145
146
|
} else {
|
|
146
147
|
const isGlobalCategoriesAvailable = !!categories.global.length;
|
|
147
148
|
const isGlobalDataAvailable = !!structuredData.global.length;
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
config.routes = config.routes.map((route) => {
|
|
150
|
+
if (route.path === "/categories") {
|
|
151
|
+
return { ...route, showInNav: isGlobalCategoriesAvailable };
|
|
152
|
+
}
|
|
153
|
+
if (route.path === "/data") {
|
|
154
|
+
return { ...route, showInNav: isGlobalDataAvailable };
|
|
155
|
+
}
|
|
156
|
+
return route;
|
|
157
|
+
});
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
return (
|
|
@@ -165,37 +171,34 @@ const NavMenu = (props: IProps) => {
|
|
|
165
171
|
</S.Home>
|
|
166
172
|
<S.Lists>
|
|
167
173
|
<S.List>
|
|
168
|
-
{config.routes
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
})}
|
|
174
|
+
{config.routes
|
|
175
|
+
?.filter((route: IRouter) => route.showInNav)
|
|
176
|
+
.map((route: IRouter): JSX.Element => {
|
|
177
|
+
const navItem = (
|
|
178
|
+
<NavItem
|
|
179
|
+
setHistoryPush={setHistoryPush}
|
|
180
|
+
key={route.name}
|
|
181
|
+
route={route}
|
|
182
|
+
location={location}
|
|
183
|
+
type={config.type}
|
|
184
|
+
isOpened={isOpened}
|
|
185
|
+
/>
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
return route.permission ? (
|
|
189
|
+
<Restricted key={route.name} to={route.permission}>
|
|
190
|
+
{navItem}
|
|
191
|
+
</Restricted>
|
|
192
|
+
) : (
|
|
193
|
+
navItem
|
|
194
|
+
);
|
|
195
|
+
})}
|
|
191
196
|
</S.List>
|
|
192
197
|
<S.List>
|
|
193
198
|
{isSitePublished && (
|
|
194
|
-
|
|
195
|
-
<NavItem setHistoryPush={setHistoryPush} route={siteRoute} type={config.type} isOpened={isOpened} />
|
|
196
|
-
<S.Separator />
|
|
197
|
-
</>
|
|
199
|
+
<NavItem setHistoryPush={setHistoryPush} route={siteRoute} type={config.type} isOpened={isOpened} />
|
|
198
200
|
)}
|
|
201
|
+
<S.Separator />
|
|
199
202
|
<NavItem
|
|
200
203
|
setHistoryPush={setHistoryPush}
|
|
201
204
|
route={profileRoute}
|
|
@@ -214,7 +217,7 @@ const NavMenu = (props: IProps) => {
|
|
|
214
217
|
};
|
|
215
218
|
|
|
216
219
|
interface IDispatchProps {
|
|
217
|
-
|
|
220
|
+
logoutAndNavigate(): void;
|
|
218
221
|
setHistoryPush(path: string): void;
|
|
219
222
|
}
|
|
220
223
|
|
|
@@ -237,6 +240,7 @@ interface INavMenuProps {
|
|
|
237
240
|
siteLanguages: ILanguage[];
|
|
238
241
|
lang: { locale: string; id: number };
|
|
239
242
|
globalSettings: IGlobalSettings;
|
|
243
|
+
currentUser: IUser | null;
|
|
240
244
|
}
|
|
241
245
|
|
|
242
246
|
const mapStateToProps = (state: IRootState) => ({
|
|
@@ -246,12 +250,13 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
246
250
|
siteLanguages: state.sites.currentSiteLanguages,
|
|
247
251
|
lang: state.app.lang,
|
|
248
252
|
globalSettings: state.app.globalSettings,
|
|
253
|
+
currentUser: state.users.currentUser,
|
|
249
254
|
});
|
|
250
255
|
|
|
251
256
|
type IProps = INavMenuProps & IDispatchProps & RouteComponentProps;
|
|
252
257
|
|
|
253
258
|
const mapDispatchToProps = {
|
|
254
|
-
|
|
259
|
+
logoutAndNavigate: appActions.logoutAndNavigate,
|
|
255
260
|
setHistoryPush: appActions.setHistoryPush,
|
|
256
261
|
};
|
|
257
262
|
|