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/.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/.vscode/settings.json b/.vscode/settings.json
index 0c01357f2..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", "UNDISMISS"]
+ "cSpell.words": ["digma", "digmathon", "digmo", "undismiss"]
}
diff --git a/assets/index.ejs b/assets/index.ejs
index f47fa0bfe..e82c4626d 100644
--- a/assets/index.ejs
+++ b/assets/index.ejs
@@ -32,6 +32,10 @@
window.isDockerInstalled;
window.isDockerComposeInstalled;
window.isMicrometerProject;
+ window.productKey;
+ window.userId;
+ window.isDigmathonModeEnabled;
+ window.isDigmathonGameFinished;
<% for (var i = 0; i < environmentVariables.length; i++) { %>
window.<%= environmentVariables[i] %>;<% } %>
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/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/actions.ts b/src/actions.ts
index 2795a686f..2c3f83dae 100644
--- a/src/actions.ts
+++ b/src/actions.ts
@@ -41,5 +41,10 @@ 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",
+ SET_IS_DIGMATHON_GAME_FINISHED: "SET_IS_DIGMATHON_GAME_FINISHED",
+ FINISH_DIGMATHON_GAME: "FINISH_DIGMATHON_GAME"
});
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/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/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;
+`;
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..2845f3ad7 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,20 +72,47 @@ export const FinishStep = (props: FinishStepProps) => {
)}
+ {config.isDigmathonModeEnabled && (
+
+
+ Product key(optional)
+
+
+ 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
-
-
+
+ {props.errors.email && (
+
+
+ {props.errors.email}
+
+ )}
{props.isEmailValid === false && (
@@ -105,7 +136,7 @@ export const FinishStep = (props: FinishStepProps) => {
/>
)}
-
+
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..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;
@@ -7,4 +8,7 @@ export interface FinishStepProps {
email: string;
isEmailValid?: boolean;
isEmailValidating: boolean;
+ productKey: string;
+ onProductKeyInputChange: (e: ChangeEvent) => void;
+ errors: FieldsErrors;
}
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/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 12f2aae87..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,11 +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 [errors, setErrors] = useState({});
// const [
// isDigmaCloudNotificationCheckboxChecked,
// setIsDigmaCloudNotificationCheckboxChecked
@@ -226,12 +237,21 @@ export const InstallationWizard = () => {
const value = e.target.value.trim();
if (email !== value) {
+ setErrors({});
setIsEmailValid(undefined);
setIsEmailValidating(true);
setEmail(value);
}
};
+ const handleProductKeyInputChange = (e: ChangeEvent) => {
+ const value = e.target.value.trim();
+
+ if (productKey !== value) {
+ setProductKey(value);
+ }
+ };
+
const handleCloseButtonClick = () => {
window.sendMessageToDigma({
action: actions.CLOSE
@@ -239,12 +259,19 @@ export const InstallationWizard = () => {
};
const handleFinishButtonClick = () => {
- window.sendMessageToDigma({
- action: actions.FINISH,
- payload: {
- ...(debouncedEmail.length > 0 ? { email: debouncedEmail } : {})
- }
- });
+ 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 = () => {
@@ -319,6 +346,9 @@ export const InstallationWizard = () => {
onEmailInputChange={handleEmailInputChange}
isEmailValid={isEmailValid}
isEmailValidating={isEmailValidating}
+ productKey={productKey}
+ errors={errors}
+ onProductKeyInputChange={handleProductKeyInputChange}
/>
)
}
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;
+}
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..0205d3332
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx
@@ -0,0 +1,64 @@
+import { useContext, useEffect } from "react";
+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
+ );
+ };
+
+ useEffect(() => {
+ sendTrackingEvent(trackingEvents.DIGMATHON_CONGRATULATIONS_VIEWED);
+ }, []);
+
+ return (
+
+
+
+
+
+ Congratulations!
+ 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/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/CongratulationsView/types.ts b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts
new file mode 100644
index 000000000..9fe5e814b
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/CongratulationsView/types.ts
@@ -0,0 +1,5 @@
+import { DigmathonInsightData } from "../../types";
+
+export interface CongratulationsViewProps {
+ data: DigmathonInsightData[];
+}
diff --git a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx
new file mode 100644
index 000000000..a3ecc1d2b
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx
@@ -0,0 +1,47 @@
+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
+ });
+ });
+ }, 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..9da2fc505
--- /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..6be8d3704
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx
@@ -0,0 +1,56 @@
+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 = ({ data, foundIssuesCount }: ProgressViewProps) => {
+ useEffect(() => {
+ 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 (
+ <>
+
+ Search for issues
+
+ Improve your code, and win a gift card
+
+
+ {foundIssuesCount} out of{" "}
+ {data.length} issues found
+
+
+
+ {data.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..1f1da0ce3
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts
@@ -0,0 +1,73 @@
+import styled from "styled-components";
+import {
+ bodyMediumTypography,
+ footnoteMediumTypography,
+ footnoteRegularTypography,
+ subscriptRegularTypography
+} 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;
+`;
+
+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/Digmathon/ProgressView/types.ts b/src/components/RecentActivity/Digmathon/ProgressView/types.ts
new file mode 100644
index 000000000..86771d029
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/ProgressView/types.ts
@@ -0,0 +1,6 @@
+import { DigmathonInsightData } from "../../types";
+
+export interface ProgressViewProps {
+ data: 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..c71f8cc2e
--- /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..34e9aba28
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/index.tsx
@@ -0,0 +1,73 @@
+import { useEffect } from "react";
+import { sendUserActionTrackingEvent } from "../../../utils/actions/sendUserActionTrackingEvent";
+import { NewCircleLoader } from "../../common/NewCircleLoader";
+import { ChevronIcon } from "../../common/icons/16px/ChevronIcon";
+import { Direction } from "../../common/icons/types";
+import { trackingEvents } from "../tracking";
+import { DigmathonInsightData } from "../types";
+import { CongratulationsView } from "./CongratulationsView";
+import { ProgressView } from "./ProgressView";
+import * as s from "./styles";
+import { DigmathonProgressProps } from "./types";
+
+export const Digmathon = ({
+ data,
+ getData,
+ foundIssuesCount,
+ isDigmathonCompleted,
+ onGoBack
+}: DigmathonProgressProps) => {
+ useEffect(() => {
+ getData();
+ }, []);
+
+ const handleGoBackButtonClick = () => {
+ sendUserActionTrackingEvent(
+ trackingEvents.DIGMATHON_VIEW_BACK_BUTTON_CLICKED
+ );
+ onGoBack();
+ };
+
+ const handleExitButtonClick = () => {
+ sendUserActionTrackingEvent(
+ trackingEvents.DIGMATHON_VIEW_EXIT_BUTTON_CLICKED
+ );
+ onGoBack();
+ };
+
+ const renderContent = (data: DigmathonInsightData[]) =>
+ isDigmathonCompleted ? (
+
+ ) : (
+
+ );
+
+ return (
+
+
+
+
+ Back
+
+
+ Digmathon
+
+
+ {data ? (
+ renderContent(data)
+ ) : (
+
+
+
+ )}
+
+ );
+};
diff --git a/src/components/RecentActivity/Digmathon/mockData.ts b/src/components/RecentActivity/Digmathon/mockData.ts
new file mode 100644
index 000000000..6cbc87177
--- /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: [
+ 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/styles.ts b/src/components/RecentActivity/Digmathon/styles.ts
new file mode 100644
index 000000000..0f8644688
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/styles.ts
@@ -0,0 +1,71 @@
+import styled from "styled-components";
+import {
+ subscriptRegularTypography,
+ subscriptSemiboldTypography
+} from "../../common/App/typographies";
+import { Button } from "../../common/v3/Button";
+
+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 ExitButton = styled(Button)`
+ margin-left: auto;
+`;
+
+export const ContentContainer = styled.div`
+ display: flex;
+ flex-direction: column;
+ 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/Digmathon/types.ts b/src/components/RecentActivity/Digmathon/types.ts
new file mode 100644
index 000000000..043c130c1
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/types.ts
@@ -0,0 +1,9 @@
+import { DigmathonInsightData } from "../types";
+
+export interface DigmathonProgressProps {
+ data?: DigmathonInsightData[];
+ getData: () => void;
+ foundIssuesCount: number;
+ isDigmathonCompleted: boolean;
+ onGoBack: () => void;
+}
diff --git a/src/components/RecentActivity/EnvironmentPanel/index.tsx b/src/components/RecentActivity/EnvironmentPanel/index.tsx
index 71ee6aa96..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";
@@ -170,6 +171,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 +181,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 && config.productKey
+ ? [
+ {
+ id: "digmathon",
+ label: "Digmathon!",
+ 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..5c6275816 100644
--- a/src/components/RecentActivity/RecentActivity.stories.tsx
+++ b/src/components/RecentActivity/RecentActivity.stories.tsx
@@ -1,5 +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";
@@ -673,3 +675,41 @@ 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"
+ }
+ });
+ window.postMessage({
+ type: "digma",
+ action: actions.SET_DIGMATHON_PROGRESS_DATA,
+ payload: {
+ insights: []
+ }
+ });
+ }
+};
+
+export const OpenCongratulationsDigmathonView: Story = {
+ play: () => {
+ setTimeout(() => {
+ window.postMessage({
+ type: "digma",
+ action: actions.SET_DIGMATHON_PROGRESS_DATA,
+ payload: mockedDigmathonProgressData
+ });
+ }, 0);
+ }
+};
diff --git a/src/components/RecentActivity/actions.ts b/src/components/RecentActivity/actions.ts
index 57125d570..691fbcd50 100644
--- a/src/components/RecentActivity/actions.ts
+++ b/src/components/RecentActivity/actions.ts
@@ -19,5 +19,7 @@ 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"
});
diff --git a/src/components/RecentActivity/index.tsx b/src/components/RecentActivity/index.tsx
index 544095d36..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";
@@ -18,6 +19,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";
@@ -37,6 +39,7 @@ import {
RecentActivityProps,
ViewModeOption
} from "./types";
+import { useDigmathonProgressData } from "./useDigmathonProgressData";
export const RECENT_ACTIVITY_CONTAINER_ID = "recent-activity";
@@ -99,7 +102,15 @@ export const RecentActivity = (props: RecentActivityProps) => {
config.userRegistrationEmail
);
const previousEnvironment = usePrevious(config.environment);
+ const [isDigmathonMode, setIsDigmathonMode] = 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) : {}),
@@ -182,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 &&
@@ -409,6 +430,14 @@ export const RecentActivity = (props: RecentActivityProps) => {
}
};
+ const handleDigmathonModeButtonClick = () => {
+ setIsDigmathonMode(true);
+ };
+
+ const handleDigmathonGoBack = () => {
+ setIsDigmathonMode(false);
+ };
+
const renderContent = () => {
if (selectedEnvironment?.isPending) {
switch (selectedEnvironment.type) {
@@ -466,45 +495,56 @@ export const RecentActivity = (props: RecentActivityProps) => {
return (
-
-
- {/*
+ {isDigmathonMode ? (
+
+ ) : (
+
+
+ {/*
*/}
-
-
-
- {!selectedEnvironment?.isPending && (
-
- Recent Activity
-
-
- )}
- {!config.isObservabilityEnabled && }
-
-
-
- {renderContent()}
-
-
-
- {liveData && (
-
-
-
- )}
-
-
+
+
+
+ {!selectedEnvironment?.isPending && (
+
+ Recent Activity
+
+
+ )}
+ {!config.isObservabilityEnabled && }
+
+
+
+ {renderContent()}
+
+
+
+ {liveData && (
+
+
+
+ )}
+
+
+ )}
{environmentToDelete && (
{
+ 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(() => {
+ 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 01aeb6906..c343bcbaa 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,
+ productKey: isString(window.productKey) ? window.productKey : "",
+ isDigmathonModeEnabled: window.isDigmathonModeEnabled === true,
+ 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 12ca1fef9..feaf600c4 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 handleIsDigmathonGameFinished = (data: unknown) => {
+ if (isObject(data) && isBoolean(data.isDigmathonGameFinished)) {
+ setConfig((config) => ({
+ ...config,
+ isDigmathonGameFinished: data.isDigmathonGameFinished as boolean
+ }));
+ }
+ };
+
dispatcher.addActionListener(actions.SET_THEME, handleSetTheme);
dispatcher.addActionListener(actions.SET_MAIN_FONT, handleSetMainFont);
dispatcher.addActionListener(actions.SET_CODE_FONT, handleSetCodeFont);
@@ -295,6 +322,15 @@ 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_IS_DIGMATHON_GAME_FINISHED,
+ handleIsDigmathonGameFinished
+ );
return () => {
dispatcher.removeActionListener(actions.SET_THEME, handleSetTheme);
@@ -362,6 +398,18 @@ 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_IS_DIGMATHON_GAME_FINISHED,
+ handleIsDigmathonGameFinished
+ );
};
}, []);
diff --git a/src/components/common/App/types.ts b/src/components/common/App/types.ts
index dceae5331..cc6754401 100644
--- a/src/components/common/App/types.ts
+++ b/src/components/common/App/types.ts
@@ -103,6 +103,10 @@ export interface ConfigContextData {
isMicrometerProject: boolean;
state?: GlobalState;
insightStats?: InsightStats;
+ productKey: string;
+ isDigmathonModeEnabled: boolean;
+ userId: string;
+ isDigmathonGameFinished: 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/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);
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..0fa4a948b 100644
--- a/src/globals.d.ts
+++ b/src/globals.d.ts
@@ -55,6 +55,10 @@ declare global {
testsRefreshInterval?: unknown;
wizardSkipInstallationStep?: unknown;
wizardFirstLaunch?: unknown;
+ productKey?: unknown;
+ isDigmathonModeEnabled?: unknown;
+ userId?: unknown;
+ isDigmathonGameFinished?: 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"]