diff --git a/.vscode/settings.json b/.vscode/settings.json
index a2da032a6..e80de0519 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", "digmo", "undismiss"]
+ "cSpell.words": ["digma", "digmathon", "digmo", "leaderboard", "undismiss"]
}
diff --git a/digmathonLeaderboard.html b/digmathonLeaderboard.html
new file mode 100644
index 000000000..2953ad335
--- /dev/null
+++ b/digmathonLeaderboard.html
@@ -0,0 +1,159 @@
+
+
+
+
+
Leaderboard
+
* Updated at 9 am GMT+2
+
+
+
+ |
+ Contestant Email
+ |
+ Issues found |
+ |
+ Last issue |
+ |
+ Last update |
+
+
+
+
+
+
+
+
+ 1
+
+ example1@email.com
+
+ |
+ 26 |
+ • |
+ Bottleneck |
+ • |
+
+ Today, 1:20 pm
+ |
+
+
+
+
+
+
+ 2
+
+ example2@email.com
+
+ |
+ 20 |
+ • |
+ Code Nexus Point |
+ • |
+
+ May 5, 2:33 pm
+ |
+
+
+
+
+
+
+ 3
+
+ example3@email.com
+
+ |
+ 16 |
+ • |
+ Inefficient Query |
+ • |
+
+ May 5, 1:04 pm
+ |
+
+
+
+
+
+
diff --git a/digmathonLeaderboardEmpty.html b/digmathonLeaderboardEmpty.html
new file mode 100644
index 000000000..816270de3
--- /dev/null
+++ b/digmathonLeaderboardEmpty.html
@@ -0,0 +1,152 @@
+
+
+
+
+
Leaderboard
+
coming soon
+
+
+
Stay tuned
+
Digmathon results will appear here and update hourly.
+
+
+
+
diff --git a/src/components/Highlights/Highlights.stories.tsx b/src/components/Highlights/Highlights.stories.tsx
index 200d2385b..84fc6cd74 100644
--- a/src/components/Highlights/Highlights.stories.tsx
+++ b/src/components/Highlights/Highlights.stories.tsx
@@ -1,19 +1,41 @@
import { Meta, StoryObj } from "@storybook/react";
import { Highlights } from ".";
+import { featureFlagMinBackendVersions } from "../../featureFlags";
+import { FeatureFlag } from "../../types";
import { actions as mainActions } from "../Main/actions";
+import { ConfigContext, initialState } from "../common/App/ConfigContext";
+import { DeploymentType } from "../common/App/types";
import { mockedImpactData } from "./Impact/mockData";
import { mockedPerformanceData } from "./Performance/mockData";
import { mockedTopIssuesData } from "./TopIssues/mockData";
// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
+
+const mockedConfig = {
+ ...initialState,
+ backendInfo: {
+ applicationVersion:
+ featureFlagMinBackendVersions[FeatureFlag.ARE_IMPACT_HIGHLIGHTS_ENABLED],
+ deploymentType: DeploymentType.HELM,
+ centralize: true
+ }
+};
+
const meta: Meta = {
title: "Highlights/Highlights",
component: Highlights,
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: "fullscreen"
- }
+ },
+ decorators: [
+ (Story) => (
+
+
+
+ )
+ ]
};
export default meta;
diff --git a/src/components/Highlights/Impact/index.tsx b/src/components/Highlights/Impact/index.tsx
index b7b0f8c67..f56ec9dd7 100644
--- a/src/components/Highlights/Impact/index.tsx
+++ b/src/components/Highlights/Impact/index.tsx
@@ -146,5 +146,5 @@ export const Impact = () => {
return renderImpactCard(data.impactHighlights);
};
- return ;
+ return ;
};
diff --git a/src/components/Highlights/Performance/index.tsx b/src/components/Highlights/Performance/index.tsx
index 679d71de7..b69a4f860 100644
--- a/src/components/Highlights/Performance/index.tsx
+++ b/src/components/Highlights/Performance/index.tsx
@@ -125,7 +125,7 @@ export const Performance = () => {
return (
Performance}
+ header={Duration}
content={
columns={columns}
diff --git a/src/components/Highlights/common/Section/index.tsx b/src/components/Highlights/common/Section/index.tsx
index 62310a8ed..6eaa01b53 100644
--- a/src/components/Highlights/common/Section/index.tsx
+++ b/src/components/Highlights/common/Section/index.tsx
@@ -1,12 +1,15 @@
+import { isString } from "../../../../typeGuards/isString";
import * as s from "./styles";
import { SectionProps } from "./types";
export const Section = ({ title, toolbarContent, children }: SectionProps) => (
-
- {title}
- {toolbarContent}
-
+ {(isString(title) || toolbarContent) && (
+
+ {title}
+ {toolbarContent}
+
+ )}
{children}
);
diff --git a/src/components/Highlights/common/Section/types.ts b/src/components/Highlights/common/Section/types.ts
index 82e882810..0da7b5c2d 100644
--- a/src/components/Highlights/common/Section/types.ts
+++ b/src/components/Highlights/common/Section/types.ts
@@ -1,7 +1,7 @@
import { ReactNode } from "react";
export interface SectionProps {
- title: string;
+ title?: string;
toolbarContent?: ReactNode;
children: ReactNode;
}
diff --git a/src/components/Navigation/EnvironmentBar/index.tsx b/src/components/Navigation/EnvironmentBar/index.tsx
index af7d9c222..4a624e0ca 100644
--- a/src/components/Navigation/EnvironmentBar/index.tsx
+++ b/src/components/Navigation/EnvironmentBar/index.tsx
@@ -16,6 +16,10 @@ export const EnvironmentBar = (props: EnvironmentBarProps) => {
}
};
+ const environmentName = props.selectedEnvironment
+ ? props.selectedEnvironment.name
+ : "No environments";
+
return (
{
)}
- {props.selectedEnvironment ? (
-
-
- {props.selectedEnvironment.name}
-
-
- ) : (
- "No environments"
- )}
+
+ {environmentName}
+
theme.colors.v3.icon.disabled};
diff --git a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx
index 0205d3332..c108ee543 100644
--- a/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx
+++ b/src/components/RecentActivity/Digmathon/CongratulationsView/index.tsx
@@ -1,4 +1,5 @@
import { useContext, useEffect } from "react";
+import { isString } from "../../../../typeGuards/isString";
import { sendTrackingEvent } from "../../../../utils/actions/sendTrackingEvent";
import { sendUserActionTrackingEvent } from "../../../../utils/actions/sendUserActionTrackingEvent";
import { ConfigContext } from "../../../common/App/ConfigContext";
@@ -19,7 +20,7 @@ const getEmailURL = (
const subject = `Digmathon Challenge Completed! [${userId}]`;
const foundInsights = data
- .filter((x) => x.isFound)
+ .filter((x) => isString(x.foundAt))
.map((x) => x.data?.title || x.type)
.join(", ");
const body = [
diff --git a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx
index a4c596a04..536b3591c 100644
--- a/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx
+++ b/src/components/RecentActivity/Digmathon/Digmathon.stories.tsx
@@ -1,12 +1,31 @@
import { Meta, StoryObj } from "@storybook/react";
import { Digmathon } from ".";
import { actions } from "../actions";
+import { useDigmathonProgressData } from "../useDigmathonProgressData";
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,
+ decorators: [
+ () => {
+ const { data, getData, foundIssuesCount, isDigmathonCompleted } =
+ useDigmathonProgressData();
+
+ return (
+ {
+ return undefined;
+ }}
+ />
+ );
+ }
+ ],
parameters: {
// More on how to position stories at: https://storybook.js.org/docs/react/configure/story-layout
layout: "fullscreen"
@@ -19,35 +38,45 @@ type Story = StoryObj;
// More on writing stories with args: https://storybook.js.org/docs/react/writing-stories/args
export const Default: Story = {
- args: {
- getData: () => {
- return undefined;
- }
- },
play: () => {
window.setTimeout(() => {
window.postMessage({
type: "digma",
action: actions.SET_DIGMATHON_PROGRESS_DATA,
- payload: mockedDigmathonProgressData
+ payload: {
+ ...mockedDigmathonProgressData,
+ insights: mockedDigmathonProgressData.insights.slice(0, 4)
+ }
});
}, 0);
}
};
-export const Congratulations: Story = {
- args: {
- getData: () => {
- return undefined;
- }
- },
+export const HasNewData: Story = {
play: () => {
window.setTimeout(() => {
window.postMessage({
type: "digma",
action: actions.SET_DIGMATHON_PROGRESS_DATA,
- payload: mockedDigmathonProgressData
+ payload: {
+ ...mockedDigmathonProgressData,
+ insights: mockedDigmathonProgressData.insights.slice(0, 4)
+ }
});
}, 0);
+
+ window.setTimeout(() => {
+ window.postMessage({
+ type: "digma",
+ action: actions.SET_DIGMATHON_PROGRESS_DATA,
+ payload: {
+ ...mockedDigmathonProgressData,
+ insights: mockedDigmathonProgressData.insights.slice(0, 6),
+ lastUpdatedByUserAt: "2023-01-05T13:14:47.010Z"
+ }
+ });
+ }, 1000);
}
};
+
+export const Congratulations: Story = {};
diff --git a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts
index 205d0cd22..5d13b6dbe 100644
--- a/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts
+++ b/src/components/RecentActivity/Digmathon/DigmathonInsightCard/styles.ts
@@ -25,6 +25,7 @@ export const Container = styled.div`
box-sizing: border-box;
overflow: hidden;
position: relative;
+ transition-duration: 500ms;
`;
export const NumberContainer = styled.div`
diff --git a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx
index 6be8d3704..351fbb9f1 100644
--- a/src/components/RecentActivity/Digmathon/ProgressView/index.tsx
+++ b/src/components/RecentActivity/Digmathon/ProgressView/index.tsx
@@ -1,20 +1,77 @@
-import { useEffect } from "react";
+import { useContext, useEffect } from "react";
+import { CSSTransition } from "react-transition-group";
+import { isString } from "../../../../typeGuards/isString";
+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 { ChevronIcon } from "../../../common/icons/16px/ChevronIcon";
+import { DigmaLogoIcon } from "../../../common/icons/DigmaLogoIcon";
+import { Direction } from "../../../common/icons/types";
+import { Button } from "../../../common/v3/Button";
+import { actions } from "../../actions";
import { trackingEvents } from "../../tracking";
import { DigmathonInsightCard } from "../DigmathonInsightCard";
+import { getProgressEmailLink } from "../getProgressEmailLink";
import * as s from "./styles";
import { ProgressViewProps } from "./types";
-export const ProgressView = ({ data, foundIssuesCount }: ProgressViewProps) => {
+const DIGMATHON_URL = "https://www.digma.ai/digmathon";
+const DIGMATHON_LEADERBOARD_URL = "https://www.digma.ai/digmathon/#leaderboard";
+
+export const ProgressView = ({ data }: ProgressViewProps) => {
+ const config = useContext(ConfigContext);
useEffect(() => {
sendTrackingEvent(trackingEvents.DIGMATHON_PROGRESS_VIEWED);
}, []);
+ const handleFindOutMoreButtonClick = () => {
+ sendUserActionTrackingEvent(
+ trackingEvents.DIGMATHON_FIND_OUT_MORE_BUTTON_CLICKED
+ );
+ openURLInDefaultBrowser(DIGMATHON_URL);
+ };
+
+ const handleUpdateProgressButtonClick = () => {
+ sendUserActionTrackingEvent(
+ trackingEvents.DIGMATHON_UPDATE_PROGRESS_BUTTON_CLICKED
+ );
+ const anchorElement = document.createElement("a");
+ anchorElement.href = getProgressEmailLink(data.insights, config);
+ anchorElement.click();
+ anchorElement.remove();
+
+ window.sendMessageToDigma({
+ action: actions.UPDATE_DIGMATHON_PROGRESS_DATA
+ });
+ };
+
+ const handleDigmathonLeaderboard = () => {
+ sendUserActionTrackingEvent(
+ trackingEvents.DIGMATHON_LEADERBOARD_BUTTON_CLICKED
+ );
+ openURLInDefaultBrowser(DIGMATHON_LEADERBOARD_URL);
+ };
+
+ const foundIssuesCount = data.insights.filter((x) =>
+ isString(x.foundAt)
+ ).length;
+
+ const isNewProgressDataAvailable =
+ (data.insights.length > 0 && data.lastUpdatedByUserAt === null) ||
+ data.insights.some(
+ (x) =>
+ isString(x.foundAt) &&
+ isString(data.lastUpdatedByUserAt) &&
+ new Date(x.foundAt).valueOf() >=
+ new Date(data.lastUpdatedByUserAt).valueOf()
+ );
+
if (foundIssuesCount === 0) {
return (
-
+
Start Digmathon
@@ -22,6 +79,10 @@ export const ProgressView = ({ data, foundIssuesCount }: ProgressViewProps) => {
issues. Check back here to see your progress!
+
);
@@ -30,23 +91,52 @@ export const ProgressView = ({ data, foundIssuesCount }: ProgressViewProps) => {
return (
<>
- Search for issues
-
- Improve your code, and win a gift card
-
- {foundIssuesCount} out of{" "}
- {data.length} issues found
+
+ {foundIssuesCount}
+ {" "}
+ issues found
+
+
+ New issues found, please update the progress
+
+
+
+
+
+
+
+
+
- {data.map((x, i) =>
+ {data.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
index 1f1da0ce3..346518a42 100644
--- a/src/components/RecentActivity/Digmathon/ProgressView/styles.ts
+++ b/src/components/RecentActivity/Digmathon/ProgressView/styles.ts
@@ -1,21 +1,31 @@
-import styled from "styled-components";
+import styled, { css, keyframes } from "styled-components";
import {
bodyMediumTypography,
footnoteMediumTypography,
footnoteRegularTypography,
+ footnoteSemiboldTypography,
subscriptRegularTypography
} from "../../../common/App/typographies";
+import { Button } from "../../../common/v3/Button";
+import {
+ FoundIssuesNumberProps,
+ NewIssuesFoundMessageProps,
+ UpdateProgressButtonProps
+} from "./types";
+
+export const NEW_ISSUES_FOUND_MESSAGE_TRANSITION_DURATION = 500; //in milliseconds
+export const NEW_ISSUES_FOUND_MESSAGE_ANIMATION_CLASS_NAME =
+ "new-issues-found-message";
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;
+ padding: 8px 12px;
`;
export const HeaderTitle = styled.span`
@@ -27,11 +37,90 @@ export const HeaderDescription = styled.span`
`;
export const IssuesCounter = styled.span`
- margin-left: auto;
+ margin-right: auto;
+`;
+
+export const FoundIssuesNumber = styled.span`
+ ${({ theme, $isNew }) =>
+ $isNew
+ ? css`
+ ${footnoteSemiboldTypography}
+
+ color: ${theme.colors.v3.text.link};
+ `
+ : ""}
+`;
+
+export const NewIssuesFoundMessage = styled.div`
+ ${footnoteSemiboldTypography}
+
+ color: ${({ theme }) => theme.colors.v3.text.link};
+ display: flex;
+ align-items: center;
+
+ ${({ $transitionClassName, $transitionDuration }) => {
+ return `
+ &.${$transitionClassName}-enter {
+ opacity: 0;
+ transform: translateX(100px);
+ }
+ &.${$transitionClassName}-enter-active {
+ opacity: 1;
+ transform: translateX(0);
+ transition: all ${$transitionDuration}ms;
+ }
+ &.${$transitionClassName}-exit {
+ opacity: 1;
+ transform: translateX(0);
+ }
+ &.${$transitionClassName}-exit-active {
+ opacity: 0;
+ transform: translateX(100px);
+ transition: all ${$transitionDuration}ms;
+ }`;
+ }}
`;
-export const FoundIssuesNumber = styled.span`
- color: ${({ theme }) => theme.colors.v3.icon.brandSecondary};
+export const ButtonsContainer = styled.div`
+ display: flex;
+ gap: 8px;
+ align-items: center;
+ z-index: 1;
+`;
+
+const shineAnimation = keyframes`
+ 0% { left: -100%; }
+ 25% { left: 100%; }
+ 100% { left: 100%; }
+`;
+
+export const UpdateProgressButtonContainer = styled.div`
+ position: relative;
+ overflow: hidden;
+`;
+
+export const UpdateProgressButton = styled(Button)`
+ &::before {
+ content: "";
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(
+ 100deg,
+ rgb(255 255 255 / 0%) 30%,
+ rgb(255 255 255 / 35%),
+ rgb(255 255 255 / 0%) 70%
+ );
+
+ ${({ $isShining }) =>
+ $isShining
+ ? css`
+ animation: ${shineAnimation} 3s infinite linear;
+ `
+ : ""};
+ }
`;
export const CardsContainer = styled.div`
@@ -53,6 +142,7 @@ export const EmptyStateContentContainer = styled.div`
width: 290px;
display: flex;
flex-direction: column;
+ align-items: center;
gap: 12px;
`;
diff --git a/src/components/RecentActivity/Digmathon/ProgressView/types.ts b/src/components/RecentActivity/Digmathon/ProgressView/types.ts
index 86771d029..ada026782 100644
--- a/src/components/RecentActivity/Digmathon/ProgressView/types.ts
+++ b/src/components/RecentActivity/Digmathon/ProgressView/types.ts
@@ -1,6 +1,17 @@
-import { DigmathonInsightData } from "../../types";
+import { DigmathonProgressData } from "../../types";
export interface ProgressViewProps {
- data: DigmathonInsightData[];
- foundIssuesCount: number;
+ data: DigmathonProgressData;
+}
+export interface FoundIssuesNumberProps {
+ $isNew: boolean;
+}
+
+export interface NewIssuesFoundMessageProps {
+ $transitionClassName: string;
+ $transitionDuration: number;
+}
+
+export interface UpdateProgressButtonProps {
+ $isShining: boolean;
}
diff --git a/src/components/RecentActivity/Digmathon/getProgressEmailLink.ts b/src/components/RecentActivity/Digmathon/getProgressEmailLink.ts
new file mode 100644
index 000000000..22d78fb50
--- /dev/null
+++ b/src/components/RecentActivity/Digmathon/getProgressEmailLink.ts
@@ -0,0 +1,33 @@
+import { isString } from "../../../typeGuards/isString";
+import { ConfigContextData } from "../../common/App/types";
+import { DigmathonInsightData } from "../types";
+
+export const EMAIL_ADDRESS = "digmathon@digma.ai";
+const LINE_BREAK = "%0D%0A";
+
+export const getProgressEmailLink = (
+ insights: DigmathonInsightData[],
+ config: ConfigContextData
+) => {
+ const userId = config.userId || config.userRegistrationEmail || "";
+ const subject = `Digmathon Challenge [${userId}]`;
+
+ const foundInsights = insights
+ .filter((x) => isString(x.foundAt))
+ .sort(
+ (a, b) =>
+ new Date(a.foundAt as string).valueOf() -
+ new Date(b.foundAt as string).valueOf()
+ )
+ .map(
+ (x, i) =>
+ `${i + 1}) ${x.data?.title || x.type}${LINE_BREAK}Found at: ${new Date(
+ x.foundAt as string
+ ).toISOString()}`
+ )
+ .join(LINE_BREAK.repeat(2));
+
+ const body = ["Insights found:", foundInsights].join(LINE_BREAK);
+
+ return `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`;
+};
diff --git a/src/components/RecentActivity/Digmathon/index.tsx b/src/components/RecentActivity/Digmathon/index.tsx
index 34e9aba28..b468c0225 100644
--- a/src/components/RecentActivity/Digmathon/index.tsx
+++ b/src/components/RecentActivity/Digmathon/index.tsx
@@ -4,7 +4,7 @@ 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 { DigmathonProgressData } from "../types";
import { CongratulationsView } from "./CongratulationsView";
import { ProgressView } from "./ProgressView";
import * as s from "./styles";
@@ -13,7 +13,6 @@ import { DigmathonProgressProps } from "./types";
export const Digmathon = ({
data,
getData,
- foundIssuesCount,
isDigmathonCompleted,
onGoBack
}: DigmathonProgressProps) => {
@@ -35,11 +34,11 @@ export const Digmathon = ({
onGoBack();
};
- const renderContent = (data: DigmathonInsightData[]) =>
+ const renderContent = (data: DigmathonProgressData) =>
isDigmathonCompleted ? (
-
+
) : (
-
+
);
return (
diff --git a/src/components/RecentActivity/Digmathon/mockData.ts b/src/components/RecentActivity/Digmathon/mockData.ts
index 6cbc87177..1529d4292 100644
--- a/src/components/RecentActivity/Digmathon/mockData.ts
+++ b/src/components/RecentActivity/Digmathon/mockData.ts
@@ -1,21 +1,49 @@
import { InsightType } from "../../Insights/types";
-import { DigmathonProgressData } from "../types";
+import { SetDigmathonProgressDataPayload } from "../types";
-export const mockedDigmathonProgressData: DigmathonProgressData = {
+export const mockedDigmathonProgressData: SetDigmathonProgressDataPayload = {
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
- ]
+ { type: InsightType.SpanScaling, foundAt: "2024-01-05T13:14:47.010Z" },
+ { type: InsightType.SpanNexus, foundAt: "2024-01-05T13:14:47.010Z" },
+ {
+ type: InsightType.EndpointQueryOptimizationV2,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ {
+ type: InsightType.EndpointQueryOptimization,
+ foundAt: "2024-01-05T11:14:47.010Z"
+ },
+ {
+ type: InsightType.SpanQueryOptimization,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ { type: InsightType.HotSpot, foundAt: "2024-01-05T12:14:47.010Z" },
+ {
+ type: InsightType.EndpointSpanNPlusOne,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ {
+ type: InsightType.EndpointSpaNPlusOne,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ { type: InsightType.SpaNPlusOne, foundAt: "2024-01-05T13:14:47.010Z" },
+ {
+ type: InsightType.EndpointSessionInView,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ { type: InsightType.SpanUsages, foundAt: "2024-01-05T11:14:47.010Z" },
+ {
+ type: InsightType.EndpointHighNumberOfQueries,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ {
+ type: InsightType.EndpointBottleneck,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ },
+ {
+ type: InsightType.SpanEndpointBottleneck,
+ foundAt: "2024-01-05T13:14:47.010Z"
+ }
+ ],
+ lastUpdatedByUserAt: "2026-01-05T13:14:47.010Z"
};
diff --git a/src/components/RecentActivity/Digmathon/types.ts b/src/components/RecentActivity/Digmathon/types.ts
index 043c130c1..cad5d53c0 100644
--- a/src/components/RecentActivity/Digmathon/types.ts
+++ b/src/components/RecentActivity/Digmathon/types.ts
@@ -1,7 +1,7 @@
-import { DigmathonInsightData } from "../types";
+import { DigmathonProgressData } from "../types";
export interface DigmathonProgressProps {
- data?: DigmathonInsightData[];
+ data?: DigmathonProgressData;
getData: () => void;
foundIssuesCount: number;
isDigmathonCompleted: boolean;
diff --git a/src/components/RecentActivity/RecentActivity.stories.tsx b/src/components/RecentActivity/RecentActivity.stories.tsx
index b64db9bdb..a99790018 100644
--- a/src/components/RecentActivity/RecentActivity.stories.tsx
+++ b/src/components/RecentActivity/RecentActivity.stories.tsx
@@ -725,7 +725,8 @@ export const EnableDigmathonMode: Story = {
type: "digma",
action: actions.SET_DIGMATHON_PROGRESS_DATA,
payload: {
- insights: []
+ ...mockedDigmathonProgressData,
+ insights: mockedDigmathonProgressData.insights.slice(0, 4)
}
});
}
diff --git a/src/components/RecentActivity/actions.ts b/src/components/RecentActivity/actions.ts
index 4a9c7348a..902b1a596 100644
--- a/src/components/RecentActivity/actions.ts
+++ b/src/components/RecentActivity/actions.ts
@@ -24,5 +24,6 @@ export const actions = addPrefix(ACTION_PREFIX, {
SET_DIGMATHON_PROGRESS_DATA: "SET_DIGMATHON_PROGRESS_DATA",
OPEN_REGISTRATION_DIALOG: "OPEN_REGISTRATION_DIALOG",
CREATE_ENVIRONMENT: "CREATE_ENVIRONMENT",
- SET_CREATE_ENVIRONMENT_RESULT: "SET_CREATE_ENVIRONMENT_RESULT"
+ SET_CREATE_ENVIRONMENT_RESULT: "SET_CREATE_ENVIRONMENT_RESULT",
+ UPDATE_DIGMATHON_PROGRESS_DATA: "UPDATE_DIGMATHON_PROGRESS_DATA"
});
diff --git a/src/components/RecentActivity/tracking.ts b/src/components/RecentActivity/tracking.ts
index f957a6503..ed416e784 100644
--- a/src/components/RecentActivity/tracking.ts
+++ b/src/components/RecentActivity/tracking.ts
@@ -32,7 +32,13 @@ export const trackingEvents = addPrefix(
CREATE_ENVIRONMENT_CANCEL_CONFIRMATION_CANCEL_CLICKED:
"create environment cancel confirmation cancel clicked",
CREATE_ENVIRONMENT_CANCEL_CONFIRMATION_CLOSE_CLICKED:
- "create environment cancel confirmation close clicked"
+ "create environment cancel confirmation close clicked",
+ DIGMATHON_UPDATE_PROGRESS_BUTTON_CLICKED:
+ "digmathon update progress button clicked",
+ DIGMATHON_LEADERBOARD_BUTTON_CLICKED:
+ "digmathon leaderboard button clicked",
+ DIGMATHON_FIND_OUT_MORE_BUTTON_CLICKED:
+ "digmathon find out more button clicked"
},
" "
);
diff --git a/src/components/RecentActivity/types.ts b/src/components/RecentActivity/types.ts
index c6ddf76fd..c656b6b54 100644
--- a/src/components/RecentActivity/types.ts
+++ b/src/components/RecentActivity/types.ts
@@ -93,8 +93,14 @@ export interface ViewModeOptionProps {
$selected: boolean;
}
-export interface DigmathonProgressData {
- insights: InsightType[];
+export interface DigmathonProgressInsightData {
+ type: InsightType;
+ foundAt: string;
+}
+
+export interface SetDigmathonProgressDataPayload {
+ insights: DigmathonProgressInsightData[];
+ lastUpdatedByUserAt: string | null;
}
export interface DigmathonInsightCardData {
@@ -106,7 +112,12 @@ export interface DigmathonInsightCardData {
export interface DigmathonInsightData {
type: InsightType;
data: DigmathonInsightCardData | undefined;
- isFound: boolean;
+ foundAt: string | null;
+}
+
+export interface DigmathonProgressData {
+ insights: DigmathonInsightData[];
+ lastUpdatedByUserAt: string | null;
}
export interface CreateEnvironmentPayload {
diff --git a/src/components/RecentActivity/useDigmathonProgressData.ts b/src/components/RecentActivity/useDigmathonProgressData.ts
index 0e3183cfa..a4ea9f63f 100644
--- a/src/components/RecentActivity/useDigmathonProgressData.ts
+++ b/src/components/RecentActivity/useDigmathonProgressData.ts
@@ -2,12 +2,19 @@ import { useEffect, useState } from "react";
import { actions as globalActions } from "../../actions";
import { dispatcher } from "../../dispatcher";
import { usePrevious } from "../../hooks/usePrevious";
+import { isNull } from "../../typeGuards/isNull";
+import { isString } from "../../typeGuards/isString";
import { InsightType } from "../Insights/types";
import { getDigmathonInsightCardData } from "./Digmathon/getDigmathonInsightData";
import { actions } from "./actions";
-import { DigmathonInsightData, DigmathonProgressData } from "./types";
+import {
+ DigmathonInsightData,
+ DigmathonProgressData,
+ DigmathonProgressInsightData,
+ SetDigmathonProgressDataPayload
+} from "./types";
-const REQUIRED_COUNT_OF_FOUND_ISSUES = 3;
+// const REQUIRED_COUNT_OF_FOUND_ISSUES = 3;
const getData = () => {
window.sendMessageToDigma({
@@ -15,109 +22,136 @@ const getData = () => {
});
};
-const getIsFound = (
- data: DigmathonProgressData | undefined,
+const getMinFoundDate = (insights: DigmathonProgressInsightData[]) =>
+ insights.reduce((minDate, cur) => {
+ if (
+ isNull(minDate) ||
+ new Date(cur.foundAt).valueOf() < new Date(minDate).valueOf()
+ ) {
+ return cur.foundAt;
+ }
+
+ return minDate;
+ }, null as string | null);
+
+const getFoundAt = (
+ insights: DigmathonProgressInsightData[],
type: InsightType
) => {
- if (!data) {
- return false;
- }
-
switch (type) {
case InsightType.EndpointQueryOptimizationV2:
case InsightType.EndpointQueryOptimization:
- case InsightType.SpanQueryOptimization:
- return data.insights.some((x) =>
+ case InsightType.SpanQueryOptimization: {
+ const filteredInsights = insights.filter((x) =>
[
InsightType.EndpointQueryOptimizationV2,
InsightType.EndpointQueryOptimization,
InsightType.SpanQueryOptimization
- ].includes(x)
+ ].includes(x.type)
);
+ return getMinFoundDate(filteredInsights);
+ }
case InsightType.EndpointSpanNPlusOne:
case InsightType.EndpointSpaNPlusOne:
- case InsightType.SpaNPlusOne:
- return data.insights.some((x) =>
+ case InsightType.SpaNPlusOne: {
+ const filteredInsights = insights.filter((x) =>
[
InsightType.EndpointSpanNPlusOne,
InsightType.EndpointSpaNPlusOne,
InsightType.SpaNPlusOne
- ].includes(x)
+ ].includes(x.type)
);
+ return getMinFoundDate(filteredInsights);
+ }
case InsightType.EndpointBottleneck:
- case InsightType.SpanEndpointBottleneck:
- return data.insights.some((x) =>
+ case InsightType.SpanEndpointBottleneck: {
+ const filteredInsights = insights.filter((x) =>
[
InsightType.EndpointBottleneck,
InsightType.SpanEndpointBottleneck
- ].includes(x)
+ ].includes(x.type)
);
+ return getMinFoundDate(filteredInsights);
+ }
default:
- return data.insights.includes(type);
+ return insights.find((x) => x.type === type)?.foundAt || null;
}
};
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 [data, setData] = useState();
+ const foundIssuesCount =
+ data?.insights.filter((x) => isString(x.foundAt)).length || 0;
+ const isDigmathonCompleted = false;
+ // foundIssuesCount >= REQUIRED_COUNT_OF_FOUND_ISSUES;
const previousIsDigmathonCompleted = usePrevious(isDigmathonCompleted);
-
useEffect(() => {
const handleSetDigmaProgressData = (data: unknown) => {
- const payload = data as DigmathonProgressData;
+ const payload = data as SetDigmathonProgressDataPayload;
+
const insights: DigmathonInsightData[] = [
{
type: InsightType.SpanScaling,
data: getDigmathonInsightCardData(InsightType.SpanScaling),
- isFound: getIsFound(payload, InsightType.SpanScaling)
+ foundAt: getFoundAt(payload.insights, InsightType.SpanScaling)
},
{
type: InsightType.SpanNexus,
data: getDigmathonInsightCardData(InsightType.SpanNexus),
- isFound: getIsFound(payload, InsightType.SpanNexus)
+ foundAt: getFoundAt(payload.insights, InsightType.SpanNexus)
},
{
type: InsightType.SpanQueryOptimization,
data: getDigmathonInsightCardData(InsightType.SpanQueryOptimization),
- isFound: getIsFound(payload, InsightType.SpanQueryOptimization)
+ foundAt: getFoundAt(
+ payload.insights,
+ InsightType.SpanQueryOptimization
+ )
},
{
type: InsightType.HotSpot,
data: getDigmathonInsightCardData(InsightType.HotSpot),
- isFound: getIsFound(payload, InsightType.HotSpot)
+ foundAt: getFoundAt(payload.insights, InsightType.HotSpot)
},
{
type: InsightType.SpaNPlusOne,
data: getDigmathonInsightCardData(InsightType.SpaNPlusOne),
- isFound: getIsFound(payload, InsightType.SpaNPlusOne)
+ foundAt: getFoundAt(payload.insights, InsightType.SpaNPlusOne)
},
{
type: InsightType.EndpointSessionInView,
data: getDigmathonInsightCardData(InsightType.EndpointSessionInView),
- isFound: getIsFound(payload, InsightType.EndpointSessionInView)
+ foundAt: getFoundAt(
+ payload.insights,
+ InsightType.EndpointSessionInView
+ )
},
{
type: InsightType.SpanUsages,
data: getDigmathonInsightCardData(InsightType.SpanUsages),
- isFound: getIsFound(payload, InsightType.SpanUsages)
+ foundAt: getFoundAt(payload.insights, InsightType.SpanUsages)
},
{
type: InsightType.EndpointHighNumberOfQueries,
data: getDigmathonInsightCardData(
InsightType.EndpointHighNumberOfQueries
),
- isFound: getIsFound(payload, InsightType.EndpointHighNumberOfQueries)
+ foundAt: getFoundAt(
+ payload.insights,
+ InsightType.EndpointHighNumberOfQueries
+ )
},
{
type: InsightType.EndpointBottleneck,
data: getDigmathonInsightCardData(InsightType.EndpointBottleneck),
- isFound: getIsFound(payload, InsightType.EndpointBottleneck)
+ foundAt: getFoundAt(payload.insights, InsightType.EndpointBottleneck)
}
];
- setData(insights);
+ setData({
+ insights,
+ lastUpdatedByUserAt: payload.lastUpdatedByUserAt
+ });
};
dispatcher.addActionListener(
diff --git a/src/components/common/App/typographies.ts b/src/components/common/App/typographies.ts
index e27e0b0f3..1c7414578 100644
--- a/src/components/common/App/typographies.ts
+++ b/src/components/common/App/typographies.ts
@@ -121,6 +121,12 @@ export const footnoteMediumTypography = css`
line-height: ${typographies.footNote.lineHeight}px;
`;
+export const footnoteSemiboldTypography = css`
+ font-size: ${typographies.footNote.fontSize}px;
+ font-weight: ${typographies.footNote.fontWeight.semibold};
+ line-height: ${typographies.footNote.lineHeight}px;
+`;
+
export const footnoteBoldTypography = css`
font-size: ${typographies.footNote.fontSize}px;
font-weight: ${typographies.footNote.fontWeight.bold};