@griddo/ax 11.14.2-rc.0 → 11.14.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/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 +173 -0
- package/src/__tests__/components/KeywordsPreviewModal/KeywordsPreviewModal.test.tsx +3 -4
- 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 +11 -7
- package/src/components/ElementsTooltip/index.tsx +96 -34
- package/src/components/ElementsTooltip/style.tsx +12 -1
- package/src/components/Fields/FileField/index.tsx +16 -18
- 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/KeywordsPreviewModal/atoms.tsx +2 -2
- package/src/components/KeywordsPreviewModal/index.tsx +6 -6
- package/src/components/KeywordsPreviewModal/utils.tsx +2 -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/index.tsx +2 -1
- package/src/hooks/modals.tsx +4 -3
- package/src/hooks/window.ts +50 -2
- 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
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { IconAction } from "@ax/components";
|
|
2
|
+
import type { IQueryValue, ISiteListConfig } from "@ax/types";
|
|
3
|
+
|
|
4
|
+
import GridHeaderFilter from "../GridView/GridHeaderFilter";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const AllSitesHeader = ({ displayMode, sortItems, sortedListStatus, changeDisplayMode }: IAllSitesHeaderProps) => (
|
|
9
|
+
<S.SectionHeader data-testid="all-sites-header">
|
|
10
|
+
<S.Title isActive={true} data-testid="all-sites-title">
|
|
11
|
+
All sites
|
|
12
|
+
</S.Title>
|
|
13
|
+
<S.HeaderIconsWrapper data-testid="all-sites-header-icons">
|
|
14
|
+
{displayMode === "grid" ? (
|
|
15
|
+
<S.FilterSelect data-testid="all-sites-grid-filter">
|
|
16
|
+
<S.FilterSelectLabel data-testid="all-sites-grid-filter-label">Sort by:</S.FilterSelectLabel>{" "}
|
|
17
|
+
<GridHeaderFilter sortItems={sortItems} sortedState={sortedListStatus} />{" "}
|
|
18
|
+
</S.FilterSelect>
|
|
19
|
+
) : null}
|
|
20
|
+
<IconAction icon="Grid2" onClick={() => changeDisplayMode("grid")} active={displayMode === "grid"} />
|
|
21
|
+
<IconAction icon="BulletList" onClick={() => changeDisplayMode("list")} active={displayMode === "list"} />
|
|
22
|
+
</S.HeaderIconsWrapper>
|
|
23
|
+
</S.SectionHeader>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export interface IAllSitesHeaderProps {
|
|
27
|
+
displayMode: "list" | "grid";
|
|
28
|
+
sortItems: (orderPointer: IQueryValue[], isAscending: boolean) => void;
|
|
29
|
+
sortedListStatus: ISiteListConfig["sortedListStatus"];
|
|
30
|
+
changeDisplayMode: (mode: "list" | "grid") => void;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default AllSitesHeader;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
|
|
3
|
+
const SectionHeader = styled.div`
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
margin: 0 ${(p) => p.theme.spacing.m};
|
|
7
|
+
margin-top: ${(p) => p.theme.spacing.m};
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const Title = styled.h1<{ isActive?: boolean }>`
|
|
11
|
+
margin: 0;
|
|
12
|
+
margin-right: ${(p) => p.theme.spacing.m};
|
|
13
|
+
${(p) => p.theme.textStyle.headingM};
|
|
14
|
+
color: ${(p) => (p.isActive ? p.theme.colors.textHighEmphasis : p.theme.colors.textMediumEmphasis)};
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const HeaderIconsWrapper = styled.div`
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
`;
|
|
21
|
+
|
|
22
|
+
const FilterSelectLabel = styled.span`
|
|
23
|
+
color: ${(p) => p.theme.color.interactive01};
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
const FilterSelect = styled.div`
|
|
27
|
+
${(p) => p.theme.textStyle.uiS};
|
|
28
|
+
line-height: 32px;
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: center;
|
|
31
|
+
position: relative;
|
|
32
|
+
color: ${(p) => p.theme.color.textLowEmphasis};
|
|
33
|
+
`;
|
|
34
|
+
|
|
35
|
+
export { SectionHeader, Title, HeaderIconsWrapper, FilterSelect, FilterSelectLabel };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { FloatingMenu, Icon, ListItem, ListTitle } from "@ax/components";
|
|
2
|
+
import type { IQueryValue } from "@ax/types";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import { IQueryValue } from "@ax/types";
|
|
4
|
+
import type { ISortedListStatus } from "../../utils";
|
|
5
5
|
|
|
6
6
|
import * as S from "./style";
|
|
7
7
|
|
|
@@ -66,8 +66,8 @@ const GridHeaderFilter = ({ sortItems, sortedState }: IGridHeaderFilterProps): J
|
|
|
66
66
|
};
|
|
67
67
|
|
|
68
68
|
export interface IGridHeaderFilterProps {
|
|
69
|
-
sortedState:
|
|
70
|
-
sortItems(orderPointer: IQueryValue[], isAscending: boolean):
|
|
69
|
+
sortedState: ISortedListStatus;
|
|
70
|
+
sortItems(orderPointer: IQueryValue[], isAscending: boolean): void;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
export default GridHeaderFilter;
|
|
@@ -1,132 +1,36 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
1
|
import { connect } from "react-redux";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import { isDevelopment } from "@ax/helpers";
|
|
6
|
-
import { IGetSitesParams, ISite } from "@ax/types";
|
|
3
|
+
import { FieldsBehavior, Icon, Modal, Tooltip } from "@ax/components";
|
|
7
4
|
import { appActions } from "@ax/containers/App";
|
|
8
5
|
import { sitesActions } from "@ax/containers/Sites";
|
|
9
|
-
import {
|
|
6
|
+
import { isDevelopment } from "@ax/helpers";
|
|
7
|
+
import type { IGetSitesParams, ISite } from "@ax/types";
|
|
10
8
|
|
|
11
9
|
import { ItemName, ItemThumbnail } from "./../../atoms";
|
|
10
|
+
import { useSiteActions } from "./../../hooks";
|
|
12
11
|
|
|
13
12
|
import * as S from "./style";
|
|
14
13
|
|
|
15
14
|
const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
16
15
|
const { site, setSiteInfo, setHistoryPush, deleteSite, publishSite, unpublishSite, getParams } = props;
|
|
17
|
-
const { isOpen: isOpenDelete, toggleModal: toggleDeleteModal } = useModal();
|
|
18
|
-
const { isOpen: isOpenPublish, toggleModal: togglePublishModal } = useModal();
|
|
19
|
-
const [inputValue, setInputValue] = useState("");
|
|
20
|
-
const { updated, isPublished } = site;
|
|
21
|
-
|
|
22
|
-
const allowedToDeleteSite = usePermission("general.deleteSite");
|
|
23
|
-
const allowedToPublishSite = usePermission("general.publishSite");
|
|
24
|
-
const allowedToUnpublishSite = usePermission("general.unpublishSite");
|
|
25
|
-
|
|
26
|
-
const getPublishedState = (isPublished: boolean, updated: boolean) => {
|
|
27
|
-
switch (isPublished) {
|
|
28
|
-
case true:
|
|
29
|
-
return updated ? "active" : "upload-pending";
|
|
30
|
-
case false:
|
|
31
|
-
return updated ? "offline" : "offline-pending";
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
const publishedState = getPublishedState(isPublished, updated);
|
|
36
|
-
|
|
37
|
-
const publishedTooltip = {
|
|
38
|
-
active: "Live",
|
|
39
|
-
"upload-pending": "Publication pending",
|
|
40
|
-
offline: "Offline",
|
|
41
|
-
"offline-pending": "Offline pending",
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const setSite = async () => {
|
|
45
|
-
await setSiteInfo(site);
|
|
46
|
-
const contentPath = "/sites/pages";
|
|
47
|
-
setHistoryPush(contentPath, false);
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
const getPublishOption = (isPublished: boolean) =>
|
|
51
|
-
isPublished && allowedToUnpublishSite
|
|
52
|
-
? { label: "Unpublish", icon: "offlinePending" }
|
|
53
|
-
: !isPublished && allowedToPublishSite
|
|
54
|
-
? { label: "Publish", icon: "uploadPending" }
|
|
55
|
-
: null;
|
|
56
|
-
|
|
57
|
-
const publishOptionProps = getPublishOption(isPublished);
|
|
58
|
-
|
|
59
|
-
const deleteOption = allowedToDeleteSite
|
|
60
|
-
? {
|
|
61
|
-
label: "delete",
|
|
62
|
-
icon: "delete",
|
|
63
|
-
action: toggleDeleteModal,
|
|
64
|
-
}
|
|
65
|
-
: undefined;
|
|
66
|
-
|
|
67
|
-
const publishOption = publishOptionProps
|
|
68
|
-
? {
|
|
69
|
-
label: publishOptionProps.label,
|
|
70
|
-
icon: publishOptionProps.icon,
|
|
71
|
-
action: togglePublishModal,
|
|
72
|
-
}
|
|
73
|
-
: undefined;
|
|
74
|
-
|
|
75
|
-
const menuOptions = [deleteOption, publishOption];
|
|
76
|
-
|
|
77
|
-
const handleDeleteSite = async () => {
|
|
78
|
-
const params = getParams();
|
|
79
|
-
await deleteSite(site.id, params);
|
|
80
|
-
toggleDeleteModal();
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const mainDeleteAction = {
|
|
84
|
-
title: "Delete Site",
|
|
85
|
-
onClick: handleDeleteSite,
|
|
86
|
-
disabled: inputValue !== site.name.toUpperCase(),
|
|
87
|
-
};
|
|
88
|
-
const secondaryDeleteAction = { title: "Cancel", onClick: toggleDeleteModal };
|
|
89
|
-
|
|
90
|
-
const handlePublishSite = async () => {
|
|
91
|
-
const params = getParams();
|
|
92
|
-
await publishSite(site.id, params);
|
|
93
|
-
togglePublishModal();
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
const handleUnpublishSite = async () => {
|
|
97
|
-
const params = getParams();
|
|
98
|
-
await unpublishSite(site.id, params);
|
|
99
|
-
togglePublishModal();
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
const getPublishModal = (isPublished: boolean) =>
|
|
103
|
-
isPublished
|
|
104
|
-
? {
|
|
105
|
-
mainAction: { title: "Unpublish Site", onClick: handleUnpublishSite },
|
|
106
|
-
secondaryAction: { title: "Cancel", onClick: togglePublishModal },
|
|
107
|
-
title: "Unpublish Site",
|
|
108
|
-
content: (
|
|
109
|
-
<p>
|
|
110
|
-
You are going to unpublish <strong>{site.name}</strong> site.
|
|
111
|
-
<br />
|
|
112
|
-
This action can take several minutes.
|
|
113
|
-
</p>
|
|
114
|
-
),
|
|
115
|
-
}
|
|
116
|
-
: {
|
|
117
|
-
mainAction: { title: "Publish Site", onClick: handlePublishSite },
|
|
118
|
-
secondaryAction: { title: "Cancel", onClick: togglePublishModal },
|
|
119
|
-
title: "Publish Site",
|
|
120
|
-
content: (
|
|
121
|
-
<p>
|
|
122
|
-
You are going to publish <strong>{site.name}</strong> site.
|
|
123
|
-
<br />
|
|
124
|
-
Make sure everything is ok before you do it.
|
|
125
|
-
</p>
|
|
126
|
-
),
|
|
127
|
-
};
|
|
128
16
|
|
|
129
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
publishedState,
|
|
19
|
+
publishedTooltip,
|
|
20
|
+
setSite,
|
|
21
|
+
menuOptions,
|
|
22
|
+
inputValue,
|
|
23
|
+
setInputValue,
|
|
24
|
+
isOpenDelete,
|
|
25
|
+
toggleDeleteModal,
|
|
26
|
+
mainDeleteAction,
|
|
27
|
+
secondaryDeleteAction,
|
|
28
|
+
isOpenPublish,
|
|
29
|
+
togglePublishModal,
|
|
30
|
+
publishModal,
|
|
31
|
+
} = useSiteActions({ site, setSiteInfo, setHistoryPush, deleteSite, publishSite, unpublishSite, getParams });
|
|
32
|
+
|
|
33
|
+
const { title, mainAction, secondaryAction, content } = publishModal;
|
|
130
34
|
|
|
131
35
|
const siteTooltip = isDevelopment() && `Site id: ${site.id}`;
|
|
132
36
|
|
|
@@ -134,7 +38,7 @@ const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
|
134
38
|
<>
|
|
135
39
|
<S.SiteWrapper key={site.id} onClick={setSite} data-testid="grid-site-item">
|
|
136
40
|
<Tooltip content={siteTooltip}>
|
|
137
|
-
<ItemThumbnail src={site.thumbnail} width={
|
|
41
|
+
<ItemThumbnail src={site.thumbnail} width={290} height={199} />
|
|
138
42
|
</Tooltip>
|
|
139
43
|
<S.Wrapper>
|
|
140
44
|
<S.Title>
|
|
@@ -158,7 +62,7 @@ const GridSiteItem = (props: IGridSiteItemProps): JSX.Element => {
|
|
|
158
62
|
{isOpenDelete ? (
|
|
159
63
|
<S.ModalContent data-testid="delete-modal">
|
|
160
64
|
<p>
|
|
161
|
-
Are you sure you want to delete <strong
|
|
65
|
+
Are you sure you want to delete <strong>'{site.name}'</strong> site?
|
|
162
66
|
<br />
|
|
163
67
|
This action <strong>cannot be undone</strong>.
|
|
164
68
|
</p>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { CheckField, LastAccessFilter, LiveFilter, NameFilter, TableCounter, Tooltip } from "@ax/components";
|
|
2
|
+
import type { IQueryValue } from "@ax/types";
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
-
import { IQueryValue } from "@ax/types";
|
|
4
|
+
import type { ISortedListStatus } from "../../../utils";
|
|
5
5
|
|
|
6
6
|
import * as S from "./style";
|
|
7
7
|
|
|
@@ -66,7 +66,7 @@ interface IProps {
|
|
|
66
66
|
checkState: Record<string, boolean>;
|
|
67
67
|
sortItems: (orderPointer: IQueryValue[], isAscending: boolean) => void;
|
|
68
68
|
filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
|
|
69
|
-
sortedListStatus:
|
|
69
|
+
sortedListStatus: ISortedListStatus;
|
|
70
70
|
filterValues: Record<string, IQueryValue[]>;
|
|
71
71
|
setHoverCheck: (state: boolean) => void;
|
|
72
72
|
}
|
|
@@ -4,6 +4,7 @@ import type { IBulkSelectedItems } from "@ax/hooks";
|
|
|
4
4
|
import { usePermission } from "@ax/hooks";
|
|
5
5
|
import type { IQueryValue } from "@ax/types";
|
|
6
6
|
|
|
7
|
+
import type { ISortedListStatus } from "../../utils";
|
|
7
8
|
import TableHeader from "./TableHeader";
|
|
8
9
|
|
|
9
10
|
import * as S from "./style";
|
|
@@ -77,15 +78,15 @@ const BulkHeader = (props: IBulkHeaderProps): JSX.Element => {
|
|
|
77
78
|
|
|
78
79
|
export interface IBulkHeaderProps {
|
|
79
80
|
showBulk: boolean;
|
|
80
|
-
checkState:
|
|
81
|
+
checkState: Record<string, boolean>;
|
|
81
82
|
bulkPublish: () => void;
|
|
82
83
|
bulkUnpublish: () => void;
|
|
83
84
|
selectItems: () => void;
|
|
84
85
|
selectAllItems: () => void;
|
|
85
86
|
totalItems: number;
|
|
86
|
-
sortItems: (orderPointer: IQueryValue[], isAscending:
|
|
87
|
+
sortItems: (orderPointer: IQueryValue[], isAscending: boolean) => void;
|
|
87
88
|
filterItems: (filterPointer: string, filtersSelected: IQueryValue[]) => void;
|
|
88
|
-
sortedListStatus:
|
|
89
|
+
sortedListStatus: ISortedListStatus;
|
|
89
90
|
filterValues: Record<string, IQueryValue[]>;
|
|
90
91
|
setHoverCheck: (state: boolean) => void;
|
|
91
92
|
selectedItems: IBulkSelectedItems;
|
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import React, { useState } from "react";
|
|
2
1
|
import { connect } from "react-redux";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import { getFormattedDateWithTimezone } from "@ax/helpers";
|
|
6
|
-
import { ICheck, IGetSitesParams, ISite } from "@ax/types";
|
|
3
|
+
import { CheckField, FieldsBehavior, Icon, Modal, Tooltip } from "@ax/components";
|
|
7
4
|
import { appActions } from "@ax/containers/App";
|
|
8
5
|
import { sitesActions } from "@ax/containers/Sites";
|
|
9
|
-
import {
|
|
6
|
+
import { getFormattedDateWithTimezone } from "@ax/helpers";
|
|
7
|
+
import type { ICheck, IGetSitesParams, ISite } from "@ax/types";
|
|
10
8
|
|
|
11
9
|
import { ItemName, ItemThumbnail } from "./../../atoms";
|
|
10
|
+
import { useSiteActions } from "./../../hooks";
|
|
12
11
|
|
|
13
12
|
import * as S from "./style";
|
|
14
13
|
|
|
@@ -26,119 +25,23 @@ const ListSiteItem = (props: IListSiteItemProps): JSX.Element => {
|
|
|
26
25
|
hoverCheck,
|
|
27
26
|
} = props;
|
|
28
27
|
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
const publishedState = getPublishedState(isPublished, updated);
|
|
48
|
-
|
|
49
|
-
const publishedTooltip = {
|
|
50
|
-
active: "Live",
|
|
51
|
-
"upload-pending": "Publication pending",
|
|
52
|
-
offline: "Offline",
|
|
53
|
-
"offline-pending": "Offline pending",
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
const setSite = async () => {
|
|
57
|
-
await setSiteInfo(site);
|
|
58
|
-
const contentPath = "/sites/pages";
|
|
59
|
-
setHistoryPush(contentPath, false);
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
const getPublishOption = (isPublished: boolean) =>
|
|
63
|
-
isPublished && allowedToUnpublishSite
|
|
64
|
-
? { label: "Unpublish", icon: "offlinePending" }
|
|
65
|
-
: allowedToPublishSite
|
|
66
|
-
? { label: "Publish", icon: "uploadPending" }
|
|
67
|
-
: null;
|
|
68
|
-
|
|
69
|
-
const publishOptionProps = getPublishOption(isPublished);
|
|
70
|
-
|
|
71
|
-
const deleteOption = allowedToDeleteSite
|
|
72
|
-
? {
|
|
73
|
-
label: "delete",
|
|
74
|
-
icon: "delete",
|
|
75
|
-
action: toggleDeleteModal,
|
|
76
|
-
}
|
|
77
|
-
: undefined;
|
|
78
|
-
|
|
79
|
-
const publishOption = publishOptionProps
|
|
80
|
-
? {
|
|
81
|
-
label: publishOptionProps.label,
|
|
82
|
-
icon: publishOptionProps.icon,
|
|
83
|
-
action: togglePublishModal,
|
|
84
|
-
}
|
|
85
|
-
: undefined;
|
|
86
|
-
|
|
87
|
-
const menuOptions = [deleteOption, publishOption];
|
|
88
|
-
|
|
89
|
-
const handleDeleteSite = async () => {
|
|
90
|
-
const params = getParams();
|
|
91
|
-
await deleteSite(site.id, params);
|
|
92
|
-
toggleDeleteModal();
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const mainDeleteAction = {
|
|
96
|
-
title: "Delete Site",
|
|
97
|
-
onClick: handleDeleteSite,
|
|
98
|
-
disabled: inputValue !== site.name.toUpperCase(),
|
|
99
|
-
};
|
|
100
|
-
const secondaryDeleteAction = { title: "Cancel", onClick: toggleDeleteModal };
|
|
101
|
-
|
|
102
|
-
const handlePublishSite = async () => {
|
|
103
|
-
const params = getParams();
|
|
104
|
-
await publishSite(site.id, params);
|
|
105
|
-
togglePublishModal();
|
|
106
|
-
};
|
|
107
|
-
|
|
108
|
-
const handleUnpublishSite = async () => {
|
|
109
|
-
const params = getParams();
|
|
110
|
-
await unpublishSite(site.id, params);
|
|
111
|
-
togglePublishModal();
|
|
112
|
-
};
|
|
113
|
-
|
|
114
|
-
const getPublishModal = (isPublished: boolean) =>
|
|
115
|
-
isPublished
|
|
116
|
-
? {
|
|
117
|
-
mainAction: { title: "Unpublish Site", onClick: handleUnpublishSite },
|
|
118
|
-
secondaryAction: { title: "Cancel", onClick: togglePublishModal },
|
|
119
|
-
title: "Unpublish Site",
|
|
120
|
-
content: (
|
|
121
|
-
<p>
|
|
122
|
-
You are going to unpublish <strong>{site.name}</strong> site.
|
|
123
|
-
<br />
|
|
124
|
-
This action can take several minutes.
|
|
125
|
-
</p>
|
|
126
|
-
),
|
|
127
|
-
}
|
|
128
|
-
: {
|
|
129
|
-
mainAction: { title: "Publish Site", onClick: handlePublishSite },
|
|
130
|
-
secondaryAction: { title: "Cancel", onClick: togglePublishModal },
|
|
131
|
-
title: "Publish Site",
|
|
132
|
-
content: (
|
|
133
|
-
<p>
|
|
134
|
-
You are going to publish <strong>{site.name}</strong> site.
|
|
135
|
-
<br />
|
|
136
|
-
Make sure everything is ok before you do it.
|
|
137
|
-
</p>
|
|
138
|
-
),
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
const { title, mainAction, secondaryAction, content } = getPublishModal(site.isPublished);
|
|
28
|
+
const {
|
|
29
|
+
publishedState,
|
|
30
|
+
publishedTooltip,
|
|
31
|
+
setSite,
|
|
32
|
+
menuOptions,
|
|
33
|
+
inputValue,
|
|
34
|
+
setInputValue,
|
|
35
|
+
isOpenDelete,
|
|
36
|
+
toggleDeleteModal,
|
|
37
|
+
mainDeleteAction,
|
|
38
|
+
secondaryDeleteAction,
|
|
39
|
+
isOpenPublish,
|
|
40
|
+
togglePublishModal,
|
|
41
|
+
publishModal,
|
|
42
|
+
} = useSiteActions({ site, setSiteInfo, setHistoryPush, deleteSite, publishSite, unpublishSite, getParams });
|
|
43
|
+
|
|
44
|
+
const { title, mainAction, secondaryAction, content } = publishModal;
|
|
142
45
|
|
|
143
46
|
const handleOnChange = (value: ICheck) => onCheck(value);
|
|
144
47
|
|
|
@@ -181,7 +84,7 @@ const ListSiteItem = (props: IListSiteItemProps): JSX.Element => {
|
|
|
181
84
|
{isOpenDelete ? (
|
|
182
85
|
<S.ModalContent data-testid="delete-modal">
|
|
183
86
|
<p>
|
|
184
|
-
Are you sure you want to delete <strong
|
|
87
|
+
Are you sure you want to delete <strong>'{site.name}'</strong> site?
|
|
185
88
|
<br />
|
|
186
89
|
This action <strong>cannot be undone</strong>.
|
|
187
90
|
</p>
|
|
@@ -214,7 +117,7 @@ interface IListSiteItemProps {
|
|
|
214
117
|
deleteSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
215
118
|
publishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
216
119
|
unpublishSite(siteID: number, params?: IGetSitesParams): Promise<void>;
|
|
217
|
-
onCheck: (e:
|
|
120
|
+
onCheck: (e: ICheck) => void;
|
|
218
121
|
getParams: () => IGetSitesParams;
|
|
219
122
|
}
|
|
220
123
|
|
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import React from "react";
|
|
2
1
|
import { connect } from "react-redux";
|
|
3
2
|
|
|
4
|
-
import {
|
|
5
|
-
import { ISite } from "@ax/types";
|
|
3
|
+
import { Tooltip } from "@ax/components";
|
|
6
4
|
import { appActions } from "@ax/containers/App";
|
|
7
5
|
import { sitesActions } from "@ax/containers/Sites";
|
|
8
|
-
import {
|
|
6
|
+
import { isDevelopment } from "@ax/helpers";
|
|
7
|
+
import type { ISite } from "@ax/types";
|
|
9
8
|
|
|
10
|
-
import { ItemName, ItemThumbnail } from "
|
|
9
|
+
import { ItemName, ItemThumbnail } from "../../atoms";
|
|
11
10
|
|
|
12
11
|
import * as S from "./style";
|
|
13
12
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { Icon } from "@ax/components";
|
|
2
|
+
import type { ISite } from "@ax/types";
|
|
3
|
+
|
|
4
|
+
import RecentSiteItem from "./RecentSiteItem";
|
|
5
|
+
|
|
6
|
+
import * as S from "./style";
|
|
7
|
+
|
|
8
|
+
const RecentSitesHeader = ({ isRecentSitesListDisplayed, toggleRecentSites }: IRecentSitesHeaderProps) => (
|
|
9
|
+
<S.SectionHeader data-testid="recent-sites-header" isRecentSites={true}>
|
|
10
|
+
<S.Title data-testid="recent-sites-title" isActive={isRecentSitesListDisplayed}>
|
|
11
|
+
Recent sites
|
|
12
|
+
</S.Title>
|
|
13
|
+
<S.CollapseButton data-testid="recent-sites-collapse-button" onClick={toggleRecentSites}>
|
|
14
|
+
{isRecentSitesListDisplayed ? (
|
|
15
|
+
<>
|
|
16
|
+
<S.Label data-testid="recent-sites-hide-label">Hide recent sites </S.Label> <Icon name="UpArrow" />
|
|
17
|
+
</>
|
|
18
|
+
) : (
|
|
19
|
+
<>
|
|
20
|
+
<S.Label data-testid="recent-sites-show-label">Show recent sites </S.Label> <Icon name="DownArrow" />
|
|
21
|
+
</>
|
|
22
|
+
)}
|
|
23
|
+
</S.CollapseButton>
|
|
24
|
+
</S.SectionHeader>
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const RecentSitesList = ({ recentSites, isRecentSitesListDisplayed, toggleRecentSites }: IRecentSitesListProps) => (
|
|
28
|
+
<S.RecentSites data-testid="recent-sites-list" fullWidth={recentSites.length >= 5}>
|
|
29
|
+
<RecentSitesHeader isRecentSitesListDisplayed={isRecentSitesListDisplayed} toggleRecentSites={toggleRecentSites} />
|
|
30
|
+
<S.RecentSitesItemsWrapper data-testid="recent-sites-items-wrapper" isHidden={!isRecentSitesListDisplayed}>
|
|
31
|
+
{recentSites.map((site: ISite) => (
|
|
32
|
+
<RecentSiteItem key={site.id} site={site} />
|
|
33
|
+
))}
|
|
34
|
+
</S.RecentSitesItemsWrapper>
|
|
35
|
+
</S.RecentSites>
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
interface IRecentSitesHeaderProps {
|
|
39
|
+
isRecentSitesListDisplayed: boolean;
|
|
40
|
+
toggleRecentSites: () => void;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface IRecentSitesListProps {
|
|
44
|
+
recentSites: ISite[];
|
|
45
|
+
isRecentSitesListDisplayed: boolean;
|
|
46
|
+
toggleRecentSites: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export default RecentSitesList;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import styled, { css } from "styled-components";
|
|
2
|
+
|
|
3
|
+
const SectionHeader = styled.div<{ isRecentSites?: boolean }>`
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: space-between;
|
|
6
|
+
margin: 0 ${(p) => (p.isRecentSites ? p.theme.spacing.s : p.theme.spacing.m)};
|
|
7
|
+
margin-top: ${(p) => p.theme.spacing.m};
|
|
8
|
+
`;
|
|
9
|
+
|
|
10
|
+
const Title = styled.h1<{ isActive?: boolean }>`
|
|
11
|
+
margin: 0;
|
|
12
|
+
margin-right: ${(p) => p.theme.spacing.m};
|
|
13
|
+
${(p) => p.theme.textStyle.headingM};
|
|
14
|
+
color: ${(p) => (p.isActive ? p.theme.colors.textHighEmphasis : p.theme.colors.textMediumEmphasis)};
|
|
15
|
+
`;
|
|
16
|
+
|
|
17
|
+
const CollapseButton = styled.div`
|
|
18
|
+
display: flex;
|
|
19
|
+
align-items: center;
|
|
20
|
+
${(p) => p.theme.textStyle.uiS};
|
|
21
|
+
color: ${(p) => p.theme.colors.textMediumEmphasis};
|
|
22
|
+
padding: 0;
|
|
23
|
+
margin: 0;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
`;
|
|
26
|
+
|
|
27
|
+
const Label = styled.span`
|
|
28
|
+
margin-right: 14px;
|
|
29
|
+
`;
|
|
30
|
+
|
|
31
|
+
const RecentSites = styled.div<{ fullWidth: boolean }>`
|
|
32
|
+
display: ${(p) => (p.fullWidth ? "block" : "inline-block")};
|
|
33
|
+
padding-bottom: ${(p) => p.theme.spacing.s};
|
|
34
|
+
border-radius: ${(p) => p.theme.radii.s};
|
|
35
|
+
background: ${(p) => p.theme.color.uiBackground02};
|
|
36
|
+
min-height: 56px;
|
|
37
|
+
overflow: hidden;
|
|
38
|
+
margin: ${(p) => p.theme.spacing.m};
|
|
39
|
+
margin-bottom: 0;
|
|
40
|
+
flex-shrink: 0;
|
|
41
|
+
|
|
42
|
+
h1 {
|
|
43
|
+
padding-left: 0;
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
const RecentSitesItemsWrapper = styled.div<{ isHidden: boolean }>`
|
|
48
|
+
display: grid;
|
|
49
|
+
grid-auto-flow: column;
|
|
50
|
+
overflow: auto;
|
|
51
|
+
grid-gap: ${(p) => p.theme.spacing.m};
|
|
52
|
+
padding: ${(p) => p.theme.spacing.s} ${(p) => p.theme.spacing.s} 0px ${(p) => p.theme.spacing.s};
|
|
53
|
+
overflow: hidden;
|
|
54
|
+
|
|
55
|
+
div {
|
|
56
|
+
min-width: 100%;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
${(props) =>
|
|
60
|
+
props.isHidden &&
|
|
61
|
+
css`
|
|
62
|
+
height: 0;
|
|
63
|
+
padding: 0;
|
|
64
|
+
padding-left: ${(p) => p.theme.spacing.s};
|
|
65
|
+
padding-top: ${(p) => p.theme.spacing.xs};
|
|
66
|
+
`};
|
|
67
|
+
|
|
68
|
+
@media (min-width: 1200px) {
|
|
69
|
+
div:nth-child(6),
|
|
70
|
+
div:nth-child(7) {
|
|
71
|
+
display: none;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@media (min-width: 1600px) {
|
|
76
|
+
div:nth-child(6) {
|
|
77
|
+
display: block;
|
|
78
|
+
}
|
|
79
|
+
div:nth-child(7) {
|
|
80
|
+
display: none;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
@media (min-width: 1750px) {
|
|
85
|
+
div:nth-child(6),
|
|
86
|
+
div:nth-child(7) {
|
|
87
|
+
display: block;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
`;
|
|
91
|
+
|
|
92
|
+
export { SectionHeader, Title, CollapseButton, Label, RecentSites, RecentSitesItemsWrapper };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { useEffect } from "react";
|
|
2
2
|
import { connect } from "react-redux";
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import type { ILanguage, IRootState, ISettingsForm } from "@ax/types";
|
|
5
5
|
import { slugify } from "@ax/helpers";
|
|
6
6
|
import { ErrorToast, FieldsBehavior } from "@ax/components";
|
|
7
7
|
|
|
@@ -20,10 +20,11 @@ const SiteModal = (props: ISiteModalProps): JSX.Element => {
|
|
|
20
20
|
const setPathValue = (value: string) => updateForm({ path: value });
|
|
21
21
|
const setDomainValue = (value: number) => updateForm({ domain: value });
|
|
22
22
|
|
|
23
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: TODO: fix this
|
|
24
23
|
useEffect(() => {
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
if (globalDefaultLang) {
|
|
25
|
+
setForm((state: ISettingsForm) => ({ ...state, defaultLanguage: globalDefaultLang.id }));
|
|
26
|
+
}
|
|
27
|
+
}, [globalDefaultLang, setForm]);
|
|
27
28
|
|
|
28
29
|
const slugHelpText =
|
|
29
30
|
"The path is for the site on this language. Please, fill with the name of the site. Example: /the-site-url";
|
|
@@ -84,8 +85,8 @@ const mapStateToProps = (state: IRootState) => ({
|
|
|
84
85
|
|
|
85
86
|
interface IStateProps {
|
|
86
87
|
languages?: ILanguage[];
|
|
87
|
-
form:
|
|
88
|
-
setForm: React.Dispatch<React.SetStateAction<
|
|
88
|
+
form: ISettingsForm;
|
|
89
|
+
setForm: React.Dispatch<React.SetStateAction<ISettingsForm>>;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
92
|
type ISiteModalProps = IStateProps;
|