Skip to content
24 changes: 24 additions & 0 deletions src/components/Insights/common/InsightCard/InsightCard.stories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { Meta, StoryObj } from "@storybook/react";
import { InsightCard } from ".";
import { ConfigContext, InitialData } from "../../../common/App/ConfigContext";
import { DeploymentType } from "../../../common/App/types";
import { mockedEndpointNPlusOneInsight } from "../../EndpointNPlusOneInsight/mockData";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
Expand Down Expand Up @@ -69,3 +71,25 @@ export const Dismissed: Story = {
onOpenHistogram: undefined
}
};

export const WithNewVersion: Story = {
decorators: [
(Story) => (
<ConfigContext.Provider
value={{
...InitialData,
backendInfo: {
applicationVersion: "v0.2.243",
deploymentType: DeploymentType.DOCKER_COMPOSE
}
}}
>
<Story />
</ConfigContext.Provider>
)
],
args: {
isAsync: true,
insight: { ...mockedEndpointNPlusOneInsight, criticality: 0.9 }
}
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useContext } from "react";
import { formatTimeDistance } from "../../../../../utils/formatTimeDistance";
import {
InsightTypeInfo,
getInsightTypeInfo
Expand Down Expand Up @@ -74,6 +75,14 @@ export const InsightHeader = (props: InsightHeaderProps) => {
</s.InfoContainer>
</Tooltip>
)}
{props.lastUpdateTimer && (
<s.Description>
Updated:
<Tooltip title={new Date(props.lastUpdateTimer).toString()}>
<span>{formatTimeDistance(props.lastUpdateTimer)}</span>
</Tooltip>
</s.Description>
)}
</s.Title>
<s.BadgeContainer>
{props.isAsync && <AsyncTag />}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import styled from "styled-components";
import { bodySemiboldTypography } from "../../../../common/App/typographies";
import {
bodySemiboldTypography,
footnoteRegularTypography
} from "../../../../common/App/typographies";

export const Container = styled.div`
display: flex;
Expand Down Expand Up @@ -41,3 +44,10 @@ export const BadgeContainer = styled.div`
gap: 8px;
height: 24px;
`;

export const Description = styled.div`
${footnoteRegularTypography}
gap: 4px;
display: flex;
color: ${({ theme }) => theme.colors.v3.text.secondary};
`;
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export interface InsightHeaderProps {
spanInfo?: SpanInfo | null;
onSpanLinkClick: (spanCodeObjectId: string) => void;
status?: InsightStatus;
lastUpdateTimer?: string | null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Meta, StoryObj } from "@storybook/react";
import { RecalculateBar } from ".";

// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction
const meta: Meta<typeof RecalculateBar> = {
title: "Insights/common/InsightCard/RecalculateBar",
component: RecalculateBar,
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<typeof meta>;

export const Default: Story = {
args: {}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { RecalculateStartedIcon } from "../../../../common/icons/16px/RecalculateStartedIcon";
import * as s from "./styles";

export const RecalculateBar = () => {
return (
<s.Container>
<s.IconContainer>
<RecalculateStartedIcon size={16} color={"currentColor"} />
</s.IconContainer>
<s.Title>Rechecking insight</s.Title>
<s.Info>Check here for updates</s.Info>
</s.Container>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import styled from "styled-components";
import {
caption1RegularTypography,
footnoteBoldTypography
} from "../../../../common/App/typographies";

export const Container = styled.div`
${footnoteBoldTypography}

