Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 60 additions & 19 deletions src/components/InstallationWizard/FinishStep/index.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import { DefaultTheme, useTheme } from "styled-components";
import { getThemeKind } from "../../common/App/styles";
import { CircleLoader } from "../../common/CircleLoader";
import { BellIcon } from "../../common/icons/BellIcon";
import { ChatIcon } from "../../common/icons/ChatIcon";
import { CheckmarkCircleInvertedIcon } from "../../common/icons/CheckmarkCircleInvertedIcon";
import { GearIcon } from "../../common/icons/GearIcon";
import { PlayIcon } from "../../common/icons/PlayIcon";
import { SlackLogoIcon } from "../../common/icons/SlackLogoIcon";
import { WarningCircleLargeIcon } from "../../common/icons/WarningCircleLargeIcon";
import { Link } from "../styles";
import * as s from "./styles";
import { FinishStepProps } from "./types";

const EMAIL_ERROR_MESSAGE = "Enter a valid email";

const getPlayIconColor = (theme: DefaultTheme) => {
switch (theme.mode) {
case "light":
Expand All @@ -18,6 +24,16 @@ const getPlayIconColor = (theme: DefaultTheme) => {
}
};

const getErrorIconColor = (theme: DefaultTheme) => {
switch (theme.mode) {
case "light":
return "#e00036";
case "dark":
case "dark-jetbrains":
return "#f93967";
}
};

export const FinishStep = (props: FinishStepProps) => {
const theme = useTheme();
const themeKind = getThemeKind(theme);
Expand All @@ -41,28 +57,42 @@ export const FinishStep = (props: FinishStepProps) => {
</>
)}
<s.SectionTitle icon={BellIcon}>
Stay Up To Date<s.SectionTitleNote>(optional)</s.SectionTitleNote>
Stay up to date<s.SectionTitleNote>(optional)</s.SectionTitleNote>
</s.SectionTitle>
<s.SectionDescription>
Enter your E-mail address to be the first to get Digma updates
</s.SectionDescription>
<s.EmailInput
type={"text"}
placeholder={"Enter E-mail"}
onChange={props.onEmailChange}
value={props.email}
/>
{props.emailErrorMessage && (
<s.ErrorMessage>{props.emailErrorMessage}</s.ErrorMessage>
)}
<s.SlackLink
target={"_blank"}
rel={"noopener noreferrer"}
href={props.slackChannelURL}
>
<SlackLogoIcon />
Join our Slack channel
</s.SlackLink>
<s.EmailField>
<s.EmailInput
type={"text"}
placeholder={"Enter E-mail"}
value={props.email}
onChange={props.onEmailInputChange}
/>
{props.isEmailValid === false && (
<s.ErrorMessage>
<WarningCircleLargeIcon color={getErrorIconColor(theme)} />
{EMAIL_ERROR_MESSAGE}
</s.ErrorMessage>
)}
{props.isEmailValid && (
<s.EmailInputIconContainer>
<CheckmarkCircleInvertedIcon color={"#00c108"} size={16} />
</s.EmailInputIconContainer>
)}
{props.isEmailValidating && (
<s.EmailInputIconContainer>
<CircleLoader
size={16}
colors={{
start: "rgba(53, 56, 205, 0.3)",
end: "#fff",
background: "#252526"
}}
/>
</s.EmailInputIconContainer>
)}
</s.EmailField>
<s.SectionTitle icon={PlayIcon}>
Run / Debug your application
</s.SectionTitle>
Expand All @@ -73,7 +103,7 @@ export const FinishStep = (props: FinishStepProps) => {
<s.IllustrationContainer>
<s.RunOrDebugIllustration src={`/images/runOrDebug_${themeKind}.gif`} />
</s.IllustrationContainer>
<s.SectionTitle>Getting Started</s.SectionTitle>
<s.SectionTitle>Getting started</s.SectionTitle>
<s.SectionDescription>
We&apos;ve prepared a short video to show you the ropes on getting
started analyzing your code with Digma.
Expand All @@ -92,6 +122,17 @@ export const FinishStep = (props: FinishStepProps) => {
/>
</s.IllustrationContainer>
</Link>
<s.GiveUsFeedbackTitle icon={ChatIcon}>
Give us feedback
</s.GiveUsFeedbackTitle>
<s.SlackLink
target={"_blank"}
rel={"noopener noreferrer"}
href={props.slackChannelURL}
>
<SlackLogoIcon />
Join Our Slack Channel
</s.SlackLink>
</s.Container>
);
};
31 changes: 29 additions & 2 deletions src/components/InstallationWizard/FinishStep/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export const SectionDescription = styled(CommonSectionDescription)`
export const IllustrationContainer = styled(CommonIllustrationContainer)`
margin: 0 0 12px;
position: relative;
overflow: hidden;
`;

export const PlayIconContainer = styled.div`
Expand All @@ -62,6 +63,14 @@ export const RunOrDebugIllustration = styled.img`
margin: 7% 17%;
`;

export const EmailField = styled.div`
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 20px;
position: relative;
`;

export const EmailInput = styled.input`
box-sizing: border-box;
font-size: 12px;
Expand Down Expand Up @@ -127,8 +136,22 @@ export const EmailInput = styled.input`
}
`;

export const EmailInputIconContainer = styled.div`
position: absolute;
top: 0;
bottom: 0;
right: 8px;
height: 16px;
width: 16px;
margin: auto;
`;

export const ErrorMessage = styled(CommonSectionDescription)`
margin-top: 4px;
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
line-height: 14px;
color: ${({ theme }) => {
switch (theme.mode) {
case "light":
Expand All @@ -144,10 +167,14 @@ export const SlackLink = styled(Link)`
display: flex;
align-items: center;
gap: 4px;
margin: 12px 0 20px;
margin-bottom: 12px;
`;

export const ThumbnailPlayCircleIcon = styled(PlayCircleIcon)`
width: 100%;
height: 100%;
`;

export const GiveUsFeedbackTitle = styled(SectionTitle)`
margin-top: 8px;
`;
5 changes: 3 additions & 2 deletions src/components/InstallationWizard/FinishStep/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import { ChangeEvent } from "react";
export interface FinishStepProps {
quickstartURL?: string;
slackChannelURL: string;
onEmailChange: (e: ChangeEvent<HTMLInputElement>) => void;
onEmailInputChange: (e: ChangeEvent<HTMLInputElement>) => void;
email: string;
emailErrorMessage?: string;
isEmailValid?: boolean;
isEmailValidating: boolean;
}
2 changes: 1 addition & 1 deletion src/components/InstallationWizard/InstallStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ export const InstallStep = (props: InstallStepProps) => {
<s.InstallTabContentContainer>
<s.SectionTitle>
<DockerLogoIcon size={24} color={"#2396ed"} />
Install Digma Docker Extension
Install Digma Docker extension
</s.SectionTitle>
<SectionDescription>
(You&apos;ll need{" "}
Expand Down
52 changes: 31 additions & 21 deletions src/components/InstallationWizard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { useEffect, useRef, useState } from "react";
import { ChangeEvent, useEffect, useRef, useState } from "react";
import { CSSTransition } from "react-transition-group";
import { useTheme } from "styled-components";
import { dispatcher } from "../../dispatcher";
import { IDE } from "../../globals";
import { useDebounce } from "../../hooks/useDebounce";
import { usePrevious } from "../../hooks/usePrevious";
import { ide } from "../../platform";
import { addPrefix } from "../../utils/addPrefix";
Expand Down Expand Up @@ -97,6 +98,10 @@ const getStepStatus = (index: number, currentStep: number): StepStatus => {
return "not-completed";
};

const validateEmailFormat = (email: string): boolean => {
return new RegExp(EMAIL_ADDRESS_REGEX).test(email);
};

export const InstallationWizard = () => {
const [currentStep, setCurrentStep] = useState<number>(firstStep);
const previousStep = usePrevious(currentStep);
Expand All @@ -113,7 +118,20 @@ export const InstallationWizard = () => {
const theme = useTheme();
const themeKind = getThemeKind(theme);
const [email, setEmail] = useState(preselectedEmail);
const [emailErrorMessage, setEmailErrorMessage] = useState("");
const [isEmailValid, setIsEmailValid] = useState(
email.length > 0 ? validateEmailFormat(email) : undefined
);
const [isEmailValidating, setIsEmailValidating] = useState(false);
const debouncedEmail = useDebounce<string>(email, 1000);

useEffect(() => {
const res =
debouncedEmail.length > 0
? validateEmailFormat(debouncedEmail)
: undefined;
setIsEmailValid(res);
setIsEmailValidating(false);
}, [debouncedEmail]);

useEffect(() => {
if (previousStep === 0 && currentStep === 1) {
Expand Down Expand Up @@ -228,29 +246,17 @@ export const InstallationWizard = () => {
setInstallationType(installationType);
};

const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (emailErrorMessage) {
setEmailErrorMessage("");
}
const handleEmailInputChange = (e: ChangeEvent<HTMLInputElement>) => {
setIsEmailValid(undefined);
setIsEmailValidating(true);
setEmail(e.target.value.trim());
};

const validateEmailFormat = (email: string): boolean => {
return new RegExp(EMAIL_ADDRESS_REGEX).test(email);
};

const handleFinishButtonClick = () => {
if (email && !validateEmailFormat(email)) {
setEmailErrorMessage(
"E-mail has incorrect format. Please try another one"
);
return;
}

window.sendMessageToDigma({
action: actions.FINISH,
payload: {
...(email.length > 0 ? { email } : {})
...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {})
}
});
};
Expand Down Expand Up @@ -296,8 +302,9 @@ export const InstallationWizard = () => {
quickstartURL={quickstartURL}
slackChannelURL={SLACK_CHANNEL_URL}
email={email}
onEmailChange={handleEmailChange}
emailErrorMessage={emailErrorMessage}
onEmailInputChange={handleEmailInputChange}
isEmailValid={isEmailValid}
isEmailValidating={isEmailValidating}
/>
)
}
Expand Down Expand Up @@ -393,7 +400,10 @@ export const InstallationWizard = () => {
transitionClassName={footerTransitionClassName}
transitionDuration={TRANSITION_DURATION}
>
<s.MainButton onClick={handleFinishButtonClick}>
<s.MainButton
onClick={handleFinishButtonClick}
disabled={isEmailValid === false || isEmailValidating}
>
Finish
</s.MainButton>
</s.FooterContent>
Expand Down
17 changes: 17 additions & 0 deletions src/components/common/CircleLoader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { DEFAULT_ICON_SIZE } from "../icons/hooks";
import * as s from "./styles";
import { CircleLoaderProps } from "./types";

export const CircleLoader = (props: CircleLoaderProps) => {
const size = props.size || DEFAULT_ICON_SIZE;

return (
<s.OuterCircle
size={size}
startColor={props.colors.start}
endColor={props.colors.end}
>
<s.InnerCircle background={props.colors.background} />
</s.OuterCircle>
);
};
29 changes: 29 additions & 0 deletions src/components/common/CircleLoader/styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import styled, { keyframes } from "styled-components";
import { InnerCircleProps, OuterCircleProps } from "./types";

const rotateAnimation = keyframes`
from { transform: rotate(0); }
to { transform: rotate(360deg); }
`;

export const OuterCircle = styled.div<OuterCircleProps>`
display: flex;
align-items: center;
justify-content: center;
width: ${({ size }) => size}px;
height: ${({ size }) => size}px;
border-radius: 50%;
background: conic-gradient(
from 90deg at 50% 50%,
${({ startColor }) => startColor} 0deg,
${({ endColor }) => endColor} 360deg
);
animation: ${rotateAnimation} 1s linear infinite;
`;

export const InnerCircle = styled.div<InnerCircleProps>`
width: 83%;
height: 83%;
border-radius: 50%;
background: ${({ background }) => background};
`;
18 changes: 18 additions & 0 deletions src/components/common/CircleLoader/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface CircleLoaderProps {
size?: number;
colors: {
start: string;
end: string;
background: string;
};
}

export interface OuterCircleProps {
size: number;
startColor: string;
endColor: string;
}

export interface InnerCircleProps {
background: string;
}
Loading