From 8ca9ab727929bc2a7eaed6a7c9fab3b8dfbbbf08 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sat, 6 Apr 2024 03:15:17 +0200 Subject: [PATCH 01/14] Add Digmathon mode --- .storybook/preview-body.html | 1 + .vscode/settings.json | 2 +- public/images/confettiBackground.svg | 295 ++++++++++++++++++ .../insightCards/BottleneckInsightCard.svg | 90 ++++++ ...EndpointHighNumberOfQueriesInsightCard.svg | 96 ++++++ .../EndpointSessionInViewInsightCard.svg | 60 ++++ .../insightCards/HotSpotInsightCard.svg | 50 +++ .../insightCards/NPlusOneInsightCard.svg | 96 ++++++ .../QueryOptimizationInsightCard.svg | 81 +++++ .../insightCards/SpanNexusInsightCard.svg | 50 +++ .../insightCards/SpanScalingInsightCard.svg | 19 ++ .../insightCards/SpanUsagesInsightCard.svg | 51 +++ .../EndpointBottleneckHighlightCard/index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../HotSpotHighlightCard/index.tsx | 4 +- .../SpaNPlusOneHighlightCard/index.tsx | 4 +- .../index.tsx | 4 +- .../index.tsx | 4 +- .../SpanScalingHighlightCard/index.tsx | 4 +- .../highlightCards/goToEnvironmentIssues.ts | 2 +- .../Highlights/TopIssues/useTopIssuesData.ts | 22 +- src/components/Insights/useInsightsData.ts | 59 ++-- .../InstallationWizard/FinishStep/index.tsx | 28 +- .../InstallationWizard/FinishStep/styles.ts | 4 +- .../InstallationWizard/FinishStep/types.ts | 2 + src/components/InstallationWizard/index.tsx | 14 +- src/components/Notifications/index.tsx | 3 + .../Digmathon/CongratulationsView/index.tsx | 43 +++ .../Digmathon/CongratulationsView/styles.ts | 46 +++ .../Digmathon/Digmathon.stories.tsx | 53 ++++ .../DigmathonInsightCard.stories.tsx | 37 +++ .../Digmathon/DigmathonInsightCard/index.tsx | 18 ++ .../Digmathon/DigmathonInsightCard/styles.ts | 76 +++++ .../Digmathon/DigmathonInsightCard/types.ts | 11 + .../Digmathon/ProgressView/index.tsx | 42 +++ .../Digmathon/ProgressView/styles.ts | 41 +++ .../Digmathon/ProgressView/types.ts | 6 + .../Digmathon/getDigmathonInsightData.tsx | 90 ++++++ .../RecentActivity/Digmathon/index.tsx | 184 +++++++++++ .../RecentActivity/Digmathon/mockData.ts | 21 ++ .../RecentActivity/Digmathon/styles.ts | 59 ++++ .../RecentActivity/Digmathon/types.ts | 22 ++ .../RecentActivity/EnvironmentPanel/index.tsx | 27 +- .../RecentActivity/EnvironmentPanel/types.ts | 1 + .../RecentActivity/RecentActivity.stories.tsx | 9 + src/components/RecentActivity/actions.ts | 5 +- src/components/RecentActivity/index.tsx | 119 ++++--- src/components/RecentActivity/styles.ts | 1 + src/components/RecentActivity/tracking.ts | 7 +- src/components/common/App/ConfigContext.ts | 6 +- src/components/common/App/types.ts | 2 + src/components/common/App/typographies.ts | 34 +- src/components/common/icons/16px/KeyIcon.tsx | 26 ++ .../common/icons/20px/CheckmarkCircleIcon.tsx | 34 ++ src/globals.d.ts | 2 + src/utils/actions/openURLInDefaultBrowser.ts | 7 +- webpackEntries.ts | 4 + 62 files changed, 1982 insertions(+), 124 deletions(-) create mode 100644 public/images/confettiBackground.svg create mode 100644 public/images/insightCards/BottleneckInsightCard.svg create mode 100644 public/images/insightCards/EndpointHighNumberOfQueriesInsightCard.svg create mode 100644 public/images/insightCards/EndpointSessionInViewInsightCard.svg create mode 100644 public/images/insightCards/HotSpotInsightCard.svg create mode 100644 public/images/insightCards/NPlusOneInsightCard.svg create mode 100644 public/images/insightCards/QueryOptimizationInsightCard.svg create mode 100644 public/images/insightCards/SpanNexusInsightCard.svg create mode 100644 public/images/insightCards/SpanScalingInsightCard.svg create mode 100644 public/images/insightCards/SpanUsagesInsightCard.svg create mode 100644 src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx create mode 100644 src/components/RecentActivity/Digmathon/CongratulationsView/styles.ts create mode 100644 src/components/RecentActivity/Digmathon/Digmathon.stories.tsx create mode 100644 src/components/RecentActivity/Digmathon/DigmathonInsightCard/DigmathonInsightCard.stories.tsx create mode 100644 src/components/RecentActivity/Digmathon/DigmathonInsightCard/index.tsx create mode 100644 src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts create mode 100644 src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts create mode 100644 src/components/RecentActivity/Digmathon/ProgressView/index.tsx create mode 100644 src/components/RecentActivity/Digmathon/ProgressView/styles.ts create mode 100644 src/components/RecentActivity/Digmathon/ProgressView/types.ts create mode 100644 src/components/RecentActivity/Digmathon/getDigmathonInsightData.tsx create mode 100644 src/components/RecentActivity/Digmathon/index.tsx create mode 100644 src/components/RecentActivity/Digmathon/mockData.ts create mode 100644 src/components/RecentActivity/Digmathon/styles.ts create mode 100644 src/components/RecentActivity/Digmathon/types.ts create mode 100644 src/components/common/icons/16px/KeyIcon.tsx create mode 100644 src/components/common/icons/20px/CheckmarkCircleIcon.tsx diff --git a/.storybook/preview-body.html b/.storybook/preview-body.html index 70e02152f..2379489d7 100644 --- a/.storybook/preview-body.html +++ b/.storybook/preview-body.html @@ -16,6 +16,7 @@ window.isDockerInstalled = true; window.isDockerComposeInstalled = true; window.isMicrometerProject; + window.isDigmathonModeEnabled = true; window.assetsRefreshInterval; window.assetsSearch = true; diff --git a/.vscode/settings.json b/.vscode/settings.json index 0c01357f2..90160881f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,5 @@ }, "editor.defaultFormatter": "esbenp.prettier-vscode", "stylelint.validate": ["typescript"], - "cSpell.words": ["Digma", "UNDISMISS"] + "cSpell.words": ["digma", "digmathon", "undismiss"] } diff --git a/public/images/confettiBackground.svg b/public/images/confettiBackground.svg new file mode 100644 index 000000000..bf8328cd6 --- /dev/null +++ b/public/images/confettiBackground.svg @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/BottleneckInsightCard.svg b/public/images/insightCards/BottleneckInsightCard.svg new file mode 100644 index 000000000..ab0d6687f --- /dev/null +++ b/public/images/insightCards/BottleneckInsightCard.svg @@ -0,0 +1,90 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/EndpointHighNumberOfQueriesInsightCard.svg b/public/images/insightCards/EndpointHighNumberOfQueriesInsightCard.svg new file mode 100644 index 000000000..aa7137fa1 --- /dev/null +++ b/public/images/insightCards/EndpointHighNumberOfQueriesInsightCard.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/EndpointSessionInViewInsightCard.svg b/public/images/insightCards/EndpointSessionInViewInsightCard.svg new file mode 100644 index 000000000..de4affc5b --- /dev/null +++ b/public/images/insightCards/EndpointSessionInViewInsightCard.svg @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/HotSpotInsightCard.svg b/public/images/insightCards/HotSpotInsightCard.svg new file mode 100644 index 000000000..58abf5153 --- /dev/null +++ b/public/images/insightCards/HotSpotInsightCard.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/NPlusOneInsightCard.svg b/public/images/insightCards/NPlusOneInsightCard.svg new file mode 100644 index 000000000..da70bbda6 --- /dev/null +++ b/public/images/insightCards/NPlusOneInsightCard.svg @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/QueryOptimizationInsightCard.svg b/public/images/insightCards/QueryOptimizationInsightCard.svg new file mode 100644 index 000000000..7e6d73307 --- /dev/null +++ b/public/images/insightCards/QueryOptimizationInsightCard.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/SpanNexusInsightCard.svg b/public/images/insightCards/SpanNexusInsightCard.svg new file mode 100644 index 000000000..72870cfb0 --- /dev/null +++ b/public/images/insightCards/SpanNexusInsightCard.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/SpanScalingInsightCard.svg b/public/images/insightCards/SpanScalingInsightCard.svg new file mode 100644 index 000000000..d9a89501d --- /dev/null +++ b/public/images/insightCards/SpanScalingInsightCard.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/insightCards/SpanUsagesInsightCard.svg b/public/images/insightCards/SpanUsagesInsightCard.svg new file mode 100644 index 000000000..d0add0c67 --- /dev/null +++ b/public/images/insightCards/SpanUsagesInsightCard.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx index 1d4c78638..f7d2d1618 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointBottleneckHighlightCard/index.tsx @@ -10,7 +10,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EndpointBottleneckMetrics, EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointBottleneckHighlightCardProps } from "./types"; @@ -65,7 +65,7 @@ export const EndpointBottleneckHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx index 5a5a5527f..3c462213c 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointChattyApiV2HighlightCard/index.tsx @@ -7,7 +7,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EndpointChattyApiV2Metrics, EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointChattyApiV2HighlightCardProps } from "./types"; @@ -35,7 +35,7 @@ export const EndpointChattyApiV2HighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx index 03b21e3a6..9a1fd7efa 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointHighNumberOfQueriesHighlightCard/index.tsx @@ -10,7 +10,7 @@ import { EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointHighNumberOfQueriesHighlightCardProps } from "./types"; @@ -52,7 +52,7 @@ export const EndpointHighNumberOfQueriesHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx index fce8e8763..37257f58f 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointQueryOptimizationV2HighlightCard/index.tsx @@ -11,7 +11,7 @@ import { EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointQueryOptimizationV2HighlightCardProps } from "./types"; @@ -39,7 +39,7 @@ export const EndpointQueryOptimizationV2HighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx index 887bfa4d9..f1f05d8df 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSessionInViewHighlightCard/index.tsx @@ -6,7 +6,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EndpointSessionInViewMetrics, EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointSessionInViewHighlightCardProps } from "./types"; @@ -23,7 +23,7 @@ export const EndpointSessionInViewHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx index 90854b544..f74a03b15 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSlowdownSourceHighlightCard/index.tsx @@ -8,7 +8,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EndpointSlowdownSourceMetrics, EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointSlowdownSourceHighlightCardProps } from "./types"; @@ -39,7 +39,7 @@ export const EndpointSlowdownSourceHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx index ab379c54d..202829407 100644 --- a/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/EndpointSpanNPlusOneHighlightCard/index.tsx @@ -10,7 +10,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EndpointSpanNPlusOneMetrics, EnvironmentData } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { EndpointSpanNPlusOneHighlightCardProps } from "./types"; @@ -57,7 +57,7 @@ export const EndpointSpanNPlusOneHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx index 39faccf1c..28f0875f0 100644 --- a/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/HotSpotHighlightCard/index.tsx @@ -6,7 +6,7 @@ import { TableText } from "../../../common/TableText"; import { HighlightCard } from "../../common/HighlightCard"; import { EnvironmentData, HotSpotMetrics } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { HotSpotHighlightCardProps } from "./types"; export const HotSpotHighlightCard = ({ data }: HotSpotHighlightCardProps) => { @@ -28,7 +28,7 @@ export const HotSpotHighlightCard = ({ data }: HotSpotHighlightCardProps) => { const columns = addEnvironmentColumns(columnHelper, metricsColumns); const handleTableRowClick = (row: Row>) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx index 302e5fde3..29ce0fb4a 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpaNPlusOneHighlightCard/index.tsx @@ -9,7 +9,7 @@ import { TableText } from "../../../common/TableText"; import { HighlightCard } from "../../common/HighlightCard"; import { EnvironmentData, SpaNPlusOneMetrics } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { SpaNPlusOneHighlightCardProps } from "./types"; export const SpaNPlusOneHighlightCard = ({ @@ -66,7 +66,7 @@ export const SpaNPlusOneHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx index dabc0b809..80685de4d 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanEndpointBottleneckHighlightCard/index.tsx @@ -9,7 +9,7 @@ import { TableText } from "../../../common/TableText"; import { HighlightCard } from "../../common/HighlightCard"; import { EnvironmentData, SpanEndpointBottleneckMetrics } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { SpanEndpointBottleneckHighlightCardProps } from "./types"; export const SpanEndpointBottleneckHighlightCard = ({ @@ -63,7 +63,7 @@ export const SpanEndpointBottleneckHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx index 1396e9200..b6ea7f6bc 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanQueryOptimizationHighlightCard/index.tsx @@ -10,7 +10,7 @@ import { AssetLink } from "../../common/AssetLink"; import { HighlightCard } from "../../common/HighlightCard"; import { EnvironmentData, SpanQueryOptimizationMetrics } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { DescriptionContainer } from "../styles"; import { SpanQueryOptimizationHighlightCardProps } from "./types"; @@ -70,7 +70,7 @@ export const SpanQueryOptimizationHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx b/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx index b627e4ab4..93c20cef8 100644 --- a/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx +++ b/src/components/Highlights/TopIssues/highlightCards/SpanScalingHighlightCard/index.tsx @@ -6,7 +6,7 @@ import { TableText } from "../../../common/TableText"; import { HighlightCard } from "../../common/HighlightCard"; import { EnvironmentData, SpanScalingMetrics } from "../../types"; import { addEnvironmentColumns } from "../addEnvironmentColumns"; -import { goToEnvironmentIssues } from "../goToEnvironmentIssues"; +import { handleEnvironmentTableRowClick } from "../goToEnvironmentIssues"; import { SpanScalingHighlightCardProps } from "./types"; export const SpanScalingHighlightCard = ({ @@ -36,7 +36,7 @@ export const SpanScalingHighlightCard = ({ const handleTableRowClick = ( row: Row> ) => { - goToEnvironmentIssues( + handleEnvironmentTableRowClick( config.environments, row.original.environmentName, data.insightType diff --git a/src/components/Highlights/TopIssues/highlightCards/goToEnvironmentIssues.ts b/src/components/Highlights/TopIssues/highlightCards/goToEnvironmentIssues.ts index ad64e2eb9..1b231e6c9 100644 --- a/src/components/Highlights/TopIssues/highlightCards/goToEnvironmentIssues.ts +++ b/src/components/Highlights/TopIssues/highlightCards/goToEnvironmentIssues.ts @@ -8,7 +8,7 @@ import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserA import { Environment } from "../../../common/App/types"; import { trackingEvents } from "../../tracking"; -export const goToEnvironmentIssues = ( +export const handleEnvironmentTableRowClick = ( environments: Environment[] | undefined, environmentNameToSelect: string, insightType: InsightType diff --git a/src/components/Highlights/TopIssues/useTopIssuesData.ts b/src/components/Highlights/TopIssues/useTopIssuesData.ts index 7511d33de..44588936a 100644 --- a/src/components/Highlights/TopIssues/useTopIssuesData.ts +++ b/src/components/Highlights/TopIssues/useTopIssuesData.ts @@ -26,26 +26,26 @@ export const useTopIssuesData = () => { } }); }, [config.scope?.span?.spanCodeObjectId, config.environments]); - const previousGetData = usePrevious(getData); + useEffect(() => { + if (previousGetData && previousGetData !== getData) { + window.clearTimeout(refreshTimerId.current); + + getData(); + } + }, [previousGetData, getData]); + useEffect(() => { if ( - (previousGetData && previousGetData !== getData) || - (previousLastSetDataTimeStamp && - previousLastSetDataTimeStamp !== lastSetDataTimeStamp) + previousLastSetDataTimeStamp && + previousLastSetDataTimeStamp !== lastSetDataTimeStamp ) { - window.clearTimeout(refreshTimerId.current); refreshTimerId.current = window.setTimeout(() => { getData(); }, REFRESH_INTERVAL); } - }, [ - previousLastSetDataTimeStamp, - lastSetDataTimeStamp, - getData, - previousGetData - ]); + }, [previousLastSetDataTimeStamp, lastSetDataTimeStamp, getData]); useEffect(() => { const handleTopIssuesData = (data: any, timeStamp: number) => { diff --git a/src/components/Insights/useInsightsData.ts b/src/components/Insights/useInsightsData.ts index f787e5a64..e56a16c0f 100644 --- a/src/components/Insights/useInsightsData.ts +++ b/src/components/Insights/useInsightsData.ts @@ -21,16 +21,16 @@ interface UseInsightDataProps { query: InsightsQuery; } -const getData = (query: ScopedInsightsQuery, state?: GlobalState) => { +const getData = (scopedQuery: ScopedInsightsQuery, state?: GlobalState) => { const getDataQuery: InsightsDataQuery = { - displayName: query.searchQuery, - sortBy: query.sorting.criterion, - sortOrder: query.sorting.order, - page: query.page, - scopedSpanCodeObjectId: query.scopedSpanCodeObjectId, - showDismissed: query.showDismissed, - insightViewType: query.insightViewType, - showUnreadOnly: query.showUnreadOnly + displayName: scopedQuery.searchQuery, + sortBy: scopedQuery.sorting.criterion, + sortOrder: scopedQuery.sorting.order, + page: scopedQuery.page, + scopedSpanCodeObjectId: scopedQuery.scopedSpanCodeObjectId, + showDismissed: scopedQuery.showDismissed, + insightViewType: scopedQuery.insightViewType, + showUnreadOnly: scopedQuery.showUnreadOnly }; window.sendMessageToDigma({ @@ -41,7 +41,7 @@ const getData = (query: ScopedInsightsQuery, state?: GlobalState) => { }); const globalStateSlice = - query.insightViewType === "Analytics" ? "analytics" : "insights"; + scopedQuery.insightViewType === "Analytics" ? "analytics" : "insights"; window.sendMessageToDigma({ action: globalActions.UPDATE_STATE, @@ -55,7 +55,10 @@ const getData = (query: ScopedInsightsQuery, state?: GlobalState) => { }); }; -export const useInsightsData = (props: UseInsightDataProps) => { +export const useInsightsData = ({ + refreshInterval, + query +}: UseInsightDataProps) => { const [data, setData] = useState({ insightsStatus: InsightsStatus.LOADING, insights: [], @@ -71,16 +74,16 @@ export const useInsightsData = (props: UseInsightDataProps) => { const config = useContext(ConfigContext); const { scope, environment, state } = config; - const query = useMemo( + const scopedQuery = useMemo( () => ({ - ...props.query, + ...query, scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null }), - [props.query, scope] + [query, scope] ); useEffect(() => { - getData(query, state); + getData(scopedQuery, state); setIsInitialLoading(true); setIsLoading(true); @@ -113,13 +116,9 @@ export const useInsightsData = (props: UseInsightDataProps) => { useEffect(() => { if (previousLastSetDataTimeStamp !== lastSetDataTimeStamp) { window.clearTimeout(refreshTimerId.current); - refreshTimerId.current = window.setTimeout( - (insightsQuery: ScopedInsightsQuery) => { - getData(insightsQuery, state); - }, - props.refreshInterval, - query - ); + refreshTimerId.current = window.setTimeout(() => { + getData(scopedQuery, state); + }, refreshInterval); } return () => { @@ -128,26 +127,20 @@ export const useInsightsData = (props: UseInsightDataProps) => { }, [ lastSetDataTimeStamp, previousLastSetDataTimeStamp, - query, environment, - props.refreshInterval + scopedQuery, + refreshInterval ]); useEffect(() => { + getData(scopedQuery, state); setIsLoading(true); - getData( - { - ...props.query, - scopedSpanCodeObjectId: scope?.span?.spanCodeObjectId || null - }, - state - ); - }, [props.query, scope, environment]); + }, [scopedQuery, scope?.span?.spanCodeObjectId, environment?.originalName]); return { isInitialLoading, data, isLoading, - refresh: () => getData(query, state) + refresh: () => getData(scopedQuery, state) }; }; diff --git a/src/components/InstallationWizard/FinishStep/index.tsx b/src/components/InstallationWizard/FinishStep/index.tsx index 36d8b409d..61e08ed5b 100644 --- a/src/components/InstallationWizard/FinishStep/index.tsx +++ b/src/components/InstallationWizard/FinishStep/index.tsx @@ -1,10 +1,13 @@ +import { useContext } from "react"; import { DefaultTheme, useTheme } from "styled-components"; import { GETTING_STARTED_VIDEO_URL } from "../../../constants"; import { openURLInDefaultBrowser } from "../../../utils/actions/openURLInDefaultBrowser"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; +import { ConfigContext } from "../../common/App/ConfigContext"; import { getThemeKind } from "../../common/App/styles"; import { CircleLoader } from "../../common/CircleLoader"; import { Link } from "../../common/Link"; +import { KeyIcon } from "../../common/icons/16px/KeyIcon"; import { ChatIcon } from "../../common/icons/ChatIcon"; import { CheckmarkCircleInvertedIcon } from "../../common/icons/CheckmarkCircleInvertedIcon"; import { GearIcon } from "../../common/icons/GearIcon"; @@ -42,6 +45,7 @@ const getErrorIconColor = (theme: DefaultTheme) => { export const FinishStep = (props: FinishStepProps) => { const theme = useTheme(); const themeKind = getThemeKind(theme); + const config = useContext(ConfigContext); const handleGettingStartedVideoLinkClick = () => { sendUserActionTrackingEvent( @@ -68,6 +72,24 @@ export const FinishStep = (props: FinishStepProps) => { )} + {config.isDigmathonModeEnabled && ( + + + Code product key(optional) + + + If you’ve received a product key, please enter it here + + + + + + )} Stay up to date(optional) @@ -75,8 +97,8 @@ export const FinishStep = (props: FinishStepProps) => { Enter your E-mail address to be the first to get Digma updates - - + { /> )} - + diff --git a/src/components/InstallationWizard/FinishStep/styles.ts b/src/components/InstallationWizard/FinishStep/styles.ts index 3bc407e74..9882ad22d 100644 --- a/src/components/InstallationWizard/FinishStep/styles.ts +++ b/src/components/InstallationWizard/FinishStep/styles.ts @@ -57,14 +57,14 @@ export const RunOrDebugIllustration = styled.img` margin: 7% 17%; `; -export const EmailField = styled.div` +export const TextField = styled.div` display: flex; flex-direction: column; gap: 4px; position: relative; `; -export const EmailInput = styled.input` +export const TextInput = styled.input` font-size: 14px; padding: 8px 10px; border-radius: 4px; diff --git a/src/components/InstallationWizard/FinishStep/types.ts b/src/components/InstallationWizard/FinishStep/types.ts index c43afea77..b3ead6fd0 100644 --- a/src/components/InstallationWizard/FinishStep/types.ts +++ b/src/components/InstallationWizard/FinishStep/types.ts @@ -7,4 +7,6 @@ export interface FinishStepProps { email: string; isEmailValid?: boolean; isEmailValidating: boolean; + productKey: string; + onProductKeyInputChange: (e: ChangeEvent) => void; } diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index 12f2aae87..ae2aac487 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -86,6 +86,7 @@ export const InstallationWizard = () => { ); const [isEmailValidating, setIsEmailValidating] = useState(false); const debouncedEmail = useDebounce(email, 1000); + const [productKey, setProductKey] = useState(config.digmathonProductKey); // const [ // isDigmaCloudNotificationCheckboxChecked, // setIsDigmaCloudNotificationCheckboxChecked @@ -232,6 +233,14 @@ export const InstallationWizard = () => { } }; + const handleProductKeyInputChange = (e: ChangeEvent) => { + const value = e.target.value.trim(); + + if (productKey !== value) { + setProductKey(value); + } + }; + const handleCloseButtonClick = () => { window.sendMessageToDigma({ action: actions.CLOSE @@ -242,7 +251,8 @@ export const InstallationWizard = () => { window.sendMessageToDigma({ action: actions.FINISH, payload: { - ...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {}) + ...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {}), + ...(productKey.length > 0 ? { productKey } : {}) } }); }; @@ -319,6 +329,8 @@ export const InstallationWizard = () => { onEmailInputChange={handleEmailInputChange} isEmailValid={isEmailValid} isEmailValidating={isEmailValidating} + productKey={productKey} + onProductKeyInputChange={handleProductKeyInputChange} /> ) } diff --git a/src/components/Notifications/index.tsx b/src/components/Notifications/index.tsx index 0eda44ba9..81a1118bb 100644 --- a/src/components/Notifications/index.tsx +++ b/src/components/Notifications/index.tsx @@ -44,6 +44,9 @@ export const trackingEvents = addPrefix( " " ); +/** + * @deprecated + */ export const Notifications = (props: NotificationsProps) => { const [data, setData] = useState(); const previousData = usePrevious(data); diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx new file mode 100644 index 000000000..b6352ae1f --- /dev/null +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -0,0 +1,43 @@ +import { useEffect } from "react"; +import { openURLInDefaultBrowser } from "../../../../utils/actions/openURLInDefaultBrowser"; +import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent"; +import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; +import { CheckmarkCircleIcon } from "../../../common/icons/12px/CheckmarkCircleIcon"; +import { trackingEvents } from "../../tracking"; +import * as s from "./styles"; + +const EMAIL_ADDRESS = "contact@digma.ai"; +const EMAIL_SUBJECT = "Digmathon"; + +export const CongratulationsView = () => { + const handleContactLinkClick = () => { + sendUserActionTrackingEvent( + trackingEvents.DIGMATHON_VIEW_CONTACT_LINK_CLICKED + ); + + const body = ""; + const url = `mailto:${EMAIL_ADDRESS}?subject=${EMAIL_SUBJECT}&body=${body}`; + openURLInDefaultBrowser(url); + }; + + useEffect(() => { + sendTrackingEvent(trackingEvents.DIGMATHON_CONGRATULATIONS_VIEWED); + }, []); + + return ( + + + + + + Congratulations! + You've successfully found all the issues. To claim your prize, + please send us an email with your name and "Digmathon" as the + subject line. + + + contact@digma.ai + + + ); +}; diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/styles.ts b/src/components/RecentActivity/Digmathon/CongratulationsView/styles.ts new file mode 100644 index 000000000..879796b09 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/styles.ts @@ -0,0 +1,46 @@ +import styled from "styled-components"; +import { + bodyRegularTypography, + footnoteRegularTypography, + subscriptRegularTypography +} from "../../../common/App/typographies"; +import { Link } from "../../../common/v3/Link"; + +export const Container = styled.div` + background: url("images/confettiBackground.svg") no-repeat center top / cover; + display: flex; + flex-direction: column; + gap: 16px; + justify-content: center; + align-items: center; + flex-grow: 1; +`; + +export const IconContainer = styled.div` + display: flex; + color: ${({ theme }) => theme.colors.v3.status.success}; +`; + +export const TextContainer = styled.div` + ${footnoteRegularTypography} + + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + text-align: center; + color: ${({ theme }) => theme.colors.v3.text.secondary}; + width: 288px; +`; + +export const Title = styled.span` + ${bodyRegularTypography} + + color: ${({ theme }) => theme.colors.v3.text.primary}; +`; + +export const ContactLink = styled(Link)` + ${subscriptRegularTypography} + + padding: 4px 0; +`; diff --git a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx new file mode 100644 index 000000000..074dbe4ba --- /dev/null +++ b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx @@ -0,0 +1,53 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { Digmathon } from "."; +import { actions } from "../actions"; +import { mockedDigmathonProgressData } from "./mockData"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Recent Activity/Digmathon", + component: Digmathon, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + play: () => { + setTimeout(() => { + window.setTimeout(() => { + window.postMessage({ + type: "digma", + action: actions.SET_DIGMATHON_PROGRESS_DATA, + payload: mockedDigmathonProgressData + }); + }); + }, 0); + } +}; + +export const Congratulations: Story = { + play: () => { + setTimeout(() => { + window.setTimeout(() => { + window.postMessage({ + type: "digma", + action: actions.SET_DIGMATHON_PROGRESS_DATA, + payload: { + ...mockedDigmathonProgressData, + insights: mockedDigmathonProgressData.insights.map((x) => ({ + ...x, + isFound: true + })) + } + }); + }); + }, 0); + } +}; diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/DigmathonInsightCard.stories.tsx b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/DigmathonInsightCard.stories.tsx new file mode 100644 index 000000000..39e23920a --- /dev/null +++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/DigmathonInsightCard.stories.tsx @@ -0,0 +1,37 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { DigmathonInsightCard } from "."; +import { InsightType } from "../../../Insights/types"; +import { getDigmathonInsightCardData } from "../getDigmathonInsightData"; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Recent Activity/Digmathon/DigmathonInsightCard", + component: DigmathonInsightCard, + parameters: { + // More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout + layout: "fullscreen" + } +}; + +export default meta; + +type Story = StoryObj; + +const data = getDigmathonInsightCardData(InsightType.EndpointSpanNPlusOne); + +// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args +export const Default: Story = { + args: { + number: 1, + data, + isActive: false + } +}; + +export const Active: Story = { + args: { + number: 1, + data, + isActive: true + } +}; diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/index.tsx b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/index.tsx new file mode 100644 index 000000000..9f489def4 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/index.tsx @@ -0,0 +1,18 @@ +import * as s from "./styles"; +import { DigmathonInsightCardProps } from "./types"; + +export const DigmathonInsightCard = ({ + number, + data, + isActive +}: DigmathonInsightCardProps) => ( + + + {number} + + {data.title} + {data.description} + + {data.illustration} + +); diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts new file mode 100644 index 000000000..205d0cd22 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts @@ -0,0 +1,76 @@ +import styled from "styled-components"; +import { + bodyBoldTypography, + caption1BoldTypography, + footnoteRegularTypography +} from "../../../common/App/typographies"; +import { ContainerProps } from "./types"; + +export const Container = styled.div` + display: flex; + gap: 8px; + padding-top: 16px; + padding-left: 16px; + opacity: ${({ $isActive }) => ($isActive ? 1 : 0.35)}; + border-radius: 8px; + border: 2px solid + ${({ theme, $isActive }) => + $isActive + ? theme.colors.v3.icon.brandSecondary + : theme.colors.stroke.primary}; + background: ${({ theme, $isActive }) => + $isActive ? theme.colors.v3.surface.brandPrimary : "none"}; + height: 114px; + min-width: 191px; + box-sizing: border-box; + overflow: hidden; + position: relative; +`; + +export const NumberContainer = styled.div` + ${caption1BoldTypography} + + display: flex; + height: 16px; + width: 16px; + align-items: center; + justify-content: center; + border: 1px solid ${({ theme }) => theme.colors.v3.surface.brandTertiary}; + border-radius: 50%; + flex-shrink: 0; +`; + +export const TextContainer = styled.div` + display: flex; + flex-direction: column; + color: ${({ theme }) => theme.colors.v3.text.primary}; + width: 143px; + flex-shrink: 0; +`; + +export const Title = styled.span` + ${bodyBoldTypography} +`; + +export const Description = styled.span` + ${footnoteRegularTypography} +`; + +export const IllustrationContainer = styled.div` + min-width: 300px; + overflow: hidden; + margin-left: auto; + z-index: 1; +`; + +export const GradientBackground = styled.div` + width: 326px; + height: 326px; + border-radius: 326px; + opacity: 0.35; + background: ${({ theme }) => theme.colors.v3.icon.primary}; + filter: blur(67px); + position: absolute; + right: -220px; + bottom: -168px; +`; diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts new file mode 100644 index 000000000..24f8d71b0 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts @@ -0,0 +1,11 @@ +import { DigmathonInsightCardData } from "../types"; + +export type DigmathonInsightCardProps = { + number: number; + data: DigmathonInsightCardData; + isActive: boolean; +}; + +export type ContainerProps = { + $isActive: boolean; +}; diff --git a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx new file mode 100644 index 000000000..102082014 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx @@ -0,0 +1,42 @@ +import { useEffect } from "react"; +import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent"; +import { trackingEvents } from "../../tracking"; +import { DigmathonInsightCard } from "../DigmathonInsightCard"; +import * as s from "./styles"; +import { ProgressViewProps } from "./types"; + +export const ProgressView = ({ + insights, + foundIssuesCount +}: ProgressViewProps) => { + useEffect(() => { + sendTrackingEvent(trackingEvents.DIGMATHON_PROGRESS_VIEWED); + }, []); + + return ( + <> + + Search for issues + + Improve your code, and win a gift card + + + {foundIssuesCount} out of{" "} + {insights.length} issues found + + + + {insights.map((x, i) => + x.data ? ( + + ) : null + )} + + + ); +}; diff --git a/src/components/RecentActivity/Digmathon/ProgressView/styles.ts b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts new file mode 100644 index 000000000..e20d4f408 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts @@ -0,0 +1,41 @@ +import styled from "styled-components"; +import { + footnoteMediumTypography, + footnoteRegularTypography +} from "../../../common/App/typographies"; + +export const Header = styled.div` + ${footnoteRegularTypography} + + display: flex; + gap: 8px; + height: 32px; + flex-shrink: 0; + align-items: center; + color: ${({ theme }) => theme.colors.v3.text.primary}; + padding: 0 12px; +`; + +export const HeaderTitle = styled.span` + ${footnoteMediumTypography} +`; + +export const HeaderDescription = styled.span` + color: ${({ theme }) => theme.colors.v3.text.secondary}; +`; + +export const IssuesCounter = styled.span` + margin-left: auto; +`; + +export const FoundIssuesNumber = styled.span` + color: ${({ theme }) => theme.colors.v3.icon.brandSecondary}; +`; + +export const CardsContainer = styled.div` + display: grid; + grid-gap: 8px 10px; + grid-template-columns: 1fr 1fr 1fr; + grid-template-rows: 1fr 1fr 1fr; + padding: 0 12px 12px; +`; diff --git a/src/components/RecentActivity/Digmathon/ProgressView/types.ts b/src/components/RecentActivity/Digmathon/ProgressView/types.ts new file mode 100644 index 000000000..5ea136bae --- /dev/null +++ b/src/components/RecentActivity/Digmathon/ProgressView/types.ts @@ -0,0 +1,6 @@ +import { DigmathonInsightData } from "../types"; + +export interface ProgressViewProps { + insights: DigmathonInsightData[]; + foundIssuesCount: number; +} diff --git a/src/components/RecentActivity/Digmathon/getDigmathonInsightData.tsx b/src/components/RecentActivity/Digmathon/getDigmathonInsightData.tsx new file mode 100644 index 000000000..bb8014983 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/getDigmathonInsightData.tsx @@ -0,0 +1,90 @@ +import { InsightType } from "../../Insights/types"; +import { DigmathonInsightCardData } from "./types"; + +export const getDigmathonInsightCardData = ( + type: InsightType +): DigmathonInsightCardData | undefined => { + switch (type) { + case InsightType.SpanScaling: + return { + title: "Span Scaling", + description: "How well does this scale?", + illustration: ( + + ) + }; + case InsightType.SpanNexus: + return { + title: "Code Nexus", + description: "The most important piece in your code Jenga tower", + illustration: ( + + ) + }; + case InsightType.EndpointQueryOptimizationV2: + case InsightType.EndpointQueryOptimization: + case InsightType.SpanQueryOptimization: + return { + title: "Query Optimization Suggested", + description: "Caching anyone?", + illustration: ( + + ) + }; + case InsightType.HotSpot: + return { + title: "Error Hotspot", + description: "Where errors congregate", + illustration: + }; + case InsightType.EndpointSpanNPlusOne: + case InsightType.EndpointSpaNPlusOne: + case InsightType.SpaNPlusOne: + return { + title: "N+1 Select", + description: "Excuse me sir, your abstraction is leaking", + illustration: ( + + ) + }; + case InsightType.EndpointSessionInView: + return { + title: "Open Session in View", + description: "Why'd you go and do that?", + illustration: ( + + ) + }; + case InsightType.SpanUsages: + return { + title: "Top Usage", + description: "Know where you're coming from to know where you're going", + illustration: ( + + ) + }; + case InsightType.EndpointHighNumberOfQueries: + return { + title: "High Number of Queries", + description: "Stop hitting that DB", + illustration: ( + + ) + }; + case InsightType.EndpointBottleneck: + case InsightType.SpanEndpointBottleneck: + return { + title: "Bottleneck", + description: "Only one way to handle clogged pipes", + illustration: ( + + ) + }; + } +}; diff --git a/src/components/RecentActivity/Digmathon/index.tsx b/src/components/RecentActivity/Digmathon/index.tsx new file mode 100644 index 000000000..e35ae17df --- /dev/null +++ b/src/components/RecentActivity/Digmathon/index.tsx @@ -0,0 +1,184 @@ +import { useEffect, useMemo, useState } from "react"; +import { dispatcher } from "../../../dispatcher"; +import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; +import { InsightType } from "../../Insights/types"; +import { ChevronIcon } from "../../common/icons/16px/ChevronIcon"; +import { Direction } from "../../common/icons/types"; +import { actions } from "../actions"; +import { trackingEvents } from "../tracking"; +import { CongratulationsView } from "./CongratulationsView"; +import { ProgressView } from "./ProgressView"; +import { getDigmathonInsightCardData } from "./getDigmathonInsightData"; +import * as s from "./styles"; +import { + DigmathonInsightData, + DigmathonProgressData, + DigmathonProgressProps +} from "./types"; + +const REQUIRED_COUNT_OF_FOUND_ISSUES = 3; + +const getIsFound = ( + data: DigmathonProgressData | undefined, + type: InsightType +) => { + if (!data) { + return false; + } + + switch (type) { + case InsightType.EndpointQueryOptimizationV2: + case InsightType.EndpointQueryOptimization: + case InsightType.SpanQueryOptimization: + return Boolean( + data.insights.find( + (x) => + [ + InsightType.EndpointQueryOptimizationV2, + InsightType.EndpointQueryOptimization, + InsightType.SpanQueryOptimization + ].includes(x.type) && x.isFound + ) + ); + case InsightType.EndpointSpanNPlusOne: + case InsightType.EndpointSpaNPlusOne: + case InsightType.SpaNPlusOne: + return Boolean( + data.insights.find( + (x) => + [ + InsightType.EndpointSpanNPlusOne, + InsightType.EndpointSpaNPlusOne, + InsightType.SpaNPlusOne + ].includes(x.type) && x.isFound + ) + ); + case InsightType.EndpointBottleneck: + case InsightType.SpanEndpointBottleneck: + return Boolean( + data.insights.find( + (x) => + [ + InsightType.EndpointBottleneck, + InsightType.SpanEndpointBottleneck + ].includes(x.type) && x.isFound + ) + ); + default: + return Boolean(data.insights.find((x) => x.type === type && x.isFound)); + } +}; + +export const Digmathon = ({ + onGoBack, + isCongratulationsView +}: DigmathonProgressProps) => { + const [data, setData] = useState(); + + useEffect(() => { + window.sendMessageToDigma({ + action: actions.GET_DIGMATHON_PROGRESS_DATA + }); + + const handleSetDigmaProgressData = (data: unknown) => { + setData(data as DigmathonProgressData); + }; + + dispatcher.addActionListener( + actions.SET_DIGMATHON_PROGRESS_DATA, + handleSetDigmaProgressData + ); + + return () => { + dispatcher.removeActionListener( + actions.SET_DIGMATHON_PROGRESS_DATA, + handleSetDigmaProgressData + ); + }; + }, []); + + const handleGoBackButtonClick = () => { + sendUserActionTrackingEvent( + trackingEvents.DIGMATHON_VIEW_BACK_BUTTON_CLICKED + ); + onGoBack(); + }; + + const insights: DigmathonInsightData[] = useMemo( + () => [ + { + type: InsightType.SpanScaling, + data: getDigmathonInsightCardData(InsightType.SpanScaling), + isFound: getIsFound(data, InsightType.SpanScaling) + }, + { + type: InsightType.SpanNexus, + data: getDigmathonInsightCardData(InsightType.SpanNexus), + isFound: getIsFound(data, InsightType.SpanNexus) + }, + { + type: InsightType.SpanQueryOptimization, + data: getDigmathonInsightCardData(InsightType.SpanQueryOptimization), + isFound: getIsFound(data, InsightType.SpanQueryOptimization) + }, + { + type: InsightType.HotSpot, + data: getDigmathonInsightCardData(InsightType.HotSpot), + isFound: getIsFound(data, InsightType.HotSpot) + }, + { + type: InsightType.SpaNPlusOne, + data: getDigmathonInsightCardData(InsightType.SpaNPlusOne), + isFound: getIsFound(data, InsightType.SpaNPlusOne) + }, + { + type: InsightType.EndpointSessionInView, + data: getDigmathonInsightCardData(InsightType.EndpointSessionInView), + isFound: getIsFound(data, InsightType.EndpointSessionInView) + }, + { + type: InsightType.SpanUsages, + data: getDigmathonInsightCardData(InsightType.SpanUsages), + isFound: getIsFound(data, InsightType.SpanUsages) + }, + { + type: InsightType.EndpointHighNumberOfQueries, + data: getDigmathonInsightCardData( + InsightType.EndpointHighNumberOfQueries + ), + isFound: getIsFound(data, InsightType.EndpointHighNumberOfQueries) + }, + { + type: InsightType.EndpointBottleneck, + data: getDigmathonInsightCardData(InsightType.EndpointBottleneck), + isFound: getIsFound(data, InsightType.EndpointBottleneck) + } + ], + [data] + ); + + const foundIssuesCount = insights.filter((x) => x.isFound).length; + + return ( + + + + + Back + + + Digmathon + + {foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES || + isCongratulationsView ? ( + + ) : ( + + )} + + ); +}; diff --git a/src/components/RecentActivity/Digmathon/mockData.ts b/src/components/RecentActivity/Digmathon/mockData.ts new file mode 100644 index 000000000..c90b2c899 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/mockData.ts @@ -0,0 +1,21 @@ +import { InsightType } from "../../Insights/types"; +import { DigmathonProgressData } from "./types"; + +export const mockedDigmathonProgressData: DigmathonProgressData = { + insights: [ + { type: InsightType.SpanScaling, isFound: true }, + { type: InsightType.SpanNexus, isFound: false }, + { type: InsightType.EndpointQueryOptimizationV2, isFound: false }, + { type: InsightType.EndpointQueryOptimization, isFound: false }, + { type: InsightType.SpanQueryOptimization, isFound: false }, + { type: InsightType.HotSpot, isFound: false }, + { type: InsightType.EndpointSpanNPlusOne, isFound: false }, + { type: InsightType.EndpointSpaNPlusOne, isFound: false }, + { type: InsightType.SpaNPlusOne, isFound: false }, + { type: InsightType.EndpointSessionInView, isFound: false }, + { type: InsightType.SpanUsages, isFound: false }, + { type: InsightType.EndpointHighNumberOfQueries, isFound: false }, + { type: InsightType.EndpointBottleneck, isFound: false }, + { type: InsightType.SpanEndpointBottleneck, isFound: false } + ] +}; diff --git a/src/components/RecentActivity/Digmathon/styles.ts b/src/components/RecentActivity/Digmathon/styles.ts new file mode 100644 index 000000000..2e099e6e9 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/styles.ts @@ -0,0 +1,59 @@ +import styled from "styled-components"; +import { + subscriptRegularTypography, + subscriptSemiboldTypography +} from "../../common/App/typographies"; + +export const Container = styled.div` + display: flex; + flex-direction: column; + background: ${({ theme }) => theme.colors.v3.surface.primary}; + min-height: 100%; +`; + +export const Header = styled.div` + display: flex; + height: 44px; + align-items: center; + background: ${({ theme }) => theme.colors.v3.surface.sidePanelHeader}; + box-shadow: 0 9px 24px 0 rgb(0 0 0 / 30%); + flex-shrink: 0; + gap: 12px; + padding: 0 12px; +`; + +export const BackButton = styled.button` + ${subscriptRegularTypography} + + font-family: inherit; + color: ${({ theme }) => theme.colors.v3.text.tertiary}; + border: none; + background: none; + padding: 0; + display: flex; + gap: 4px; + align-items: center; + cursor: pointer; +`; + +export const Divider = styled.div` + margin-right: 4px; + border-radius: 1px; + width: 1px; + height: 13px; + background: ${({ theme }) => theme.colors.v3.stroke.primary}; +`; + +export const HeaderTitle = styled.span` + ${subscriptSemiboldTypography} + + padding-left: 4px; + color: ${({ theme }) => theme.colors.v3.text.primary}; +`; + +export const ContentContainer = styled.div` + display: flex; + flex-direction: column; + overflow: auto; + height: 100%; +`; diff --git a/src/components/RecentActivity/Digmathon/types.ts b/src/components/RecentActivity/Digmathon/types.ts new file mode 100644 index 000000000..b5504dc04 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/types.ts @@ -0,0 +1,22 @@ +import { InsightType } from "../../Insights/types"; + +export interface DigmathonProgressProps { + onGoBack: () => void; + isCongratulationsView: boolean; +} + +export interface DigmathonProgressData { + insights: { type: InsightType; isFound: boolean }[]; +} + +export interface DigmathonInsightCardData { + title: string; + description: string; + illustration: JSX.Element; +} + +export interface DigmathonInsightData { + type: InsightType; + data: DigmathonInsightCardData | undefined; + isFound: boolean; +} diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index 71ee6aa96..7ddb9ad71 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -170,6 +170,9 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { }; const handleTroubleshootingClick = () => { + sendUserActionTrackingEvent(trackingEvents.KEBAB_MENU_ITEM_CLICKED, { + item: "Troubleshooting" + }); window.sendMessageToDigma({ action: globalActions.OPEN_TROUBLESHOOTING_GUIDE }); @@ -177,10 +180,20 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { }; const handleSlackLinkClick = () => { + sendUserActionTrackingEvent(trackingEvents.KEBAB_MENU_ITEM_CLICKED, { + item: "Digma Channel" + }); openURLInDefaultBrowser(SLACK_WORKSPACE_URL); setIsKebabMenuOpen(false); }; + const handleDigmathonModeMenuItemClick = () => { + sendUserActionTrackingEvent(trackingEvents.KEBAB_MENU_ITEM_CLICKED, { + item: "Digmathon mode" + }); + props.onDigmathonModeButtonClick(); + }; + return ( { onOpenChange={setIsKebabMenuOpen} isOpen={isKebabMenuOpen} content={ - + { label: "Digma Channel", icon: , onClick: handleSlackLinkClick - } + }, + ...(config.isDigmathonModeEnabled + ? [ + { + id: "digmathon", + label: "Digmathon mode", + icon: , + onClick: handleDigmathonModeMenuItemClick + } + ] + : []) ]} /> diff --git a/src/components/RecentActivity/EnvironmentPanel/types.ts b/src/components/RecentActivity/EnvironmentPanel/types.ts index 94f7682a8..87985d3b8 100644 --- a/src/components/RecentActivity/EnvironmentPanel/types.ts +++ b/src/components/RecentActivity/EnvironmentPanel/types.ts @@ -6,6 +6,7 @@ export interface EnvironmentPanelProps { onEnvironmentSelect: (environment: ExtendedEnvironment) => void; onEnvironmentAdd: (environment: string) => void; onEnvironmentDelete: (environment: string) => void; + onDigmathonModeButtonClick: () => void; } export type ViewMode = "table" | "list"; diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index 5864b156f..9380b24f8 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -673,3 +673,12 @@ export const OpenRegistrationDialog: Story = { }); } }; + +export const OpenCongratulationsDigmathonView: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW + }); + } +}; diff --git a/src/components/RecentActivity/actions.ts b/src/components/RecentActivity/actions.ts index 57125d570..6f9764d21 100644 --- a/src/components/RecentActivity/actions.ts +++ b/src/components/RecentActivity/actions.ts @@ -19,5 +19,8 @@ export const actions = addPrefix(ACTION_PREFIX, { SET_REMOTE_ENVIRONMENT_CONNECTION_CHECK_RESULT: "SET_REMOTE_ENVIRONMENT_CONNECTION_CHECK_RESULT", FINISH_ORG_DIGMA_SETUP: "FINISH_ORG_DIGMA_SETUP", - OPEN_REGISTRATION_DIALOG: "OPEN_REGISTRATION_DIALOG" + OPEN_REGISTRATION_DIALOG: "OPEN_REGISTRATION_DIALOG", + GET_DIGMATHON_PROGRESS_DATA: "GET_DIGMATHON_PROGRESS_DATA", + SET_DIGMATHON_PROGRESS_DATA: "SET_DIGMATHON_PROGRESS_DATA", + OPEN_DIGMATHON_CONGRATULATIONS_VIEW: "OPEN_DIGMATHON_CONGRATULATIONS_VIEW" }); diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index 544095d36..79aaf31e1 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -18,6 +18,7 @@ import { DigmaLogoFlatIcon } from "../common/icons/DigmaLogoFlatIcon"; import { ListIcon } from "../common/icons/ListIcon"; import { TableIcon } from "../common/icons/TableIcon"; import { DeleteEnvironmentConfirmation } from "./DeleteEnvironmentConfirmation"; +import { Digmathon } from "./Digmathon"; import { EnvironmentInstructionsPanel } from "./EnvironmentInstructionsPanel"; import { EnvironmentPanel } from "./EnvironmentPanel"; import { ViewMode } from "./EnvironmentPanel/types"; @@ -99,6 +100,11 @@ export const RecentActivity = (props: RecentActivityProps) => { config.userRegistrationEmail ); const previousEnvironment = usePrevious(config.environment); + const [isDigmathonMode, setIsDigmathonMode] = useState(false); + const [ + isDigmathonCongratulationsViewVisible, + setIsDigmathonCongratulationsViewVisible + ] = useState(false); const { observe, entry } = useDimensions(); const environmentActivities = useMemo( @@ -150,12 +156,24 @@ export const RecentActivity = (props: RecentActivityProps) => { setIsRegistrationPopupVisible(true); }; + const handleOpenCongratulationsDigmathonView = () => { + if (!config.userRegistrationEmail) { + setIsRegistrationPopupVisible(true); + } + setIsDigmathonMode(true); + setIsDigmathonCongratulationsViewVisible(true); + }; + dispatcher.addActionListener(actions.SET_DATA, handleRecentActivityData); dispatcher.addActionListener(actions.SET_LIVE_DATA, handleLiveData); dispatcher.addActionListener( actions.OPEN_REGISTRATION_DIALOG, handleOpenRegistrationDialog ); + dispatcher.addActionListener( + actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW, + handleOpenCongratulationsDigmathonView + ); return () => { dispatcher.removeActionListener( @@ -167,6 +185,10 @@ export const RecentActivity = (props: RecentActivityProps) => { actions.OPEN_REGISTRATION_DIALOG, handleOpenRegistrationDialog ); + dispatcher.removeActionListener( + actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW, + handleOpenCongratulationsDigmathonView + ); }; }, []); @@ -400,6 +422,9 @@ export const RecentActivity = (props: RecentActivityProps) => { const handleRegistrationDialogClose = () => { setIsRegistrationPopupVisible(false); + if (isDigmathonMode) { + setIsDigmathonMode(false); + } }; const handleOverlayKeyDown = (e: KeyboardEvent) => { @@ -409,6 +434,18 @@ export const RecentActivity = (props: RecentActivityProps) => { } }; + const handleDigmathonModeButtonClick = () => { + if (!config.userRegistrationEmail) { + setIsRegistrationPopupVisible(true); + } + setIsDigmathonMode(true); + }; + + const handleDigmathonGoBack = () => { + setIsDigmathonMode(false); + setIsDigmathonCongratulationsViewVisible(false); + }; + const renderContent = () => { if (selectedEnvironment?.isPending) { switch (selectedEnvironment.type) { @@ -466,45 +503,53 @@ export const RecentActivity = (props: RecentActivityProps) => { return ( - - - {/* + {config.isDigmathonModeEnabled && isDigmathonMode ? ( + + ) : ( + + + {/* */} - - - - {!selectedEnvironment?.isPending && ( - - Recent Activity - - - )} - {!config.isObservabilityEnabled && } - - - - {renderContent()} - - - - {liveData && ( - - - - )} - - + + + + {!selectedEnvironment?.isPending && ( + + Recent Activity + + + )} + {!config.isObservabilityEnabled && } + + + + {renderContent()} + + + + {liveData && ( + + + + )} + + + )} {environmentToDelete && ( theme.colors.recentActivity.background}; diff --git a/src/components/RecentActivity/tracking.ts b/src/components/RecentActivity/tracking.ts index 09756fba0..b42aa4cfa 100644 --- a/src/components/RecentActivity/tracking.ts +++ b/src/components/RecentActivity/tracking.ts @@ -10,7 +10,12 @@ export const trackingEvents = addPrefix( ENVIRONMENT_TYPE_BUTTON_CLICKED: "environment type button clicked", CHECK_CONNECTION_RESULT_RECEIVED: "check connection result received", FINISH_BUTTON_CLICKED: "finish button clicked", - OBSERVABILITY_TOGGLE_SWITCHED: "observability toggle switched" + OBSERVABILITY_TOGGLE_SWITCHED: "observability toggle switched", + KEBAB_MENU_ITEM_CLICKED: "kebab menu item clicked", + DIGMATHON_VIEW_BACK_BUTTON_CLICKED: "digmathon view back button clicked", + DIGMATHON_VIEW_CONTACT_LINK_CLICKED: "digmathon view contact link clicked", + DIGMATHON_PROGRESS_VIEWED: "digmathon progress viewed", + DIGMATHON_CONGRATULATIONS_VIEWED: "digmathon congratulations viewed" }, " " ); diff --git a/src/components/common/App/ConfigContext.ts b/src/components/common/App/ConfigContext.ts index 01aeb6906..c9c259135 100644 --- a/src/components/common/App/ConfigContext.ts +++ b/src/components/common/App/ConfigContext.ts @@ -28,7 +28,11 @@ export const initialState = { scope: undefined, isMicrometerProject: window.isMicrometerProject === true, state: undefined, - insightStats: undefined + insightStats: undefined, + digmathonProductKey: isString(window.digmathonProductKey) + ? window.digmathonProductKey + : "", + isDigmathonModeEnabled: window.isDigmathonModeEnabled === true }; export const ConfigContext = createContext(initialState); diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts index dceae5331..731a135f0 100644 --- a/src/components/common/App/types.ts +++ b/src/components/common/App/types.ts @@ -103,6 +103,8 @@ export interface ConfigContextData { isMicrometerProject: boolean; state?: GlobalState; insightStats?: InsightStats; + digmathonProductKey: string; + isDigmathonModeEnabled: boolean; } export interface InsightStats { diff --git a/src/components/common/App/typographies.ts b/src/components/common/App/typographies.ts index 1f2035b6e..73dde4e97 100644 --- a/src/components/common/App/typographies.ts +++ b/src/components/common/App/typographies.ts @@ -65,6 +65,12 @@ export const caption1RegularTypography = css` line-height: ${typographies.captionOne.lineHeight}px; `; +export const caption1BoldTypography = css` + font-size: ${typographies.captionOne.fontSize}px; + font-weight: ${typographies.captionOne.fontWeight.bold}; + line-height: ${typographies.captionOne.lineHeight}px; +`; + export const caption2RegularTypography = css` font-size: ${typographies.captionTwo.fontSize}px; font-weight: ${typographies.captionTwo.fontWeight.regular}; @@ -77,6 +83,18 @@ export const footnoteRegularTypography = css` line-height: ${typographies.footNote.lineHeight}px; `; +export const footnoteMediumTypography = css` + font-size: ${typographies.footNote.fontSize}px; + font-weight: ${typographies.footNote.fontWeight.medium}; + line-height: ${typographies.footNote.lineHeight}px; +`; + +export const footnoteBoldTypography = css` + font-size: ${typographies.footNote.fontSize}px; + font-weight: ${typographies.footNote.fontWeight.bold}; + line-height: ${typographies.footNote.lineHeight}px; +`; + export const subscriptRegularTypography = css` font-size: ${typographies.subscript.fontSize}px; font-weight: ${typographies.subscript.fontWeight.regular}; @@ -101,14 +119,20 @@ export const bodyRegularTypography = css` line-height: ${typographies.body.lineHeight}px; `; -export const bodySemiboldTypography = css` +export const bodyMediumTypography = css` font-size: ${typographies.body.fontSize}px; font-weight: ${typographies.body.fontWeight.medium}; line-height: ${typographies.body.lineHeight}px; `; -export const footnoteBoldTypography = css` - font-size: ${typographies.footNote.fontSize}px; - font-weight: ${typographies.footNote.fontWeight.bold}; - line-height: ${typographies.footNote.lineHeight}px; +export const bodySemiboldTypography = css` + font-size: ${typographies.body.fontSize}px; + font-weight: ${typographies.body.fontWeight.semibold}; + line-height: ${typographies.body.lineHeight}px; +`; + +export const bodyBoldTypography = css` + font-size: ${typographies.body.fontSize}px; + font-weight: ${typographies.body.fontWeight.bold}; + line-height: ${typographies.body.lineHeight}px; `; diff --git a/src/components/common/icons/16px/KeyIcon.tsx b/src/components/common/icons/16px/KeyIcon.tsx new file mode 100644 index 000000000..09d814277 --- /dev/null +++ b/src/components/common/icons/16px/KeyIcon.tsx @@ -0,0 +1,26 @@ +import React from "react"; +import { useIconProps } from "../hooks"; +import { IconProps } from "../types"; + +const KeyIconIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + ); +}; + +export const KeyIcon = React.memo(KeyIconIconComponent); diff --git a/src/components/common/icons/20px/CheckmarkCircleIcon.tsx b/src/components/common/icons/20px/CheckmarkCircleIcon.tsx new file mode 100644 index 000000000..683854b7c --- /dev/null +++ b/src/components/common/icons/20px/CheckmarkCircleIcon.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { useIconProps } from "../hooks"; +import { IconProps } from "../types"; + +const CheckmarkCircleIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + + + + + + + + + ); +}; + +export const CheckmarkCircleIcon = React.memo(CheckmarkCircleIconComponent); diff --git a/src/globals.d.ts b/src/globals.d.ts index ea8026560..9f91409a3 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -55,6 +55,8 @@ declare global { testsRefreshInterval?: unknown; wizardSkipInstallationStep?: unknown; wizardFirstLaunch?: unknown; + digmathonProductKey?: unknown; + isDigmathonModeEnabled?: unknown; } } diff --git a/src/utils/actions/openURLInDefaultBrowser.ts b/src/utils/actions/openURLInDefaultBrowser.ts index 72e3cd573..e1f8c9986 100644 --- a/src/utils/actions/openURLInDefaultBrowser.ts +++ b/src/utils/actions/openURLInDefaultBrowser.ts @@ -1,8 +1,13 @@ import { actions } from "../../actions"; import { isString } from "../../typeGuards/isString"; +interface OpenURLInDefaultBrowserPayload { + url: string; + title?: string; +} + export const openURLInDefaultBrowser = (url: string, title?: string) => { - window.sendMessageToDigma({ + window.sendMessageToDigma({ action: actions.OPEN_URL_IN_DEFAULT_BROWSER, payload: { url, diff --git a/webpackEntries.ts b/webpackEntries.ts index 0c8776af6..50ef32efe 100644 --- a/webpackEntries.ts +++ b/webpackEntries.ts @@ -1,6 +1,7 @@ import path from "path"; export const entries: AppEntries = { + // deprecated assets: { entry: path.resolve(__dirname, "./src/containers/Assets/index.tsx"), environmentVariables: [ @@ -17,6 +18,7 @@ export const entries: AppEntries = { entry: path.resolve(__dirname, "./src/containers/Documentation/index.tsx"), environmentVariables: ["documentationPage"] }, + // deprecated insights: { entry: path.resolve(__dirname, "./src/containers/Insights/index.tsx"), environmentVariables: ["insightsRefreshInterval"] @@ -41,6 +43,7 @@ export const entries: AppEntries = { navigation: { entry: path.resolve(__dirname, "./src/containers/Navigation/index.tsx") }, + // deprecated notifications: { entry: path.resolve(__dirname, "./src/containers/Notifications/index.tsx"), environmentVariables: [ @@ -56,6 +59,7 @@ export const entries: AppEntries = { "recentActivityIsEnvironmentManagementEnabled" ] }, + // deprecated tests: { entry: path.resolve(__dirname, "./src/containers/Tests/index.tsx"), environmentVariables: ["testsRefreshInterval"] From 8bcb401c7bea9903ff389a9a89c161299dfa5257 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sat, 6 Apr 2024 04:16:40 +0200 Subject: [PATCH 02/14] Add global environment variables --- assets/index.ejs | 3 ++ src/actions.ts | 5 ++- src/components/InstallationWizard/index.tsx | 2 +- .../RecentActivity/EnvironmentPanel/index.tsx | 2 +- src/components/RecentActivity/index.tsx | 2 +- src/components/common/App/ConfigContext.ts | 7 ++-- src/components/common/App/index.tsx | 42 +++++++++++++++++++ src/components/common/App/types.ts | 2 +- src/globals.d.ts | 3 +- 9 files changed, 58 insertions(+), 10 deletions(-) diff --git a/assets/index.ejs b/assets/index.ejs index f47fa0bfe..8f82135e9 100644 --- a/assets/index.ejs +++ b/assets/index.ejs @@ -32,6 +32,9 @@ window.isDockerInstalled; window.isDockerComposeInstalled; window.isMicrometerProject; + window.productKey; + window.userId; + window.isDigmathonModeEnabled; <% for (var i = 0; i < environmentVariables.length; i++) { %> window.<%= environmentVariables[i] %>;<% } %> diff --git a/src/actions.ts b/src/actions.ts index 2795a686f..eb0a90cb2 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -41,5 +41,8 @@ export const actions = addPrefix(ACTION_PREFIX, { CHANGE_VIEW: "CHANGE_VIEW", GET_INSIGHT_STATS: "GET_INSIGHT_STATS", SET_INSIGHT_STATS: "SET_INSIGHT_STATS", - CHANGE_ENVIRONMENT: "CHANGE_ENVIRONMENT" + CHANGE_ENVIRONMENT: "CHANGE_ENVIRONMENT", + SET_DIGMATHON_MODE: "SET_DIGMATHON_MODE", + SET_PRODUCT_KEY: "SET_PRODUCT_KEY", + SET_USER_ID: "SET_USER_ID" }); diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index ae2aac487..cf464df57 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -86,7 +86,7 @@ export const InstallationWizard = () => { ); const [isEmailValidating, setIsEmailValidating] = useState(false); const debouncedEmail = useDebounce(email, 1000); - const [productKey, setProductKey] = useState(config.digmathonProductKey); + const [productKey, setProductKey] = useState(config.productKey); // const [ // isDigmaCloudNotificationCheckboxChecked, // setIsDigmaCloudNotificationCheckboxChecked diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index 7ddb9ad71..8fae0baf2 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -237,7 +237,7 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { icon: , onClick: handleSlackLinkClick }, - ...(config.isDigmathonModeEnabled + ...(config.productKey ? [ { id: "digmathon", diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index 79aaf31e1..9e21c704e 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -503,7 +503,7 @@ export const RecentActivity = (props: RecentActivityProps) => { return ( - {config.isDigmathonModeEnabled && isDigmathonMode ? ( + {isDigmathonMode ? ( (initialState); diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx index 12ca1fef9..8bc3a9332 100644 --- a/src/components/common/App/index.tsx +++ b/src/components/common/App/index.tsx @@ -236,6 +236,33 @@ export const App = (props: AppProps) => { })); }; + const handleSetIsDigmathonModeEnabled = (data: unknown) => { + if (isObject(data) && isBoolean(data.isDigmathonModeEnabled)) { + setConfig((config) => ({ + ...config, + isDigmathonModeEnabled: data.isDigmathonModeEnabled as boolean + })); + } + }; + + const handleSetProductKey = (data: unknown) => { + if (isObject(data) && isString(data.productKey)) { + setConfig((config) => ({ + ...config, + productKey: data.productKey as string + })); + } + }; + + const handleSetUserId = (data: unknown) => { + if (isObject(data) && isString(data.userId)) { + setConfig((config) => ({ + ...config, + userId: data.userId as string + })); + } + }; + dispatcher.addActionListener(actions.SET_THEME, handleSetTheme); dispatcher.addActionListener(actions.SET_MAIN_FONT, handleSetMainFont); dispatcher.addActionListener(actions.SET_CODE_FONT, handleSetCodeFont); @@ -295,6 +322,12 @@ export const App = (props: AppProps) => { actions.SET_INSIGHT_STATS, handleSetInsightStats ); + dispatcher.addActionListener( + actions.SET_DIGMATHON_MODE, + handleSetIsDigmathonModeEnabled + ); + dispatcher.addActionListener(actions.SET_PRODUCT_KEY, handleSetProductKey); + dispatcher.addActionListener(actions.SET_USER_ID, handleSetUserId); return () => { dispatcher.removeActionListener(actions.SET_THEME, handleSetTheme); @@ -362,6 +395,15 @@ export const App = (props: AppProps) => { actions.SET_INSIGHT_STATS, handleSetInsightStats ); + dispatcher.removeActionListener( + actions.SET_DIGMATHON_MODE, + handleSetIsDigmathonModeEnabled + ); + dispatcher.removeActionListener( + actions.SET_PRODUCT_KEY, + handleSetProductKey + ); + dispatcher.removeActionListener(actions.SET_USER_ID, handleSetUserId); }; }, []); diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts index 731a135f0..aad892c82 100644 --- a/src/components/common/App/types.ts +++ b/src/components/common/App/types.ts @@ -103,7 +103,7 @@ export interface ConfigContextData { isMicrometerProject: boolean; state?: GlobalState; insightStats?: InsightStats; - digmathonProductKey: string; + productKey: string; isDigmathonModeEnabled: boolean; } diff --git a/src/globals.d.ts b/src/globals.d.ts index 9f91409a3..6eaab45a9 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -55,8 +55,9 @@ declare global { testsRefreshInterval?: unknown; wizardSkipInstallationStep?: unknown; wizardFirstLaunch?: unknown; - digmathonProductKey?: unknown; + productKey?: unknown; isDigmathonModeEnabled?: unknown; + userId?: unknown; } } From dcb420b3fd35450a76d1077654343974765eefb3 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sun, 7 Apr 2024 17:50:16 +0200 Subject: [PATCH 03/14] Fix Digmathon mode button --- src/components/RecentActivity/EnvironmentPanel/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index 8fae0baf2..f488f997a 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -237,7 +237,7 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { icon: , onClick: handleSlackLinkClick }, - ...(config.productKey + ...(config.isDigmathonModeEnabled && config.productKey ? [ { id: "digmathon", From 4c404e44369689fe658526b8927f864ad93b1263 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sun, 7 Apr 2024 19:27:40 +0200 Subject: [PATCH 04/14] Fix insight icon size --- .../insightCards/common/InsightCard/InsightHeader/index.tsx | 4 +++- .../insightCards/common/InsightCard/InsightHeader/styles.ts | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx index e4194a387..4cfcc1d77 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/index.tsx @@ -96,7 +96,9 @@ export const InsightHeader = ({ + + + } /> diff --git a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/styles.ts b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/styles.ts index 6c8b5bf2e..c9ac3bcb5 100644 --- a/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/styles.ts +++ b/src/components/Insights/InsightsCatalog/InsightsPage/insightCards/common/InsightCard/InsightHeader/styles.ts @@ -51,3 +51,7 @@ export const Description = styled.div` display: flex; color: ${({ theme }) => theme.colors.v3.text.secondary}; `; + +export const InsightIconContainer = styled.div` + display: flex; +`; From 3a1236b19909a696083c59a58e95180aef86f37f Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Sun, 7 Apr 2024 22:59:20 +0200 Subject: [PATCH 05/14] Update Recent Activity stories --- .../RecentActivity/RecentActivity.stories.tsx | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index 9380b24f8..ac254ae55 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -1,5 +1,6 @@ import { Meta, StoryObj } from "@storybook/react"; import { RecentActivity } from "."; +import { actions as globalActions } from "../../actions"; import { mockData as liveData } from "./LiveView/mockData"; import { actions } from "./actions"; import { RecentActivityData } from "./types"; @@ -682,3 +683,22 @@ export const OpenCongratulationsDigmathonView: Story = { }); } }; + +export const EnableDigmathonMode: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: globalActions.SET_DIGMATHON_MODE, + payload: { + isDigmathonModeEnabled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_PRODUCT_KEY, + payload: { + productKey: "digmathon" + } + }); + } +}; From a96b6c2b8ac2e6b9caaea665ff50987b2c8ed7a4 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 03:25:13 +0200 Subject: [PATCH 06/14] Update Digmathon email message --- .../Digmathon/CongratulationsView/index.tsx | 29 ++++++++++++++----- .../Digmathon/CongratulationsView/types.ts | 5 ++++ .../RecentActivity/Digmathon/index.tsx | 18 +++++++++--- .../RecentActivity/Digmathon/styles.ts | 7 +++++ .../RecentActivity/RecentActivity.stories.tsx | 14 +++++++++ src/components/common/App/types.ts | 1 + 6 files changed, 63 insertions(+), 11 deletions(-) create mode 100644 src/components/RecentActivity/Digmathon/CongratulationsView/types.ts diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx index b6352ae1f..f416d0411 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -1,22 +1,37 @@ -import { useEffect } from "react"; +import { useContext, useEffect } from "react"; import { openURLInDefaultBrowser } from "../../../../utils/actions/openURLInDefaultBrowser"; import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; +import { ConfigContext } from "../../../common/App/ConfigContext"; import { CheckmarkCircleIcon } from "../../../common/icons/12px/CheckmarkCircleIcon"; import { trackingEvents } from "../../tracking"; import * as s from "./styles"; +import { CongratulationsViewProps } from "./types"; -const EMAIL_ADDRESS = "contact@digma.ai"; -const EMAIL_SUBJECT = "Digmathon"; +const EMAIL_ADDRESS = "digmathon@digma.ai"; + +export const CongratulationsView = ({ insights }: CongratulationsViewProps) => { + const config = useContext(ConfigContext); -export const CongratulationsView = () => { const handleContactLinkClick = () => { sendUserActionTrackingEvent( trackingEvents.DIGMATHON_VIEW_CONTACT_LINK_CLICKED ); - const body = ""; - const url = `mailto:${EMAIL_ADDRESS}?subject=${EMAIL_SUBJECT}&body=${body}`; + const userId = config.userId || config.userRegistrationEmail || ""; + const subject = `Digmathon Challenge Completed! [${userId}]`; + + const foundInsights = insights + .filter((x) => x.isFound) + .map((x) => x.data?.title || x.type) + .join(", "); + const body = [ + "Insights found:", + foundInsights, + "Please send back the reward to this email!" + ].join("%0D%0A%0D%0A"); + + const url = `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`; openURLInDefaultBrowser(url); }; @@ -36,7 +51,7 @@ export const CongratulationsView = () => { subject line. - contact@digma.ai + {EMAIL_ADDRESS} ); diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts new file mode 100644 index 000000000..92f5a7d18 --- /dev/null +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts @@ -0,0 +1,5 @@ +import { DigmathonInsightData } from "../types"; + +export interface CongratulationsViewProps { + insights: DigmathonInsightData[]; +} diff --git a/src/components/RecentActivity/Digmathon/index.tsx b/src/components/RecentActivity/Digmathon/index.tsx index e35ae17df..352b20098 100644 --- a/src/components/RecentActivity/Digmathon/index.tsx +++ b/src/components/RecentActivity/Digmathon/index.tsx @@ -2,6 +2,7 @@ import { useEffect, useMemo, useState } from "react"; import { dispatcher } from "../../../dispatcher"; import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent"; import { InsightType } from "../../Insights/types"; +import { NewCircleLoader } from "../../common/NewCircleLoader"; import { ChevronIcon } from "../../common/icons/16px/ChevronIcon"; import { Direction } from "../../common/icons/types"; import { actions } from "../actions"; @@ -159,6 +160,14 @@ export const Digmathon = ({ const foundIssuesCount = insights.filter((x) => x.isFound).length; + const renderContent = () => + foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES || + isCongratulationsView ? ( + + ) : ( + + ); + return ( @@ -173,11 +182,12 @@ export const Digmathon = ({ Digmathon - {foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES || - isCongratulationsView ? ( - + {data ? ( + renderContent() ) : ( - + + + )} ); diff --git a/src/components/RecentActivity/Digmathon/styles.ts b/src/components/RecentActivity/Digmathon/styles.ts index 2e099e6e9..7a4302559 100644 --- a/src/components/RecentActivity/Digmathon/styles.ts +++ b/src/components/RecentActivity/Digmathon/styles.ts @@ -57,3 +57,10 @@ export const ContentContainer = styled.div` overflow: auto; height: 100%; `; + +export const LoaderContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-grow: 1; +`; diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index ac254ae55..03b336607 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -1,6 +1,7 @@ import { Meta, StoryObj } from "@storybook/react"; import { RecentActivity } from "."; import { actions as globalActions } from "../../actions"; +import { mockedDigmathonProgressData } from "./Digmathon/mockData"; import { mockData as liveData } from "./LiveView/mockData"; import { actions } from "./actions"; import { RecentActivityData } from "./types"; @@ -681,6 +682,19 @@ export const OpenCongratulationsDigmathonView: Story = { type: "digma", action: actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW }); + + setTimeout(() => { + window.postMessage({ + type: "digma", + action: actions.SET_DIGMATHON_PROGRESS_DATA, + payload: { + insights: mockedDigmathonProgressData.insights.map((x) => ({ + ...x, + isFound: true + })) + } + }); + }, 0); } }; diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts index aad892c82..fbe99e2ff 100644 --- a/src/components/common/App/types.ts +++ b/src/components/common/App/types.ts @@ -105,6 +105,7 @@ export interface ConfigContextData { insightStats?: InsightStats; productKey: string; isDigmathonModeEnabled: boolean; + userId: string; } export interface InsightStats { From 9fcec5d8b3e519cfe15b3ccdc2e655b101b51675 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 09:44:33 +0200 Subject: [PATCH 07/14] Remove registration check for Digmathon in Recent Activity --- .../InstallationWizard/FinishStep/index.tsx | 9 +- .../InstallationWizard.stories.tsx | 113 +++++++++++++++++- src/components/InstallationWizard/index.tsx | 6 +- .../Digmathon/CongratulationsView/index.tsx | 6 +- .../RecentActivity/RecentActivity.stories.tsx | 38 +++--- src/components/RecentActivity/index.tsx | 9 -- src/components/RecentActivity/styles.ts | 1 - 7 files changed, 144 insertions(+), 38 deletions(-) diff --git a/src/components/InstallationWizard/FinishStep/index.tsx b/src/components/InstallationWizard/FinishStep/index.tsx index 61e08ed5b..a89d4c36c 100644 --- a/src/components/InstallationWizard/FinishStep/index.tsx +++ b/src/components/InstallationWizard/FinishStep/index.tsx @@ -75,10 +75,10 @@ export const FinishStep = (props: FinishStepProps) => { {config.isDigmathonModeEnabled && ( - Code product key(optional) + Product key(optional) - If you’ve received a product key, please enter it here + If you've received a product key, please enter it here { )} - Stay up to date(optional) + Stay up to date + {!props.productKey && ( + (optional) + )} Enter your E-mail address to be the first to get Digma updates diff --git a/src/components/InstallationWizard/InstallationWizard.stories.tsx b/src/components/InstallationWizard/InstallationWizard.stories.tsx index 731d36360..e1aac04e9 100644 --- a/src/components/InstallationWizard/InstallationWizard.stories.tsx +++ b/src/components/InstallationWizard/InstallationWizard.stories.tsx @@ -1,6 +1,6 @@ import { Meta, StoryObj } from "@storybook/react"; - import { InstallationWizard } from "."; +import { actions as globalActions } from "../../actions"; // More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction const meta: Meta = { @@ -17,4 +17,113 @@ export default meta; type Story = StoryObj; // More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args -export const Default: Story = {}; +export const NoDigmaInstalled: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_INSTALLED, + payload: { + isDockerInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_COMPOSE_INSTALLED, + payload: { + isDockerComposeInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DIGMA_ENGINE_INSTALLED, + payload: { + isDigmaEngineInstalled: false + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_DIGMA_STATUS, + payload: { + connection: { + type: null, + status: false + }, + runningDigmaInstances: [] + } + }); + } +}; + +export const DigmaEngineInstalledAndRunning: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_INSTALLED, + payload: { + isDockerInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_COMPOSE_INSTALLED, + payload: { + isDockerComposeInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DIGMA_ENGINE_INSTALLED, + payload: { + isDigmaEngineInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_DIGMA_STATUS, + payload: { + connection: { + type: "local", + status: true + }, + runningDigmaInstances: ["localEngine"] + } + }); + } +}; + +export const DigmaEngineInstalledAndStopped: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_INSTALLED, + payload: { + isDockerInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DOCKER_COMPOSE_INSTALLED, + payload: { + isDockerComposeInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_IS_DIGMA_ENGINE_INSTALLED, + payload: { + isDigmaEngineInstalled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_DIGMA_STATUS, + payload: { + connection: { + type: null, + status: false + }, + runningDigmaInstances: [] + } + }); + } +}; diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index cf464df57..f4a432fd6 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -490,7 +490,11 @@ export const InstallationWizard = () => { Finish diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx index f416d0411..4ed9916b6 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -46,9 +46,9 @@ export const CongratulationsView = ({ insights }: CongratulationsViewProps) => { Congratulations! - You've successfully found all the issues. To claim your prize, - please send us an email with your name and "Digmathon" as the - subject line. + You've successfully reached the Digma insights goal. Please click + the link below to send us an email and claim your reward. Feel free to + keep using Digma locally for free, forever! {EMAIL_ADDRESS} diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index 03b336607..0116d30ca 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -676,6 +676,25 @@ export const OpenRegistrationDialog: Story = { } }; +export const EnableDigmathonMode: Story = { + play: () => { + window.postMessage({ + type: "digma", + action: globalActions.SET_DIGMATHON_MODE, + payload: { + isDigmathonModeEnabled: true + } + }); + window.postMessage({ + type: "digma", + action: globalActions.SET_PRODUCT_KEY, + payload: { + productKey: "digmathon" + } + }); + } +}; + export const OpenCongratulationsDigmathonView: Story = { play: () => { window.postMessage({ @@ -697,22 +716,3 @@ export const OpenCongratulationsDigmathonView: Story = { }, 0); } }; - -export const EnableDigmathonMode: Story = { - play: () => { - window.postMessage({ - type: "digma", - action: globalActions.SET_DIGMATHON_MODE, - payload: { - isDigmathonModeEnabled: true - } - }); - window.postMessage({ - type: "digma", - action: globalActions.SET_PRODUCT_KEY, - payload: { - productKey: "digmathon" - } - }); - } -}; diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index 9e21c704e..e06439815 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -157,9 +157,6 @@ export const RecentActivity = (props: RecentActivityProps) => { }; const handleOpenCongratulationsDigmathonView = () => { - if (!config.userRegistrationEmail) { - setIsRegistrationPopupVisible(true); - } setIsDigmathonMode(true); setIsDigmathonCongratulationsViewVisible(true); }; @@ -422,9 +419,6 @@ export const RecentActivity = (props: RecentActivityProps) => { const handleRegistrationDialogClose = () => { setIsRegistrationPopupVisible(false); - if (isDigmathonMode) { - setIsDigmathonMode(false); - } }; const handleOverlayKeyDown = (e: KeyboardEvent) => { @@ -435,9 +429,6 @@ export const RecentActivity = (props: RecentActivityProps) => { }; const handleDigmathonModeButtonClick = () => { - if (!config.userRegistrationEmail) { - setIsRegistrationPopupVisible(true); - } setIsDigmathonMode(true); }; diff --git a/src/components/RecentActivity/styles.ts b/src/components/RecentActivity/styles.ts index 98838c47a..30b06e9a6 100644 --- a/src/components/RecentActivity/styles.ts +++ b/src/components/RecentActivity/styles.ts @@ -6,7 +6,6 @@ const RECENT_ACTIVITY_MIN_WIDTH = 550; // in pixels export const Container = styled.div` height: 100%; - overflow: hidden; position: relative; background: ${({ theme }) => theme.colors.recentActivity.background}; From 37f2795ff74184bf3e26a4ec4edd80724a4d9044 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 13:36:49 +0200 Subject: [PATCH 08/14] Update API contract --- assets/index.ejs | 1 + src/actions.ts | 4 +- .../Digmathon/CongratulationsView/index.tsx | 4 +- .../Digmathon/CongratulationsView/types.ts | 4 +- .../Digmathon/Digmathon.stories.tsx | 8 +- .../Digmathon/DigmathonInsightCard/types.ts | 2 +- .../Digmathon/ProgressView/index.tsx | 9 +- .../Digmathon/ProgressView/types.ts | 4 +- .../Digmathon/getDigmathonInsightData.tsx | 2 +- .../RecentActivity/Digmathon/index.tsx | 161 ++---------------- .../RecentActivity/Digmathon/mockData.ts | 30 ++-- .../RecentActivity/Digmathon/types.ts | 23 +-- .../RecentActivity/RecentActivity.stories.tsx | 12 +- src/components/RecentActivity/actions.ts | 3 +- src/components/RecentActivity/index.tsx | 42 ++--- src/components/RecentActivity/types.ts | 17 ++ .../useDigmathonProgressData.ts | 155 +++++++++++++++++ src/components/common/App/ConfigContext.ts | 3 +- src/components/common/App/index.tsx | 16 +- src/components/common/App/types.ts | 1 + src/globals.d.ts | 1 + 21 files changed, 262 insertions(+), 240 deletions(-) create mode 100644 src/components/RecentActivity/useDigmathonProgressData.ts diff --git a/assets/index.ejs b/assets/index.ejs index 8f82135e9..e82c4626d 100644 --- a/assets/index.ejs +++ b/assets/index.ejs @@ -35,6 +35,7 @@ window.productKey; window.userId; window.isDigmathonModeEnabled; + window.isDigmathonGameFinished; <% for (var i = 0; i < environmentVariables.length; i++) { %> window.<%= environmentVariables[i] %>;<% } %> diff --git a/src/actions.ts b/src/actions.ts index eb0a90cb2..2c3f83dae 100644 --- a/src/actions.ts +++ b/src/actions.ts @@ -44,5 +44,7 @@ export const actions = addPrefix(ACTION_PREFIX, { CHANGE_ENVIRONMENT: "CHANGE_ENVIRONMENT", SET_DIGMATHON_MODE: "SET_DIGMATHON_MODE", SET_PRODUCT_KEY: "SET_PRODUCT_KEY", - SET_USER_ID: "SET_USER_ID" + SET_USER_ID: "SET_USER_ID", + SET_IS_DIGMATHON_GAME_FINISHED: "SET_IS_DIGMATHON_GAME_FINISHED", + FINISH_DIGMATHON_GAME: "FINISH_DIGMATHON_GAME" }); diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx index 4ed9916b6..18cbdbd62 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -10,7 +10,7 @@ import { CongratulationsViewProps } from "./types"; const EMAIL_ADDRESS = "digmathon@digma.ai"; -export const CongratulationsView = ({ insights }: CongratulationsViewProps) => { +export const CongratulationsView = ({ data }: CongratulationsViewProps) => { const config = useContext(ConfigContext); const handleContactLinkClick = () => { @@ -21,7 +21,7 @@ export const CongratulationsView = ({ insights }: CongratulationsViewProps) => { const userId = config.userId || config.userRegistrationEmail || ""; const subject = `Digmathon Challenge Completed! [${userId}]`; - const foundInsights = insights + const foundInsights = data .filter((x) => x.isFound) .map((x) => x.data?.title || x.type) .join(", "); diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts index 92f5a7d18..9fe5e814b 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts @@ -1,5 +1,5 @@ -import { DigmathonInsightData } from "../types"; +import { DigmathonInsightData } from "../../types"; export interface CongratulationsViewProps { - insights: DigmathonInsightData[]; + data: DigmathonInsightData[]; } diff --git a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx index 074dbe4ba..a3ecc1d2b 100644 --- a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx +++ b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx @@ -39,13 +39,7 @@ export const Congratulations: Story = { window.postMessage({ type: "digma", action: actions.SET_DIGMATHON_PROGRESS_DATA, - payload: { - ...mockedDigmathonProgressData, - insights: mockedDigmathonProgressData.insights.map((x) => ({ - ...x, - isFound: true - })) - } + payload: mockedDigmathonProgressData }); }); }, 0); diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts index 24f8d71b0..9da2fc505 100644 --- a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts +++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/types.ts @@ -1,4 +1,4 @@ -import { DigmathonInsightCardData } from "../types"; +import { DigmathonInsightCardData } from "../../types"; export type DigmathonInsightCardProps = { number: number; diff --git a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx index 102082014..dbf68ac89 100644 --- a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx +++ b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx @@ -5,10 +5,7 @@ import { DigmathonInsightCard } from "../DigmathonInsightCard"; import * as s from "./styles"; import { ProgressViewProps } from "./types"; -export const ProgressView = ({ - insights, - foundIssuesCount -}: ProgressViewProps) => { +export const ProgressView = ({ data, foundIssuesCount }: ProgressViewProps) => { useEffect(() => { sendTrackingEvent(trackingEvents.DIGMATHON_PROGRESS_VIEWED); }, []); @@ -22,11 +19,11 @@ export const ProgressView = ({ {foundIssuesCount} out of{" "} - {insights.length} issues found + {data.length} issues found - {insights.map((x, i) => + {data.map((x, i) => x.data ? ( { - if (!data) { - return false; - } - - switch (type) { - case InsightType.EndpointQueryOptimizationV2: - case InsightType.EndpointQueryOptimization: - case InsightType.SpanQueryOptimization: - return Boolean( - data.insights.find( - (x) => - [ - InsightType.EndpointQueryOptimizationV2, - InsightType.EndpointQueryOptimization, - InsightType.SpanQueryOptimization - ].includes(x.type) && x.isFound - ) - ); - case InsightType.EndpointSpanNPlusOne: - case InsightType.EndpointSpaNPlusOne: - case InsightType.SpaNPlusOne: - return Boolean( - data.insights.find( - (x) => - [ - InsightType.EndpointSpanNPlusOne, - InsightType.EndpointSpaNPlusOne, - InsightType.SpaNPlusOne - ].includes(x.type) && x.isFound - ) - ); - case InsightType.EndpointBottleneck: - case InsightType.SpanEndpointBottleneck: - return Boolean( - data.insights.find( - (x) => - [ - InsightType.EndpointBottleneck, - InsightType.SpanEndpointBottleneck - ].includes(x.type) && x.isFound - ) - ); - default: - return Boolean(data.insights.find((x) => x.type === type && x.isFound)); - } -}; +import { DigmathonProgressProps } from "./types"; export const Digmathon = ({ - onGoBack, - isCongratulationsView + data, + getData, + foundIssuesCount, + isDigmathonCompleted, + onGoBack }: DigmathonProgressProps) => { - const [data, setData] = useState(); - useEffect(() => { - window.sendMessageToDigma({ - action: actions.GET_DIGMATHON_PROGRESS_DATA - }); - - const handleSetDigmaProgressData = (data: unknown) => { - setData(data as DigmathonProgressData); - }; - - dispatcher.addActionListener( - actions.SET_DIGMATHON_PROGRESS_DATA, - handleSetDigmaProgressData - ); - - return () => { - dispatcher.removeActionListener( - actions.SET_DIGMATHON_PROGRESS_DATA, - handleSetDigmaProgressData - ); - }; + getData(); }, []); const handleGoBackButtonClick = () => { @@ -105,67 +28,11 @@ export const Digmathon = ({ onGoBack(); }; - const insights: DigmathonInsightData[] = useMemo( - () => [ - { - type: InsightType.SpanScaling, - data: getDigmathonInsightCardData(InsightType.SpanScaling), - isFound: getIsFound(data, InsightType.SpanScaling) - }, - { - type: InsightType.SpanNexus, - data: getDigmathonInsightCardData(InsightType.SpanNexus), - isFound: getIsFound(data, InsightType.SpanNexus) - }, - { - type: InsightType.SpanQueryOptimization, - data: getDigmathonInsightCardData(InsightType.SpanQueryOptimization), - isFound: getIsFound(data, InsightType.SpanQueryOptimization) - }, - { - type: InsightType.HotSpot, - data: getDigmathonInsightCardData(InsightType.HotSpot), - isFound: getIsFound(data, InsightType.HotSpot) - }, - { - type: InsightType.SpaNPlusOne, - data: getDigmathonInsightCardData(InsightType.SpaNPlusOne), - isFound: getIsFound(data, InsightType.SpaNPlusOne) - }, - { - type: InsightType.EndpointSessionInView, - data: getDigmathonInsightCardData(InsightType.EndpointSessionInView), - isFound: getIsFound(data, InsightType.EndpointSessionInView) - }, - { - type: InsightType.SpanUsages, - data: getDigmathonInsightCardData(InsightType.SpanUsages), - isFound: getIsFound(data, InsightType.SpanUsages) - }, - { - type: InsightType.EndpointHighNumberOfQueries, - data: getDigmathonInsightCardData( - InsightType.EndpointHighNumberOfQueries - ), - isFound: getIsFound(data, InsightType.EndpointHighNumberOfQueries) - }, - { - type: InsightType.EndpointBottleneck, - data: getDigmathonInsightCardData(InsightType.EndpointBottleneck), - isFound: getIsFound(data, InsightType.EndpointBottleneck) - } - ], - [data] - ); - - const foundIssuesCount = insights.filter((x) => x.isFound).length; - - const renderContent = () => - foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES || - isCongratulationsView ? ( - + const renderContent = (data: DigmathonInsightData[]) => + isDigmathonCompleted ? ( + ) : ( - + ); return ( @@ -183,7 +50,7 @@ export const Digmathon = ({ Digmathon {data ? ( - renderContent() + renderContent(data) ) : ( diff --git a/src/components/RecentActivity/Digmathon/mockData.ts b/src/components/RecentActivity/Digmathon/mockData.ts index c90b2c899..6cbc87177 100644 --- a/src/components/RecentActivity/Digmathon/mockData.ts +++ b/src/components/RecentActivity/Digmathon/mockData.ts @@ -1,21 +1,21 @@ import { InsightType } from "../../Insights/types"; -import { DigmathonProgressData } from "./types"; +import { DigmathonProgressData } from "../types"; export const mockedDigmathonProgressData: DigmathonProgressData = { insights: [ - { type: InsightType.SpanScaling, isFound: true }, - { type: InsightType.SpanNexus, isFound: false }, - { type: InsightType.EndpointQueryOptimizationV2, isFound: false }, - { type: InsightType.EndpointQueryOptimization, isFound: false }, - { type: InsightType.SpanQueryOptimization, isFound: false }, - { type: InsightType.HotSpot, isFound: false }, - { type: InsightType.EndpointSpanNPlusOne, isFound: false }, - { type: InsightType.EndpointSpaNPlusOne, isFound: false }, - { type: InsightType.SpaNPlusOne, isFound: false }, - { type: InsightType.EndpointSessionInView, isFound: false }, - { type: InsightType.SpanUsages, isFound: false }, - { type: InsightType.EndpointHighNumberOfQueries, isFound: false }, - { type: InsightType.EndpointBottleneck, isFound: false }, - { type: InsightType.SpanEndpointBottleneck, isFound: false } + InsightType.SpanScaling, + InsightType.SpanNexus, + InsightType.EndpointQueryOptimizationV2, + InsightType.EndpointQueryOptimization, + InsightType.SpanQueryOptimization, + InsightType.HotSpot, + InsightType.EndpointSpanNPlusOne, + InsightType.EndpointSpaNPlusOne, + InsightType.SpaNPlusOne, + InsightType.EndpointSessionInView, + InsightType.SpanUsages, + InsightType.EndpointHighNumberOfQueries, + InsightType.EndpointBottleneck, + InsightType.SpanEndpointBottleneck ] }; diff --git a/src/components/RecentActivity/Digmathon/types.ts b/src/components/RecentActivity/Digmathon/types.ts index b5504dc04..043c130c1 100644 --- a/src/components/RecentActivity/Digmathon/types.ts +++ b/src/components/RecentActivity/Digmathon/types.ts @@ -1,22 +1,9 @@ -import { InsightType } from "../../Insights/types"; +import { DigmathonInsightData } from "../types"; export interface DigmathonProgressProps { + data?: DigmathonInsightData[]; + getData: () => void; + foundIssuesCount: number; + isDigmathonCompleted: boolean; onGoBack: () => void; - isCongratulationsView: boolean; -} - -export interface DigmathonProgressData { - insights: { type: InsightType; isFound: boolean }[]; -} - -export interface DigmathonInsightCardData { - title: string; - description: string; - illustration: JSX.Element; -} - -export interface DigmathonInsightData { - type: InsightType; - data: DigmathonInsightCardData | undefined; - isFound: boolean; } diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index 0116d30ca..39ea40b1d 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -697,21 +697,11 @@ export const EnableDigmathonMode: Story = { export const OpenCongratulationsDigmathonView: Story = { play: () => { - window.postMessage({ - type: "digma", - action: actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW - }); - setTimeout(() => { window.postMessage({ type: "digma", action: actions.SET_DIGMATHON_PROGRESS_DATA, - payload: { - insights: mockedDigmathonProgressData.insights.map((x) => ({ - ...x, - isFound: true - })) - } + payload: mockedDigmathonProgressData }); }, 0); } diff --git a/src/components/RecentActivity/actions.ts b/src/components/RecentActivity/actions.ts index 6f9764d21..691fbcd50 100644 --- a/src/components/RecentActivity/actions.ts +++ b/src/components/RecentActivity/actions.ts @@ -21,6 +21,5 @@ export const actions = addPrefix(ACTION_PREFIX, { FINISH_ORG_DIGMA_SETUP: "FINISH_ORG_DIGMA_SETUP", OPEN_REGISTRATION_DIALOG: "OPEN_REGISTRATION_DIALOG", GET_DIGMATHON_PROGRESS_DATA: "GET_DIGMATHON_PROGRESS_DATA", - SET_DIGMATHON_PROGRESS_DATA: "SET_DIGMATHON_PROGRESS_DATA", - OPEN_DIGMATHON_CONGRATULATIONS_VIEW: "OPEN_DIGMATHON_CONGRATULATIONS_VIEW" + SET_DIGMATHON_PROGRESS_DATA: "SET_DIGMATHON_PROGRESS_DATA" }); diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx index e06439815..66b37f483 100644 --- a/src/components/RecentActivity/index.tsx +++ b/src/components/RecentActivity/index.tsx @@ -6,6 +6,7 @@ import { actions as globalActions } from "../../actions"; import { dispatcher } from "../../dispatcher"; import { usePrevious } from "../../hooks/usePrevious"; import { trackingEvents as globalTrackingEvents } from "../../trackingEvents"; +import { isBoolean } from "../../typeGuards/isBoolean"; import { ChangeEnvironmentPayload } from "../../types"; import { sendUserActionTrackingEvent } from "../../utils/actions/sendUserActionTrackingEvent"; import { groupBy } from "../../utils/groupBy"; @@ -38,6 +39,7 @@ import { RecentActivityProps, ViewModeOption } from "./types"; +import { useDigmathonProgressData } from "./useDigmathonProgressData"; export const RECENT_ACTIVITY_CONTAINER_ID = "recent-activity"; @@ -101,11 +103,14 @@ export const RecentActivity = (props: RecentActivityProps) => { ); const previousEnvironment = usePrevious(config.environment); const [isDigmathonMode, setIsDigmathonMode] = useState(false); - const [ - isDigmathonCongratulationsViewVisible, - setIsDigmathonCongratulationsViewVisible - ] = useState(false); const { observe, entry } = useDimensions(); + const { + data: digmathonProgressData, + getData: getDigmathonProgressData, + foundIssuesCount, + isDigmathonCompleted + } = useDigmathonProgressData(); + const previousIsDigmathonCompleted = usePrevious(isDigmathonCompleted); const environmentActivities = useMemo( () => (data ? groupBy(data.entries, (x) => x.environment) : {}), @@ -156,21 +161,12 @@ export const RecentActivity = (props: RecentActivityProps) => { setIsRegistrationPopupVisible(true); }; - const handleOpenCongratulationsDigmathonView = () => { - setIsDigmathonMode(true); - setIsDigmathonCongratulationsViewVisible(true); - }; - dispatcher.addActionListener(actions.SET_DATA, handleRecentActivityData); dispatcher.addActionListener(actions.SET_LIVE_DATA, handleLiveData); dispatcher.addActionListener( actions.OPEN_REGISTRATION_DIALOG, handleOpenRegistrationDialog ); - dispatcher.addActionListener( - actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW, - handleOpenCongratulationsDigmathonView - ); return () => { dispatcher.removeActionListener( @@ -182,10 +178,6 @@ export const RecentActivity = (props: RecentActivityProps) => { actions.OPEN_REGISTRATION_DIALOG, handleOpenRegistrationDialog ); - dispatcher.removeActionListener( - actions.OPEN_DIGMATHON_CONGRATULATIONS_VIEW, - handleOpenCongratulationsDigmathonView - ); }; }, []); @@ -201,6 +193,16 @@ export const RecentActivity = (props: RecentActivityProps) => { } }, [props.liveData]); + useEffect(() => { + if ( + isBoolean(previousIsDigmathonCompleted) && + previousIsDigmathonCompleted !== isDigmathonCompleted && + isDigmathonCompleted + ) { + setIsDigmathonMode(true); + } + }, [previousIsDigmathonCompleted, isDigmathonCompleted]); + useEffect(() => { if ( previousUserRegistrationEmail !== config.userRegistrationEmail && @@ -434,7 +436,6 @@ export const RecentActivity = (props: RecentActivityProps) => { const handleDigmathonGoBack = () => { setIsDigmathonMode(false); - setIsDigmathonCongratulationsViewVisible(false); }; const renderContent = () => { @@ -496,8 +497,11 @@ export const RecentActivity = (props: RecentActivityProps) => { {isDigmathonMode ? ( ) : ( diff --git a/src/components/RecentActivity/types.ts b/src/components/RecentActivity/types.ts index 818de4578..c9cd9ea17 100644 --- a/src/components/RecentActivity/types.ts +++ b/src/components/RecentActivity/types.ts @@ -1,5 +1,6 @@ import { ComponentType } from "react"; import { Duration } from "../../globals"; +import { InsightType } from "../Insights/types"; import { EnvironmentType } from "../common/App/types"; import { IconProps } from "../common/icons/types"; import { ViewMode } from "./EnvironmentPanel/types"; @@ -92,3 +93,19 @@ export interface SetIsJaegerData { export interface ViewModeOptionProps { $selected: boolean; } + +export interface DigmathonProgressData { + insights: InsightType[]; +} + +export interface DigmathonInsightCardData { + title: string; + description: string; + illustration: JSX.Element; +} + +export interface DigmathonInsightData { + type: InsightType; + data: DigmathonInsightCardData | undefined; + isFound: boolean; +} diff --git a/src/components/RecentActivity/useDigmathonProgressData.ts b/src/components/RecentActivity/useDigmathonProgressData.ts new file mode 100644 index 000000000..bc1b049cb --- /dev/null +++ b/src/components/RecentActivity/useDigmathonProgressData.ts @@ -0,0 +1,155 @@ +import { useEffect, useState } from "react"; +import { actions as globalActions } from "../../actions"; +import { dispatcher } from "../../dispatcher"; +import { usePrevious } from "../../hooks/usePrevious"; +import { InsightType } from "../Insights/types"; +import { getDigmathonInsightCardData } from "./Digmathon/getDigmathonInsightData"; +import { actions } from "./actions"; +import { DigmathonInsightData, DigmathonProgressData } from "./types"; + +const REQUIRED_COUNT_OF_FOUND_ISSUES = 3; + +const getData = () => { + window.sendMessageToDigma({ + action: actions.GET_DIGMATHON_PROGRESS_DATA + }); +}; + +const getIsFound = ( + data: DigmathonProgressData | undefined, + type: InsightType +) => { + if (!data) { + return false; + } + + switch (type) { + case InsightType.EndpointQueryOptimizationV2: + case InsightType.EndpointQueryOptimization: + case InsightType.SpanQueryOptimization: + return data.insights.some((x) => + [ + InsightType.EndpointQueryOptimizationV2, + InsightType.EndpointQueryOptimization, + InsightType.SpanQueryOptimization + ].includes(x) + ); + case InsightType.EndpointSpanNPlusOne: + case InsightType.EndpointSpaNPlusOne: + case InsightType.SpaNPlusOne: + return data.insights.some((x) => + [ + InsightType.EndpointSpanNPlusOne, + InsightType.EndpointSpaNPlusOne, + InsightType.SpaNPlusOne + ].includes(x) + ); + case InsightType.EndpointBottleneck: + case InsightType.SpanEndpointBottleneck: + return data.insights.some((x) => + [ + InsightType.EndpointBottleneck, + InsightType.SpanEndpointBottleneck + ].includes(x) + ); + default: + return data.insights.includes(type); + } +}; + +export const useDigmathonProgressData = () => { + const [data, setData] = useState(); + const foundIssuesCount = data?.filter((x) => x.isFound).length || 0; + const isDigmathonCompleted = + foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES; + const previousIsDigmathonCompleted = usePrevious(isDigmathonCompleted); + + useEffect(() => { + getData(); + + const handleSetDigmaProgressData = (data: unknown) => { + const payload = data as DigmathonProgressData; + const insights: DigmathonInsightData[] = [ + { + type: InsightType.SpanScaling, + data: getDigmathonInsightCardData(InsightType.SpanScaling), + isFound: getIsFound(payload, InsightType.SpanScaling) + }, + { + type: InsightType.SpanNexus, + data: getDigmathonInsightCardData(InsightType.SpanNexus), + isFound: getIsFound(payload, InsightType.SpanNexus) + }, + { + type: InsightType.SpanQueryOptimization, + data: getDigmathonInsightCardData(InsightType.SpanQueryOptimization), + isFound: getIsFound(payload, InsightType.SpanQueryOptimization) + }, + { + type: InsightType.HotSpot, + data: getDigmathonInsightCardData(InsightType.HotSpot), + isFound: getIsFound(payload, InsightType.HotSpot) + }, + { + type: InsightType.SpaNPlusOne, + data: getDigmathonInsightCardData(InsightType.SpaNPlusOne), + isFound: getIsFound(payload, InsightType.SpaNPlusOne) + }, + { + type: InsightType.EndpointSessionInView, + data: getDigmathonInsightCardData(InsightType.EndpointSessionInView), + isFound: getIsFound(payload, InsightType.EndpointSessionInView) + }, + { + type: InsightType.SpanUsages, + data: getDigmathonInsightCardData(InsightType.SpanUsages), + isFound: getIsFound(payload, InsightType.SpanUsages) + }, + { + type: InsightType.EndpointHighNumberOfQueries, + data: getDigmathonInsightCardData( + InsightType.EndpointHighNumberOfQueries + ), + isFound: getIsFound(payload, InsightType.EndpointHighNumberOfQueries) + }, + { + type: InsightType.EndpointBottleneck, + data: getDigmathonInsightCardData(InsightType.EndpointBottleneck), + isFound: getIsFound(payload, InsightType.EndpointBottleneck) + } + ]; + + setData(insights); + }; + + dispatcher.addActionListener( + actions.SET_DIGMATHON_PROGRESS_DATA, + handleSetDigmaProgressData + ); + + return () => { + dispatcher.removeActionListener( + actions.SET_DIGMATHON_PROGRESS_DATA, + handleSetDigmaProgressData + ); + }; + }, []); + + useEffect(() => { + if ( + previousIsDigmathonCompleted !== isDigmathonCompleted && + isDigmathonCompleted + ) { + window.sendMessageToDigma({ + action: globalActions.FINISH_DIGMATHON_GAME + }); + } + }, [isDigmathonCompleted, previousIsDigmathonCompleted]); + + return { + data, + getData, + foundIssuesCount, + isDigmathonCompleted + }; +}; diff --git a/src/components/common/App/ConfigContext.ts b/src/components/common/App/ConfigContext.ts index a4f4200f5..c343bcbaa 100644 --- a/src/components/common/App/ConfigContext.ts +++ b/src/components/common/App/ConfigContext.ts @@ -31,7 +31,8 @@ export const initialState = { insightStats: undefined, productKey: isString(window.productKey) ? window.productKey : "", isDigmathonModeEnabled: window.isDigmathonModeEnabled === true, - userId: isString(window.userId) ? window.userId : "" + userId: isString(window.userId) ? window.userId : "", + isDigmathonGameFinished: window.isDigmathonGameFinished === true }; export const ConfigContext = createContext(initialState); diff --git a/src/components/common/App/index.tsx b/src/components/common/App/index.tsx index 8bc3a9332..feaf600c4 100644 --- a/src/components/common/App/index.tsx +++ b/src/components/common/App/index.tsx @@ -254,11 +254,11 @@ export const App = (props: AppProps) => { } }; - const handleSetUserId = (data: unknown) => { - if (isObject(data) && isString(data.userId)) { + const handleIsDigmathonGameFinished = (data: unknown) => { + if (isObject(data) && isBoolean(data.isDigmathonGameFinished)) { setConfig((config) => ({ ...config, - userId: data.userId as string + isDigmathonGameFinished: data.isDigmathonGameFinished as boolean })); } }; @@ -327,7 +327,10 @@ export const App = (props: AppProps) => { handleSetIsDigmathonModeEnabled ); dispatcher.addActionListener(actions.SET_PRODUCT_KEY, handleSetProductKey); - dispatcher.addActionListener(actions.SET_USER_ID, handleSetUserId); + dispatcher.addActionListener( + actions.SET_IS_DIGMATHON_GAME_FINISHED, + handleIsDigmathonGameFinished + ); return () => { dispatcher.removeActionListener(actions.SET_THEME, handleSetTheme); @@ -403,7 +406,10 @@ export const App = (props: AppProps) => { actions.SET_PRODUCT_KEY, handleSetProductKey ); - dispatcher.removeActionListener(actions.SET_USER_ID, handleSetUserId); + dispatcher.removeActionListener( + actions.SET_IS_DIGMATHON_GAME_FINISHED, + handleIsDigmathonGameFinished + ); }; }, []); diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts index fbe99e2ff..cc6754401 100644 --- a/src/components/common/App/types.ts +++ b/src/components/common/App/types.ts @@ -106,6 +106,7 @@ export interface ConfigContextData { productKey: string; isDigmathonModeEnabled: boolean; userId: string; + isDigmathonGameFinished: boolean; } export interface InsightStats { diff --git a/src/globals.d.ts b/src/globals.d.ts index 6eaab45a9..0fa4a948b 100644 --- a/src/globals.d.ts +++ b/src/globals.d.ts @@ -58,6 +58,7 @@ declare global { productKey?: unknown; isDigmathonModeEnabled?: unknown; userId?: unknown; + isDigmathonGameFinished?: unknown; } } From 3fccd91e697c0e5185cf9400f0dc7e777f315205 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 14:37:37 +0200 Subject: [PATCH 09/14] Add Emtpy state for Digmathon --- .storybook/preview.tsx | 23 ++++++++---- public/images/DigmoWithAmazonGiftCard.svg | 36 +++++++++++++++++++ src/api/index.ts | 10 ++++-- .../Digmathon/ProgressView/index.tsx | 17 +++++++++ .../Digmathon/ProgressView/styles.ts | 34 +++++++++++++++++- .../RecentActivity/RecentActivity.stories.tsx | 7 ++++ 6 files changed, 118 insertions(+), 9 deletions(-) create mode 100644 public/images/DigmoWithAmazonGiftCard.svg diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index d4002a9f0..4574f965a 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -1,6 +1,6 @@ import type { Preview } from "@storybook/react"; import { StoryFn } from "@storybook/react"; -import React from "react"; +import React, { useEffect, useState } from "react"; import { cancelMessage, initializeDigmaMessageListener, @@ -13,17 +13,28 @@ import { Mode } from "../src/globals"; const preview: Preview = { decorators: [ (Story: StoryFn, context): JSX.Element => { + const [isInitialized, setIsInitialized] = useState(false); // TODO: Fix types - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access const theme = context.globals.theme as Mode; - initializeDigmaMessageListener(dispatcher); - window.sendMessageToDigma = sendMessage; - window.cancelMessageToDigma = cancelMessage; - return ( + useEffect(() => { + const removeDigmaMessageListener = + initializeDigmaMessageListener(dispatcher); + window.sendMessageToDigma = sendMessage; + window.cancelMessageToDigma = cancelMessage; + setIsInitialized(true); + + return () => { + removeDigmaMessageListener(); + }; + }, []); + + return isInitialized ? ( + ) : ( + <>Initializing... ); } ], diff --git a/public/images/DigmoWithAmazonGiftCard.svg b/public/images/DigmoWithAmazonGiftCard.svg new file mode 100644 index 000000000..acbf82de3 --- /dev/null +++ b/public/images/DigmoWithAmazonGiftCard.svg @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/api/index.ts b/src/api/index.ts index 335183426..01b562ac1 100644 --- a/src/api/index.ts +++ b/src/api/index.ts @@ -10,12 +10,18 @@ const isDigmaMessageEvent = (e: MessageEvent): e is DigmaMessageEvent => export const initializeDigmaMessageListener = ( dispatcher: ActionDispatcher ) => { - window.addEventListener("message", (e) => { + const handleDigmaMessage = (e: MessageEvent) => { if (isDigmaMessageEvent(e)) { console.debug("Digma message received: ", e); dispatcher.dispatch(e.timeStamp, e.data.action, e.data.payload); } - }); + }; + + window.addEventListener("message", handleDigmaMessage); + + return () => { + window.removeEventListener("message", handleDigmaMessage); + }; }; export const sendMessage = ( diff --git a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx index dbf68ac89..6be8d3704 100644 --- a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx +++ b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx @@ -10,6 +10,23 @@ export const ProgressView = ({ data, foundIssuesCount }: ProgressViewProps) => { sendTrackingEvent(trackingEvents.DIGMATHON_PROGRESS_VIEWED); }, []); + if (foundIssuesCount === 0) { + return ( + + + + + Start Digmathon + + To get started run your code with Digma and begin unlocking + issues. Check back here to see your progress! + + + + + ); + } + return ( <> diff --git a/src/components/RecentActivity/Digmathon/ProgressView/styles.ts b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts index e20d4f408..1f1da0ce3 100644 --- a/src/components/RecentActivity/Digmathon/ProgressView/styles.ts +++ b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts @@ -1,7 +1,9 @@ import styled from "styled-components"; import { + bodyMediumTypography, footnoteMediumTypography, - footnoteRegularTypography + footnoteRegularTypography, + subscriptRegularTypography } from "../../../common/App/typographies"; export const Header = styled.div` @@ -39,3 +41,33 @@ export const CardsContainer = styled.div` grid-template-rows: 1fr 1fr 1fr; padding: 0 12px 12px; `; + +export const EmptyStateContainer = styled.div` + display: flex; + align-items: center; + justify-content: center; + flex-grow: 1; +`; + +export const EmptyStateContentContainer = styled.div` + width: 290px; + display: flex; + flex-direction: column; + gap: 12px; +`; + +export const EmptyStateTextContainer = styled.div` + ${subscriptRegularTypography} + + display: flex; + flex-direction: column; + gap: 4px; + text-align: center; + color: ${({ theme }) => theme.colors.v3.text.secondary}; +`; + +export const EmptyStateTitle = styled.span` + ${bodyMediumTypography} + + color: ${({ theme }) => theme.colors.v3.text.primary}; +`; diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx index 39ea40b1d..5c6275816 100644 --- a/src/components/RecentActivity/RecentActivity.stories.tsx +++ b/src/components/RecentActivity/RecentActivity.stories.tsx @@ -692,6 +692,13 @@ export const EnableDigmathonMode: Story = { productKey: "digmathon" } }); + window.postMessage({ + type: "digma", + action: actions.SET_DIGMATHON_PROGRESS_DATA, + payload: { + insights: [] + } + }); } }; From 44827a997096568cb9e97951279d4b9946091a1a Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 14:56:48 +0200 Subject: [PATCH 10/14] Do not send message to get Digmathon progress data on Recent activity mount --- src/components/RecentActivity/useDigmathonProgressData.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/RecentActivity/useDigmathonProgressData.ts b/src/components/RecentActivity/useDigmathonProgressData.ts index bc1b049cb..0e3183cfa 100644 --- a/src/components/RecentActivity/useDigmathonProgressData.ts +++ b/src/components/RecentActivity/useDigmathonProgressData.ts @@ -65,8 +65,6 @@ export const useDigmathonProgressData = () => { const previousIsDigmathonCompleted = usePrevious(isDigmathonCompleted); useEffect(() => { - getData(); - const handleSetDigmaProgressData = (data: unknown) => { const payload = data as DigmathonProgressData; const insights: DigmathonInsightData[] = [ From dcbc155553919f863930d0b0fd9fb998b815e336 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 18:19:46 +0200 Subject: [PATCH 11/14] Add multiple link kinds for testing --- .../Digmathon/CongratulationsView/index.tsx | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx index 18cbdbd62..19c7de70e 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -3,35 +3,45 @@ import { openURLInDefaultBrowser } from "../../../../utils/actions/openURLInDefa import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; import { ConfigContext } from "../../../common/App/ConfigContext"; +import { ConfigContextData } from "../../../common/App/types"; import { CheckmarkCircleIcon } from "../../../common/icons/12px/CheckmarkCircleIcon"; import { trackingEvents } from "../../tracking"; +import { DigmathonInsightData } from "../../types"; import * as s from "./styles"; import { CongratulationsViewProps } from "./types"; const EMAIL_ADDRESS = "digmathon@digma.ai"; +const getEmailURL = ( + data: DigmathonInsightData[], + config: ConfigContextData +) => { + const userId = config.userId || config.userRegistrationEmail || ""; + const subject = `Digmathon Challenge Completed! [${userId}]`; + + const foundInsights = data + .filter((x) => x.isFound) + .map((x) => x.data?.title || x.type) + .join(", "); + const body = [ + "Insights found:", + foundInsights, + "Please send back the reward to this email!" + ].join("%0D%0A%0D%0A"); + + return `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`; +}; + export const CongratulationsView = ({ data }: CongratulationsViewProps) => { const config = useContext(ConfigContext); + const emailURL = getEmailURL(data, config); const handleContactLinkClick = () => { sendUserActionTrackingEvent( trackingEvents.DIGMATHON_VIEW_CONTACT_LINK_CLICKED ); - const userId = config.userId || config.userRegistrationEmail || ""; - const subject = `Digmathon Challenge Completed! [${userId}]`; - - const foundInsights = data - .filter((x) => x.isFound) - .map((x) => x.data?.title || x.type) - .join(", "); - const body = [ - "Insights found:", - foundInsights, - "Please send back the reward to this email!" - ].join("%0D%0A%0D%0A"); - - const url = `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`; + const url = getEmailURL(data, config); openURLInDefaultBrowser(url); }; @@ -51,7 +61,10 @@ export const CongratulationsView = ({ data }: CongratulationsViewProps) => { keep using Digma locally for free, forever! - {EMAIL_ADDRESS} + {EMAIL_ADDRESS} (send JCEF message) + + + {EMAIL_ADDRESS} (HTML anchor with href attribute) ); From 572719f646e14c5860c2e600510420ab0e2ccfd5 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Mon, 8 Apr 2024 23:34:11 +0200 Subject: [PATCH 12/14] Remove redundant link --- .../Digmathon/CongratulationsView/index.tsx | 11 ++--------- .../RecentActivity/EnvironmentPanel/index.tsx | 2 +- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx index 19c7de70e..0205d3332 100644 --- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx +++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx @@ -1,5 +1,4 @@ import { useContext, useEffect } from "react"; -import { openURLInDefaultBrowser } from "../../../../utils/actions/openURLInDefaultBrowser"; import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent"; import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent"; import { ConfigContext } from "../../../common/App/ConfigContext"; @@ -40,9 +39,6 @@ export const CongratulationsView = ({ data }: CongratulationsViewProps) => { sendUserActionTrackingEvent( trackingEvents.DIGMATHON_VIEW_CONTACT_LINK_CLICKED ); - - const url = getEmailURL(data, config); - openURLInDefaultBrowser(url); }; useEffect(() => { @@ -60,11 +56,8 @@ export const CongratulationsView = ({ data }: CongratulationsViewProps) => { the link below to send us an email and claim your reward. Feel free to keep using Digma locally for free, forever! - - {EMAIL_ADDRESS} (send JCEF message) - - - {EMAIL_ADDRESS} (HTML anchor with href attribute) + + {EMAIL_ADDRESS} ); diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index f488f997a..90c157127 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -241,7 +241,7 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { ? [ { id: "digmathon", - label: "Digmathon mode", + label: "Digmathon!", icon: , onClick: handleDigmathonModeMenuItemClick } From b8bec05179a8956fb8cc8df94aaa20e6a43e73b3 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 9 Apr 2024 01:51:49 +0200 Subject: [PATCH 13/14] Change field validation --- .../InstallationWizard/FinishStep/index.tsx | 6 +++ .../InstallationWizard/FinishStep/types.ts | 2 + .../InstallationWizard/Step/index.tsx | 2 +- .../InstallationWizard/Step/styles.ts | 4 +- .../InstallationWizard/Step/types.ts | 2 +- src/components/InstallationWizard/index.tsx | 40 +++++++++++++------ src/components/InstallationWizard/types.ts | 9 +++++ 7 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/components/InstallationWizard/FinishStep/index.tsx b/src/components/InstallationWizard/FinishStep/index.tsx index a89d4c36c..2845f3ad7 100644 --- a/src/components/InstallationWizard/FinishStep/index.tsx +++ b/src/components/InstallationWizard/FinishStep/index.tsx @@ -107,6 +107,12 @@ export const FinishStep = (props: FinishStepProps) => { value={props.email} onChange={props.onEmailInputChange} /> + {props.errors.email && ( + + + {props.errors.email} + + )} {props.isEmailValid === false && ( diff --git a/src/components/InstallationWizard/FinishStep/types.ts b/src/components/InstallationWizard/FinishStep/types.ts index b3ead6fd0..cf15f739f 100644 --- a/src/components/InstallationWizard/FinishStep/types.ts +++ b/src/components/InstallationWizard/FinishStep/types.ts @@ -1,4 +1,5 @@ import { ChangeEvent } from "react"; +import { FieldsErrors } from "../types"; export interface FinishStepProps { quickstartURL?: string; @@ -9,4 +10,5 @@ export interface FinishStepProps { isEmailValidating: boolean; productKey: string; onProductKeyInputChange: (e: ChangeEvent) => void; + errors: FieldsErrors; } diff --git a/src/components/InstallationWizard/Step/index.tsx b/src/components/InstallationWizard/Step/index.tsx index 40145cbd8..f0884a73f 100644 --- a/src/components/InstallationWizard/Step/index.tsx +++ b/src/components/InstallationWizard/Step/index.tsx @@ -69,7 +69,7 @@ export const Step = (props: StepProps) => { > diff --git a/src/components/InstallationWizard/Step/styles.ts b/src/components/InstallationWizard/Step/styles.ts index 796afe862..24754cbc5 100644 --- a/src/components/InstallationWizard/Step/styles.ts +++ b/src/components/InstallationWizard/Step/styles.ts @@ -127,8 +127,8 @@ export const Number = styled.span` align-items: center; justify-content: center; position: absolute; - background: ${({ theme, status }) => - status === "completed" ? "none" : getNumberBackgroundColor(theme)}; + background: ${({ theme, $status }) => + $status === "completed" ? "none" : getNumberBackgroundColor(theme)}; ${({ theme, $transitionClassName, $transitionDuration }) => { return ` diff --git a/src/components/InstallationWizard/Step/types.ts b/src/components/InstallationWizard/Step/types.ts index 567886b13..a9c0d2c37 100644 --- a/src/components/InstallationWizard/Step/types.ts +++ b/src/components/InstallationWizard/Step/types.ts @@ -37,5 +37,5 @@ export interface NumberContainerProps { } export interface NumberProps extends TransitionProps { - status: StepStatus; + $status: StepStatus; } diff --git a/src/components/InstallationWizard/index.tsx b/src/components/InstallationWizard/index.tsx index f4a432fd6..1a6974558 100644 --- a/src/components/InstallationWizard/index.tsx +++ b/src/components/InstallationWizard/index.tsx @@ -19,6 +19,7 @@ import { StepData, StepStatus } from "./Step/types"; import { actions } from "./actions"; import * as s from "./styles"; import { trackingEvents } from "./tracking"; +import { FieldsErrors } from "./types"; const DIGMA_DOCKER_EXTENSION_URL = "https://open.docker.com/extensions/marketplace?extensionId=digmaai/digma-docker-extension"; @@ -53,6 +54,14 @@ const getStepStatus = (index: number, currentStep: number): StepStatus => { return "not-completed"; }; +const getFieldsErrors = (productKey: string, email: string): FieldsErrors => { + if (productKey.length > 0 && email.length === 0) { + return { email: "Please enter your email" }; + } + + return {}; +}; + export const InstallationWizard = () => { const config = useContext(ConfigContext); const [currentStep, setCurrentStep] = useState(firstStep); @@ -81,12 +90,13 @@ export const InstallationWizard = () => { // const theme = useTheme(); // const themeKind = getThemeKind(theme); const [email, setEmail] = useState(config.userEmail); + const [productKey, setProductKey] = useState(config.productKey); const [isEmailValid, setIsEmailValid] = useState( email.length > 0 ? isValidEmailFormat(email) : undefined ); const [isEmailValidating, setIsEmailValidating] = useState(false); const debouncedEmail = useDebounce(email, 1000); - const [productKey, setProductKey] = useState(config.productKey); + const [errors, setErrors] = useState({}); // const [ // isDigmaCloudNotificationCheckboxChecked, // setIsDigmaCloudNotificationCheckboxChecked @@ -227,6 +237,7 @@ export const InstallationWizard = () => { const value = e.target.value.trim(); if (email !== value) { + setErrors({}); setIsEmailValid(undefined); setIsEmailValidating(true); setEmail(value); @@ -248,13 +259,19 @@ export const InstallationWizard = () => { }; const handleFinishButtonClick = () => { - window.sendMessageToDigma({ - action: actions.FINISH, - payload: { - ...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {}), - ...(productKey.length > 0 ? { productKey } : {}) - } - }); + const errors = getFieldsErrors(productKey, email); + + setErrors(errors); + + if (Object.keys(errors).length === 0) { + window.sendMessageToDigma({ + action: actions.FINISH, + payload: { + ...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {}), + ...(productKey.length > 0 ? { productKey } : {}) + } + }); + } }; const handleSlackLinkClick = () => { @@ -330,6 +347,7 @@ export const InstallationWizard = () => { isEmailValid={isEmailValid} isEmailValidating={isEmailValidating} productKey={productKey} + errors={errors} onProductKeyInputChange={handleProductKeyInputChange} /> ) @@ -490,11 +508,7 @@ export const InstallationWizard = () => { Finish diff --git a/src/components/InstallationWizard/types.ts b/src/components/InstallationWizard/types.ts index 5e60dda22..18fd26762 100644 --- a/src/components/InstallationWizard/types.ts +++ b/src/components/InstallationWizard/types.ts @@ -17,3 +17,12 @@ export interface FinishStepFooterContentProps { $transitionClassName: string; $transitionDuration: number; } + +export interface FieldValidationResult { + isValid?: boolean; + error?: string; +} + +export interface FieldsErrors { + [key: string]: string | undefined; +} From 0cddd252b266e11e438d440e778068c6320db9a2 Mon Sep 17 00:00:00 2001 From: Kyrylo Shmidt Date: Tue, 9 Apr 2024 08:58:32 +0200 Subject: [PATCH 14/14] Update styles --- .vscode/settings.json | 2 +- .../RecentActivity/Digmathon/index.tsx | 12 +++++++ .../RecentActivity/Digmathon/styles.ts | 5 +++ .../RecentActivity/EnvironmentPanel/index.tsx | 3 +- src/components/RecentActivity/tracking.ts | 1 + .../common/icons/16px/ConfettiIcon.tsx | 33 +++++++++++++++++++ 6 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 src/components/common/icons/16px/ConfettiIcon.tsx diff --git a/.vscode/settings.json b/.vscode/settings.json index 90160881f..a2da032a6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,5 @@ }, "editor.defaultFormatter": "esbenp.prettier-vscode", "stylelint.validate": ["typescript"], - "cSpell.words": ["digma", "digmathon", "undismiss"] + "cSpell.words": ["digma", "digmathon", "digmo", "undismiss"] } diff --git a/src/components/RecentActivity/Digmathon/index.tsx b/src/components/RecentActivity/Digmathon/index.tsx index 6a82d8b48..34e9aba28 100644 --- a/src/components/RecentActivity/Digmathon/index.tsx +++ b/src/components/RecentActivity/Digmathon/index.tsx @@ -28,6 +28,13 @@ export const Digmathon = ({ onGoBack(); }; + const handleExitButtonClick = () => { + sendUserActionTrackingEvent( + trackingEvents.DIGMATHON_VIEW_EXIT_BUTTON_CLICKED + ); + onGoBack(); + }; + const renderContent = (data: DigmathonInsightData[]) => isDigmathonCompleted ? ( @@ -48,6 +55,11 @@ export const Digmathon = ({ Digmathon + {data ? ( renderContent(data) diff --git a/src/components/RecentActivity/Digmathon/styles.ts b/src/components/RecentActivity/Digmathon/styles.ts index 7a4302559..0f8644688 100644 --- a/src/components/RecentActivity/Digmathon/styles.ts +++ b/src/components/RecentActivity/Digmathon/styles.ts @@ -3,6 +3,7 @@ import { subscriptRegularTypography, subscriptSemiboldTypography } from "../../common/App/typographies"; +import { Button } from "../../common/v3/Button"; export const Container = styled.div` display: flex; @@ -51,6 +52,10 @@ export const HeaderTitle = styled.span` color: ${({ theme }) => theme.colors.v3.text.primary}; `; +export const ExitButton = styled(Button)` + margin-left: auto; +`; + export const ContentContainer = styled.div` display: flex; flex-direction: column; diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx index 90c157127..c438f2b2d 100644 --- a/src/components/RecentActivity/EnvironmentPanel/index.tsx +++ b/src/components/RecentActivity/EnvironmentPanel/index.tsx @@ -14,6 +14,7 @@ import { NewButton } from "../../common/NewButton"; import { NewPopover } from "../../common/NewPopover"; import { ToggleSwitch } from "../../common/ToggleSwitch"; import { PlusIcon } from "../../common/icons/12px/PlusIcon"; +import { ConfettiIcon } from "../../common/icons/16px/ConfettiIcon"; import { HammerIcon } from "../../common/icons/16px/HammerIcon"; import { OpenTelemetryLogoIcon } from "../../common/icons/16px/OpenTelemetryLogoIcon"; import { SlackLogoIcon } from "../../common/icons/16px/SlackLogoIcon"; @@ -242,7 +243,7 @@ export const EnvironmentPanel = (props: EnvironmentPanelProps) => { { id: "digmathon", label: "Digmathon!", - icon: , + icon: , onClick: handleDigmathonModeMenuItemClick } ] diff --git a/src/components/RecentActivity/tracking.ts b/src/components/RecentActivity/tracking.ts index b42aa4cfa..0d0309f08 100644 --- a/src/components/RecentActivity/tracking.ts +++ b/src/components/RecentActivity/tracking.ts @@ -13,6 +13,7 @@ export const trackingEvents = addPrefix( OBSERVABILITY_TOGGLE_SWITCHED: "observability toggle switched", KEBAB_MENU_ITEM_CLICKED: "kebab menu item clicked", DIGMATHON_VIEW_BACK_BUTTON_CLICKED: "digmathon view back button clicked", + DIGMATHON_VIEW_EXIT_BUTTON_CLICKED: "digmathon view exit button clicked", DIGMATHON_VIEW_CONTACT_LINK_CLICKED: "digmathon view contact link clicked", DIGMATHON_PROGRESS_VIEWED: "digmathon progress viewed", DIGMATHON_CONGRATULATIONS_VIEWED: "digmathon congratulations viewed" diff --git a/src/components/common/icons/16px/ConfettiIcon.tsx b/src/components/common/icons/16px/ConfettiIcon.tsx new file mode 100644 index 000000000..849ca3f32 --- /dev/null +++ b/src/components/common/icons/16px/ConfettiIcon.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { useIconProps } from "../hooks"; +import { IconProps } from "../types"; + +const ConfettiIconComponent = (props: IconProps) => { + const { size, color } = useIconProps(props); + + return ( + + + + + + + + + + + ); +}; + +export const ConfettiIcon = React.memo(ConfettiIconComponent);