From 2515a4b3acc3ef7d4b1fc5832c58060435a9df37 Mon Sep 17 00:00:00 2001 From: Vedant Gupta <49195734+veds-g@users.noreply.github.com> Date: Tue, 21 Jan 2025 21:10:22 +0530 Subject: [PATCH 1/3] feat: contextual flow for metrics (#2345) Signed-off-by: veds-g --- .../common/MetricsModalWrapper/index.tsx | 60 ++++ .../partials/MetricsModal/index.tsx | 109 +++++++ .../common/MetricsModalWrapper/style.css | 7 + .../partials/VertexDetails/index.tsx | 307 ++++++++++-------- .../VertexDetails/partials/Buffers/index.tsx | 22 +- .../Pods/partials/PodDetails/index.tsx | 25 +- .../PodDetails/partials/Metrics/index.tsx | 48 ++- .../Metrics/partials/LineChart/index.tsx | 190 +++++++---- 8 files changed, 558 insertions(+), 210 deletions(-) create mode 100644 ui/src/components/common/MetricsModalWrapper/index.tsx create mode 100644 ui/src/components/common/MetricsModalWrapper/partials/MetricsModal/index.tsx create mode 100644 ui/src/components/common/MetricsModalWrapper/style.css diff --git a/ui/src/components/common/MetricsModalWrapper/index.tsx b/ui/src/components/common/MetricsModalWrapper/index.tsx new file mode 100644 index 0000000000..fe29be5c7d --- /dev/null +++ b/ui/src/components/common/MetricsModalWrapper/index.tsx @@ -0,0 +1,60 @@ +import React, { useCallback, useState } from "react"; +import Tooltip from "@mui/material/Tooltip"; +import Box from "@mui/material/Box"; +import { MetricsModal } from "./partials/MetricsModal"; + +import "./style.css"; + +interface MetricsModalWrapperProps { + namespaceId: string; + pipelineId: string; + vertexId: string; + type: string; + metricName: string; + value: any; +} + +export function MetricsModalWrapper({ + namespaceId, + pipelineId, + vertexId, + type, + metricName, + value, +}: MetricsModalWrapperProps) { + const [open, setOpen] = useState(false); + + const handleOpen = useCallback(() => { + setOpen(true); + }, []); + const handleClose = useCallback(() => { + setOpen(false); + }, []); + + return ( + + + Click to get more information about the trend + + } + placement={"top-start"} + arrow + > + handleOpen()}> + {value} + + + + + ); +} diff --git a/ui/src/components/common/MetricsModalWrapper/partials/MetricsModal/index.tsx b/ui/src/components/common/MetricsModalWrapper/partials/MetricsModal/index.tsx new file mode 100644 index 0000000000..c3c77405fb --- /dev/null +++ b/ui/src/components/common/MetricsModalWrapper/partials/MetricsModal/index.tsx @@ -0,0 +1,109 @@ +import React, { useCallback, useContext, useState } from "react"; +import Box from "@mui/material/Box"; +import Modal from "@mui/material/Modal"; +import IconButton from "@mui/material/IconButton"; +import CloseIcon from "@mui/icons-material/Close"; +import { Metrics } from "../../../../pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/partials/Metrics"; +import { + VertexDetailsContext, + VertexDetailsContextProps, +} from "../../../SlidingSidebar/partials/VertexDetails"; +import { metricNameMap } from "../../../../pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/partials/Metrics/utils/constants"; + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + height: "60%", + width: "80%", + bgcolor: "background.paper", + boxShadow: 24, + p: 4, +}; + +interface MetricsModalProps { + open: boolean; + handleClose: () => void; + metricName: string; + namespaceId: string; + pipelineId: string; + vertexId: string; + type: string; +} + +export function MetricsModal({ + open, + handleClose, + metricName, + namespaceId, + pipelineId, + vertexId, + type, +}: MetricsModalProps) { + const vertexDetailsContext = + useContext(VertexDetailsContext); + const { setVertexTab, setPodsViewTab, setExpanded } = vertexDetailsContext; + + const [metricsFound, setMetricsFound] = useState(false); + + const handleRedirect = useCallback(() => { + handleClose(); + setVertexTab(0); + setPodsViewTab(1); + const panelId = `${metricName}-panel`; + setExpanded((prevExpanded) => { + const newExpanded = new Set(prevExpanded); + newExpanded.add(panelId); + return newExpanded; + }); + }, [handleClose, setVertexTab, setPodsViewTab, metricName, setExpanded]); + + return ( + + + + {metricNameMap[metricName]} + + + + + + + + {metricsFound && ( + + Click to see detailed view with additional filters + + )} + + + ); +} diff --git a/ui/src/components/common/MetricsModalWrapper/style.css b/ui/src/components/common/MetricsModalWrapper/style.css new file mode 100644 index 0000000000..e69d847a57 --- /dev/null +++ b/ui/src/components/common/MetricsModalWrapper/style.css @@ -0,0 +1,7 @@ + .metrics-hyperlink { + color: #0077C5; + text-decoration-color: #0077C5; + text-decoration: underline dotted; + width: fit-content; + cursor: pointer; + } \ No newline at end of file diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx index 6113140894..7cf01e13f2 100644 --- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx +++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/index.tsx @@ -1,4 +1,12 @@ -import React, { useCallback, useEffect, useMemo, useState } from "react"; +import React, { + createContext, + Dispatch, + SetStateAction, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; import Tabs from "@mui/material/Tabs"; import Tab from "@mui/material/Tab"; import Box from "@mui/material/Box"; @@ -43,6 +51,25 @@ export interface VertexDetailsProps { refresh: () => void; } +export interface VertexDetailsContextProps { + setVertexTab: Dispatch>; + podsViewTab: number; + setPodsViewTab: Dispatch>; + expanded: Set; + setExpanded: Dispatch>>; +} + +export const VertexDetailsContext = createContext({ + // eslint-disable-next-line @typescript-eslint/no-empty-function + setVertexTab: () => {}, + podsViewTab: 0, + // eslint-disable-next-line @typescript-eslint/no-empty-function + setPodsViewTab: () => {}, + expanded: new Set(), + // eslint-disable-next-line @typescript-eslint/no-empty-function + setExpanded: () => {}, +}); + export function VertexDetails({ namespaceId, pipelineId, @@ -56,7 +83,9 @@ export function VertexDetails({ }: VertexDetailsProps) { const [vertexSpec, setVertexSpec] = useState(); const [vertexType, setVertexType] = useState(); - const [tabValue, setTabValue] = useState(PODS_VIEW_TAB_INDEX); + const [tabValue, setTabValue] = useState(PODS_VIEW_TAB_INDEX); + const [podsViewTab, setPodsViewTab] = useState(0); + const [expanded, setExpanded] = useState>(new Set()); const [updateModalOnClose, setUpdateModalOnClose] = useState< SpecEditorModalProps | undefined >(); @@ -158,6 +187,10 @@ export function VertexDetails({ setUpdateModalOpen(true); } else { setTabValue(newValue); + if (tabValue === PODS_VIEW_TAB_INDEX) { + setPodsViewTab(0); + setExpanded(new Set()); + } } }, [tabValue, updateModalOnClose] @@ -186,151 +219,171 @@ export function VertexDetails({ ); return ( - - {header} - - - - - - {buffers && ( + - )} - - - - + + - )} - {updateModalOnClose && updateModalOpen && ( - - )} - + {buffers && ( + + )} + {updateModalOnClose && updateModalOpen && ( + + )} + + ); } diff --git a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/Buffers/index.tsx b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/Buffers/index.tsx index 79a753d8a0..2af20243dd 100644 --- a/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/Buffers/index.tsx +++ b/ui/src/components/common/SlidingSidebar/partials/VertexDetails/partials/Buffers/index.tsx @@ -6,12 +6,23 @@ import TableBody from "@mui/material/TableBody"; import TableCell from "@mui/material/TableCell"; import TableHead from "@mui/material/TableHead"; import TableRow from "@mui/material/TableRow"; +import { MetricsModalWrapper } from "../../../../../MetricsModalWrapper"; export interface BuffersProps { buffers: any[]; + namespaceId: string; + pipelineId: string; + vertexId: string; + type: string; } -export function Buffers({ buffers }: BuffersProps) { +export function Buffers({ + buffers, + namespaceId, + pipelineId, + vertexId, + type, +}: BuffersProps) { if (!buffers) { return
{`No resources found.`}
; } @@ -72,7 +83,14 @@ export function Buffers({ buffers }: BuffersProps) { {bufferUsage}% - {buffer?.totalMessages} + ); diff --git a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/index.tsx b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/index.tsx index 5732eb2520..9fa53f6537 100644 --- a/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/index.tsx +++ b/ui/src/components/pages/Pipeline/partials/Graph/partials/NodeInfo/partials/Pods/partials/PodDetails/index.tsx @@ -1,4 +1,4 @@ -import React, { useContext, useState } from "react"; +import React, { useContext } from "react"; import Box from "@mui/material/Box"; import Tab from "@mui/material/Tab"; import Tabs from "@mui/material/Tabs"; @@ -7,6 +7,10 @@ import { PodLogs } from "./partials/PodLogs"; import { PodDetailProps } from "../../../../../../../../../../../types/declarations/pods"; import { AppContextProps } from "../../../../../../../../../../../types/declarations/app"; import { AppContext } from "../../../../../../../../../../../App"; +import { + VertexDetailsContext, + VertexDetailsContextProps, +} from "../../../../../../../../../../common/SlidingSidebar/partials/VertexDetails"; import "./style.css"; @@ -32,9 +36,10 @@ export function PodDetail({ const { disableMetricsCharts } = useContext(AppContext); - const [selectedTab, setSelectedTab] = useState(0); + const { podsViewTab, setPodsViewTab } = + useContext(VertexDetailsContext); const handleTabChange = (_: any, newValue: number) => { - setSelectedTab(newValue); + setPodsViewTab(newValue); }; return ( @@ -47,13 +52,13 @@ export function PodDetail({ }} >