Skip to content

Commit 51f8e36

Browse files
Add environment management (#1350)
* Add environment management
1 parent ca1fd55 commit 51f8e36

File tree

126 files changed

+2229
-1449
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+2229
-1449
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { useState } from "react";
2+
import { useAdminDispatch } from "../../../../containers/Admin/hooks";
3+
import { setEnvironmentToDelete } from "../../../../redux/slices/environmentsSlice";
4+
import { TrashBinIcon } from "../../../common/icons/16px/TrashBinIcon";
5+
import { ThreeDotsVerticalIcon } from "../../../common/icons/ThreeDotsVerticalIcon";
6+
import { NewPopover } from "../../../common/NewPopover";
7+
import { NewIconButton } from "../../../common/v3/NewIconButton";
8+
import { MenuList } from "../../../Navigation/common/MenuList";
9+
import * as s from "./styles";
10+
import type { ActionMenuButtonProps } from "./types";
11+
12+
export const ActionsMenuButton = ({ environment }: ActionMenuButtonProps) => {
13+
const [isKebabButtonMenuOpen, setIsKebabButtonMenuOpen] = useState(false);
14+
const dispatch = useAdminDispatch();
15+
16+
const handleDeleteMenuItemClick = () => {
17+
dispatch(setEnvironmentToDelete(environment.id));
18+
};
19+
20+
const handleKebabMenuOpenChange = (isOpen: boolean) => {
21+
setIsKebabButtonMenuOpen(isOpen);
22+
};
23+
24+
return (
25+
<NewPopover
26+
content={
27+
<s.Popup>
28+
<MenuList
29+
items={[
30+
{
31+
id: "delete",
32+
icon: <TrashBinIcon />,
33+
label: "Delete",
34+
onClick: handleDeleteMenuItemClick
35+
}
36+
]}
37+
/>
38+
</s.Popup>
39+
}
40+
onOpenChange={handleKebabMenuOpenChange}
41+
isOpen={isKebabButtonMenuOpen}
42+
placement={"bottom-end"}
43+
>
44+
<NewIconButton
45+
icon={ThreeDotsVerticalIcon}
46+
buttonType={"secondaryBorderless"}
47+
/>
48+
</NewPopover>
49+
);
50+
};
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import styled from "styled-components";
2+
import { Popup as CommonPopup } from "../../../Navigation/common/Popup";
3+
import { ContentContainer } from "../../../Navigation/common/Popup/styles";
4+
5+
export const Popup = styled(CommonPopup)`
6+
& > ${ContentContainer} {
7+
padding: 3px 8px;
8+
}
9+
`;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import type { Environment } from "../../../../redux/services/types";
2+
3+
export interface ActionMenuButtonProps {
4+
environment: Environment;
5+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { NewButton } from "../../../../common/v3/NewButton";
2+
import * as s from "../../../../RecentActivity/CreateEnvironmentWizard/FinishStep/styles";
3+
import type { FinishScreenContentProps } from "./types";
4+
5+
export const FinishScreenContent = ({
6+
onGoToDashboard,
7+
environment
8+
}: FinishScreenContentProps) => {
9+
const handleBackToDashboardButtonClick = () => {
10+
onGoToDashboard(environment?.id ?? null);
11+
};
12+
13+
return (
14+
<>
15+
<s.Info>
16+
<s.Title>Environment Created</s.Title>
17+
<s.Description>
18+
Your {(environment?.type ?? "").toLocaleLowerCase()} environment is
19+
successfully created.
20+
</s.Description>
21+
</s.Info>
22+
<NewButton
23+
buttonType={"primary"}
24+
label={"Back to dashboard"}
25+
onClick={handleBackToDashboardButtonClick}
26+
/>
27+
</>
28+
);
29+
};
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import type { Environment } from "../../../../../redux/services/types";
2+
3+
export interface FinishScreenContentProps {
4+
onGoToDashboard: (id: string | null) => void;
5+
environment?: Environment;
6+
}
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { useCallback, useEffect, useState } from "react";
2+
import { useGetAboutQuery } from "../../../../redux/services/digma";
3+
import type { Environment } from "../../../../redux/services/types";
4+
import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent";
5+
import { addPrefix } from "../../../../utils/addPrefix";
6+
import { CreateEnvironmentWizard } from "../../../RecentActivity/CreateEnvironmentWizard";
7+
import { CancelConfirmationDialog } from "../../../RecentActivity/CreateEnvironmentWizard/CancelConfirmationDialog";
8+
import { SidebarOverlay } from "../../common/SidebarOverlay";
9+
import type { SidebarProps } from "../../common/SidebarOverlay/Sidebar/types";
10+
import { trackingEvents } from "../../tracking";
11+
import { FinishScreenContent } from "./FinishScreenContent";
12+
import * as s from "./styles";
13+
import type { CreateEnvironmentSidebarOverlayProps } from "./types";
14+
15+
const TRACKING_PREFIX = "create environment";
16+
const prefixedTrackingEvents = addPrefix(TRACKING_PREFIX, trackingEvents, " ");
17+
18+
export const CreateEnvironmentSidebarOverlay = ({
19+
isSidebarOpen,
20+
onSidebarClose
21+
}: CreateEnvironmentSidebarOverlayProps) => {
22+
const { data: about } = useGetAboutQuery();
23+
const [createdEnvironment, setCreatedEnvironment] = useState<Environment>();
24+
const [
25+
isCancelConfirmationDialogVisible,
26+
setIsCancelConfirmationDialogVisible
27+
] = useState(false);
28+
29+
const handleCreateEnvironment = (environment: Environment) => {
30+
setCreatedEnvironment(environment);
31+
};
32+
33+
const handleCreateEnvironmentWizardClose = (id: string | null) => {
34+
if (id) {
35+
onSidebarClose();
36+
} else {
37+
setIsCancelConfirmationDialogVisible(true);
38+
}
39+
};
40+
41+
const handleCancelConfirmationDialogClose = () => {
42+
setIsCancelConfirmationDialogVisible(false);
43+
};
44+
45+
const handleCancelConfirmationDialogConfirm = useCallback(() => {
46+
setIsCancelConfirmationDialogVisible(false);
47+
onSidebarClose();
48+
}, [onSidebarClose]);
49+
50+
const handleSidebarClose = () => {
51+
if (createdEnvironment) {
52+
onSidebarClose();
53+
} else {
54+
setIsCancelConfirmationDialogVisible(true);
55+
}
56+
};
57+
58+
useEffect(() => {
59+
const handleKeyDown = (e: KeyboardEvent) => {
60+
switch (e.key) {
61+
case "Escape":
62+
sendUserActionTrackingEvent(
63+
prefixedTrackingEvents.SIDEBAR_ESCAPE_KEY_PRESSED
64+
);
65+
if (isSidebarOpen) {
66+
if (createdEnvironment) {
67+
onSidebarClose();
68+
} else {
69+
setIsCancelConfirmationDialogVisible(
70+
!isCancelConfirmationDialogVisible
71+
);
72+
}
73+
}
74+
break;
75+
case "Enter":
76+
e.preventDefault();
77+
if (isCancelConfirmationDialogVisible) {
78+
handleCancelConfirmationDialogConfirm();
79+
}
80+
}
81+
};
82+
83+
document.addEventListener("keydown", handleKeyDown);
84+
85+
return () => {
86+
document.removeEventListener("keydown", handleKeyDown);
87+
};
88+
}, [
89+
isCancelConfirmationDialogVisible,
90+
createdEnvironment,
91+
onSidebarClose,
92+
isSidebarOpen,
93+
handleCancelConfirmationDialogConfirm
94+
]);
95+
96+
const sidebarProps: SidebarProps = {
97+
title: "Create new environment",
98+
content: {
99+
header: <s.HeaderPlaceholder />,
100+
body: (
101+
<CreateEnvironmentWizard
102+
onClose={handleCreateEnvironmentWizardClose}
103+
isPanelTitleVisible={false}
104+
isCentralizedDeployment={Boolean(about?.isCentralize)}
105+
onCreate={handleCreateEnvironment}
106+
finishScreenContent={
107+
<FinishScreenContent
108+
onGoToDashboard={handleCreateEnvironmentWizardClose}
109+
environment={createdEnvironment}
110+
/>
111+
}
112+
isCancelConfirmationEnabled={false}
113+
/>
114+
)
115+
},
116+
onClose: handleSidebarClose
117+
};
118+
119+
return (
120+
<SidebarOverlay
121+
isSidebarOpen={isSidebarOpen}
122+
onSidebarClose={handleSidebarClose}
123+
sidebar={sidebarProps}
124+
>
125+
{isCancelConfirmationDialogVisible && (
126+
<s.Overlay>
127+
<CancelConfirmationDialog
128+
onClose={handleCancelConfirmationDialogClose}
129+
onConfirm={handleCancelConfirmationDialogConfirm}
130+
/>
131+
</s.Overlay>
132+
)}
133+
</SidebarOverlay>
134+
);
135+
};
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import styled from "styled-components";
2+
import { Overlay as CommonOverlay } from "../../../common/Overlay";
3+
4+
export const HeaderPlaceholder = styled.div`
5+
height: 8px;
6+
`;
7+
8+
export const Overlay = styled(CommonOverlay)`
9+
align-items: center;
10+
`;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface CreateEnvironmentSidebarOverlayProps {
2+
isSidebarOpen: boolean;
3+
onSidebarClose: () => void;
4+
}

src/components/RecentActivity/CreateEnvironmentWizard/EnvironmentCreated/EnvironmentCreated.stories.tsx renamed to src/components/Admin/Environments/Environments.stories.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type { Meta, StoryObj } from "@storybook/react";
2-
import { EnvironmentCreated } from ".";
2+
import { Environments } from ".";
33

44
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
5-
const meta: Meta<typeof EnvironmentCreated> = {
6-
title: "Recent Activity/CreateEnvironmentWizard/EnvironmentCreated",
7-
component: EnvironmentCreated,
5+
const meta: Meta<typeof Environments> = {
6+
title: "Admin/Admin/Environments",
7+
component: Environments,
88
parameters: {
99
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
1010
layout: "fullscreen"
@@ -13,7 +13,7 @@ const meta: Meta<typeof EnvironmentCreated> = {
1313

1414
export default meta;
1515

16-
type Story = StoryObj<typeof EnvironmentCreated>;
16+
type Story = StoryObj<typeof Environments>;
1717

1818
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
1919
export const Default: Story = {};

0 commit comments

Comments
 (0)