border-radius: 4px;
border: 1px solid ${({ theme }) => theme.colors.v3.status.low};
background: ${({ theme }) => theme.colors.v3.status.backgroundLow};
display: flex;
align-items: center;
padding: 6px 8px;
gap: 4px;
`;

export const IconContainer = styled.div`
color: ${({ theme }) => theme.colors.v3.status.low};
display: flex;
`;

export const Title = styled.span`
color: ${({ theme }) => theme.colors.v3.text.primary};
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
`;

export const TicketStatus = styled.div`
color: ${({ theme }) => theme.colors.v3.text.secondary};
`;

export const Info = styled.span`
${caption1RegularTypography}
`;
101 changes: 44 additions & 57 deletions src/components/Insights/common/InsightCard/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import React, { useEffect, useState } from "react";
import React, { useContext, useEffect, useState } from "react";
import { isString } from "../../../../typeGuards/isString";
import { formatTimeDistance } from "../../../../utils/formatTimeDistance";
import { TraceIcon } from "../../../common/icons/12px/TraceIcon";
import { HistogramIcon } from "../../../common/icons/16px/HistogramIcon";
import { LiveIcon } from "../../../common/icons/16px/LiveIcon";
Expand All @@ -10,18 +9,22 @@ import { CrossIcon } from "../../../common/icons/CrossIcon";
import { Button } from "../../../common/v3/Button";
import { BaseButtonProps } from "../../../common/v3/Button/types";
import { JiraButton } from "../../../common/v3/JiraButton";
import { Link } from "../../../common/v3/Link";
import { Tooltip } from "../../../common/v3/Tooltip";
import { isEndpointInsight, isSpanInsight } from "../../typeGuards";
import { InsightHeader } from "./InsightHeader";
import * as s from "./styles";
import { InsightCardProps } from "./types";

import { getFeatureFlagValue } from "../../../../featureFlags";
import { usePrevious } from "../../../../hooks/usePrevious";
import { FeatureFlag } from "../../../../types";
import { sendTrackingEvent } from "../../../../utils/sendTrackingEvent";
import { Spinner } from "../../../Navigation/CodeButtonMenu/Spinner";
import { ConfigContext } from "../../../common/App/ConfigContext";
import { trackingEvents } from "../../tracking";
import { InsightStatus } from "../../types";
import { ProductionAffectionBar } from "./ProductionAffectionBar";
import { RecalculateBar } from "./RecalculateBar";
import { useDismissalHandler } from "./useDismissalHandler";

const IS_NEW_TIME_LIMIT = 1000 * 60 * 10; // in milliseconds
Expand All @@ -33,24 +36,28 @@ export const InsightCard = (props: InsightCardProps) => {
useState(false);
const { isLoading, dismiss, show } = useDismissalHandler(props.insight.id);
const previousLoading = usePrevious(isLoading);
const config = useContext(ConfigContext);
const [insightStatus, setInsightStatus] = useState(props.insight.status);

const isCritical = props.insight.criticality > HIGH_CRITICALITY_THRESHOLD;
// TODO: remove and refresh the insight data
useEffect(() => {
setInsightStatus(props.insight.status);
}, [props.insight.status]);

useEffect(() => {
if (previousLoading && !isLoading) {
props.onRefresh(props.insight.type);
}
}, [isLoading, previousLoading, props.onRefresh]);

const handleRefreshLinkClick = () => {
props.onRefresh(props.insight.type);
};

const handleRecheckButtonClick = () => {
props.insight.prefixedCodeObjectId &&
props.onRecalculate &&
props.onRecalculate(props.insight.id);
setIsRecalculatingStarted(true);
// TODO: handle Recheck response and refresh the insight data
setInsightStatus(InsightStatus.InEvaluation);
};

const handleHistogramButtonClick = () => {
Expand All @@ -65,56 +72,38 @@ export const InsightCard = (props: InsightCardProps) => {
);
};

const handleSpanLinkClick = () => {
const getRecalculateVisibilityParams = () => {
const areStartTimesEqual =
props.insight.customStartTime &&
props.insight.actualStartTime &&
new Date(props.insight.actualStartTime).valueOf() -
new Date(props.insight.customStartTime).valueOf() ===
0;

if (
(isSpanInsight(props.insight) || isEndpointInsight(props.insight)) &&
props.insight.spanInfo
getFeatureFlagValue(config, FeatureFlag.IS_RECALCULATE_BUBBLE_ENABLED)
) {
props.onGoToSpan(props.insight.spanInfo.spanCodeObjectId);
return {
showTimer: areStartTimesEqual,
showBanner: insightStatus === InsightStatus.InEvaluation
};
}
};

const renderRecalculationBlock = (
actualStartTime: string,
customStartTime: string | null,
isRecalculatingStarted: boolean
) => {
if (!props.insight.customStartTime && !isRecalculatingStarted) {
return;
}
return {
showTimer: areStartTimesEqual,
showBanner:
!areStartTimesEqual &&
props.insight.actualStartTime &&
(props.insight.customStartTime || isRecalculatingStarted)
};
};

const handleSpanLinkClick = () => {
if (
isRecalculatingStarted ||
(customStartTime && customStartTime > actualStartTime)
(isSpanInsight(props.insight) || isEndpointInsight(props.insight)) &&
props.insight.spanInfo
) {
return (
<s.RefreshContainer>
<s.Description>
Applying the new time filter. Wait a few minutes and then refresh.
</s.Description>
<span>
<Link onClick={handleRefreshLinkClick}>Refresh</Link>
</span>
</s.RefreshContainer>
);
}

const areStartTimesEqual =
customStartTime &&
new Date(actualStartTime).valueOf() -
new Date(customStartTime).valueOf() ===
0;

if (areStartTimesEqual) {
const title = new Date(actualStartTime).toString();
return (
<s.Description>
Data from:{" "}
<Tooltip title={title}>
<span>{formatTimeDistance(actualStartTime)}</span>
</Tooltip>
</s.Description>
);
props.onGoToSpan(props.insight.spanInfo.spanCodeObjectId);
}
};

Expand Down Expand Up @@ -257,6 +246,8 @@ export const InsightCard = (props: InsightCardProps) => {
);
};

const { showBanner, showTimer } = getRecalculateVisibilityParams();

const isNew = isString(props.insight.firstDetected)
? Date.now() - new Date(props.insight.firstDetected).valueOf() <
IS_NEW_TIME_LIMIT
Expand All @@ -271,13 +262,14 @@ export const InsightCard = (props: InsightCardProps) => {
? props.insight.spanInfo
: undefined
}
status={props.insight.status}
status={insightStatus}
isNew={isNew}
isAsync={props.isAsync}
insightType={props.insight.type}
importance={props.insight.importance}
criticality={props.insight.criticality}
onSpanLinkClick={handleSpanLinkClick}
lastUpdateTimer={showTimer ? props.insight.actualStartTime : null}
/>
}
content={
Expand All @@ -288,12 +280,7 @@ export const InsightCard = (props: InsightCardProps) => {
onCreateTicket={handleCreateTicketLinkClick}
/>
)}
{props.insight.actualStartTime &&
renderRecalculationBlock(
props.insight.actualStartTime,
props.insight.customStartTime,
isRecalculatingStarted
)}
{showBanner && <RecalculateBar />}
{props.content}
</s.ContentContainer>
}
Expand Down
6 changes: 4 additions & 2 deletions src/components/common/App/ConfigContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { isEnvironment } from "../../../typeGuards/isEnvironment";
import { isString } from "../../../typeGuards/isString";
import { ConfigContextData } from "./types";

export const ConfigContext = createContext<ConfigContextData>({
export const InitialData = {
digmaApiUrl: isString(window.digmaApiUrl) ? window.digmaApiUrl : "",
digmaApiProxyPrefix: isString(window.digmaApiProxyPrefix)
? window.digmaApiProxyPrefix
Expand All @@ -28,4 +28,6 @@ export const ConfigContext = createContext<ConfigContextData>({
scope: undefined,
isMicrometerProject: window.isMicrometerProject === true,
state: undefined
});
};

export const ConfigContext = createContext<ConfigContextData>(InitialData);
6 changes: 6 additions & 0 deletions src/components/common/App/typographies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,9 @@ export const bodySemiboldTypography = css`
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;
`;
Loading