diff --git a/src/components/Insights/common/InsightCard/InsightHeader/index.tsx b/src/components/Insights/common/InsightCard/InsightHeader/index.tsx index fd45b7ab8..39dbf884b 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/index.tsx +++ b/src/components/Insights/common/InsightCard/InsightHeader/index.tsx @@ -6,7 +6,6 @@ import { import { roundTo } from "../../../../../utils/roundTo"; import { ConfigContext } from "../../../../common/App/ConfigContext"; import { InfoCircleIcon } from "../../../../common/icons/InfoCircleIcon"; -import { WarningTriangleIcon } from "../../../../common/icons/WarningTriangleIcon"; import { Link } from "../../../../common/v3/Link"; import { NewTag } from "../../../../common/v3/NewTag"; import { Tag } from "../../../../common/v3/Tag"; @@ -17,8 +16,6 @@ import { InsightStatusBadge } from "./InsightStatusBadge"; import * as s from "./styles"; import { InsightHeaderProps } from "./types"; -const HIGH_CRITICALITY_THRESHOLD = 0.8; - const getTagType = (criticality: number): TagType => { if (criticality < 0.2) { return "lowSeverity"; @@ -68,7 +65,7 @@ export const InsightHeader = (props: InsightHeaderProps) => { /> )} - + {insightTypeInfo?.label} {insightTypeInfo?.description && ( }> @@ -77,15 +74,8 @@ export const InsightHeader = (props: InsightHeaderProps) => { )} - + - {props.criticality > HIGH_CRITICALITY_THRESHOLD && ( - - - - - - )} {props.isAsync && } {props.isNew && } {props.status && } diff --git a/src/components/Insights/common/InsightCard/InsightHeader/styles.ts b/src/components/Insights/common/InsightCard/InsightHeader/styles.ts index c5e186a24..d29632416 100644 --- a/src/components/Insights/common/InsightCard/InsightHeader/styles.ts +++ b/src/components/Insights/common/InsightCard/InsightHeader/styles.ts @@ -1,5 +1,5 @@ import styled from "styled-components"; -import { bodyMediumTypography } from "../../../../common/App/typographies"; +import { bodySemiboldTypography } from "../../../../common/App/typographies"; export const Container = styled.div` display: flex; @@ -26,8 +26,8 @@ export const InfoContainer = styled.div` display: flex; `; -export const Label = styled.div` - ${bodyMediumTypography} +export const Title = styled.div` + ${bodySemiboldTypography} display: flex; gap: 4px; @@ -41,8 +41,3 @@ export const BadgeContainer = styled.div` gap: 8px; height: 24px; `; - -export const WarningTriangleContainer = styled.div` - display: flex; - color: ${({ theme }) => theme.colors.v3.status.high}; -`; diff --git a/src/components/Insights/common/InsightCard/ProductionAffectionBar/ProductionAffectionBar.stories.tsx b/src/components/Insights/common/InsightCard/ProductionAffectionBar/ProductionAffectionBar.stories.tsx new file mode 100644 index 000000000..acc33650c --- /dev/null +++ b/src/components/Insights/common/InsightCard/ProductionAffectionBar/ProductionAffectionBar.stories.tsx @@ -0,0 +1,26 @@ +import { Meta, StoryObj } from "@storybook/react"; +import { ProductionAffectionBar } from "."; + +// More on how to set up stories at: https://storybook.js.org/docs/react/writing-stories/introduction +const meta: Meta = { + title: "Insights/common/InsightCard/ProductionAffectionBar", + component: ProductionAffectionBar, + 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; + +export const WithoutTicket: Story = { + args: {} +}; + +export const WithTicket: Story = { + args: { + isTicketCreated: true + } +}; diff --git a/src/components/Insights/common/InsightCard/ProductionAffectionBar/index.tsx b/src/components/Insights/common/InsightCard/ProductionAffectionBar/index.tsx new file mode 100644 index 000000000..07f89f3bf --- /dev/null +++ b/src/components/Insights/common/InsightCard/ProductionAffectionBar/index.tsx @@ -0,0 +1,24 @@ +import { SparkleIcon } from "../../../../common/icons/16px/SparkleIcon"; +import { Link } from "../../../../common/v3/Link"; +import * as s from "./styles"; +import { ProductionAffectionBarProps } from "./types"; + +export const ProductionAffectionBar = (props: ProductionAffectionBarProps) => { + const handleCreateTicketLinkClick = () => { + props.onCreateTicket(); + }; + + return ( + + + + + Could affect production + {props.isTicketCreated ? ( + Ticket Created + ) : ( + Create Ticket + )} + + ); +}; diff --git a/src/components/Insights/common/InsightCard/ProductionAffectionBar/styles.ts b/src/components/Insights/common/InsightCard/ProductionAffectionBar/styles.ts new file mode 100644 index 000000000..749f192fc --- /dev/null +++ b/src/components/Insights/common/InsightCard/ProductionAffectionBar/styles.ts @@ -0,0 +1,34 @@ +import styled from "styled-components"; +import { footnoteRegularTypography } from "../../../../common/App/typographies"; +import { ContainerProps } from "./types"; + +export const Container = styled.div` + ${footnoteRegularTypography} + + border-radius: 4px; + border: 1px solid ${({ theme }) => theme.colors.v3.status.high}; + background: ${({ theme, $isActive }) => + $isActive + ? theme.colors.v3.status.backgroundHigh + : theme.colors.v3.pieChart.darkRed}; + display: flex; + align-items: center; + padding: 6px 8px; + gap: 4px; +`; + +export const SparkleIconContainer = styled.div` + color: ${({ theme }) => theme.colors.v3.status.high}; +`; + +export const Title = styled.span` + color: ${({ theme }) => theme.colors.v3.text.primary}; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + margin-right: auto; +`; + +export const TicketStatus = styled.div` + color: ${({ theme }) => theme.colors.v3.text.secondary}; +`; diff --git a/src/components/Insights/common/InsightCard/ProductionAffectionBar/types.ts b/src/components/Insights/common/InsightCard/ProductionAffectionBar/types.ts new file mode 100644 index 000000000..818f68783 --- /dev/null +++ b/src/components/Insights/common/InsightCard/ProductionAffectionBar/types.ts @@ -0,0 +1,8 @@ +export interface ProductionAffectionBarProps { + isTicketCreated: boolean; + onCreateTicket: () => void; +} + +export interface ContainerProps { + $isActive: boolean; +} diff --git a/src/components/Insights/common/InsightCard/index.tsx b/src/components/Insights/common/InsightCard/index.tsx index 69cb6b49e..f0bb65233 100644 --- a/src/components/Insights/common/InsightCard/index.tsx +++ b/src/components/Insights/common/InsightCard/index.tsx @@ -21,9 +21,11 @@ import { usePrevious } from "../../../../hooks/usePrevious"; import { sendTrackingEvent } from "../../../../utils/sendTrackingEvent"; import { Spinner } from "../../../Navigation/CodeButtonMenu/Spinner"; import { trackingEvents } from "../../tracking"; +import { ProductionAffectionBar } from "./ProductionAffectionBar"; import { useDismissalHandler } from "./useDismissalHandler"; const IS_NEW_TIME_LIMIT = 1000 * 60 * 10; // in milliseconds +const HIGH_CRITICALITY_THRESHOLD = 0.8; export const InsightCard = (props: InsightCardProps) => { const [isRecalculatingStarted, setIsRecalculatingStarted] = useState(false); @@ -32,6 +34,8 @@ export const InsightCard = (props: InsightCardProps) => { const { isLoading, dismiss, show } = useDismissalHandler(props.insight.id); const previousLoading = usePrevious(isLoading); + const isCritical = props.insight.criticality > HIGH_CRITICALITY_THRESHOLD; + useEffect(() => { if (previousLoading && !isLoading) { props.onRefresh(props.insight.type); @@ -42,7 +46,7 @@ export const InsightCard = (props: InsightCardProps) => { props.onRefresh(props.insight.type); }; - const handleRecalculateClick = () => { + const handleRecheckButtonClick = () => { props.insight.prefixedCodeObjectId && props.onRecalculate && props.onRecalculate(props.insight.id); @@ -112,7 +116,7 @@ export const InsightCard = (props: InsightCardProps) => { }; const handleDismissClick = () => { - sendTrackingEvent(trackingEvents.DISMISS_BUTTON_CLICKED, { + sendTrackingEvent(trackingEvents.INSIGHT_CARD_DISMISS_BUTTON_CLICKED, { insightType: props.insight.type }); dismiss(); @@ -120,12 +124,31 @@ export const InsightCard = (props: InsightCardProps) => { }; const handleShowClick = () => { - sendTrackingEvent(trackingEvents.SHOW_BUTTON_CLICKED, { + sendTrackingEvent(trackingEvents.INSIGHT_CARD_SHOW_BUTTON_CLICKED, { insightType: props.insight.type }); show(); }; + const openTicketInfo = ( + spanCodeObjectId: string | undefined, + event: string + ) => { + if (props.onJiraButtonClick) { + props.onJiraButtonClick(spanCodeObjectId, event); + } + }; + + const handleCreateTicketLinkClick = () => { + sendTrackingEvent(trackingEvents.INSIGHT_CARD_CREATE_TICKET_LINK_CLICKED, { + insightType: props.insight.type + }); + openTicketInfo( + props.jiraTicketInfo?.spanCodeObjectId, + "create ticket link clicked" + ); + }; + const renderActions = () => { const buttonsToRender: { tooltip: string; @@ -152,7 +175,7 @@ export const InsightCard = (props: InsightCardProps) => {