import { FilterOutlined } from '@ant-design/icons'; import { Button, Col } from 'antd'; import { StyledCol, StyledDiv, StyledDivider, StyledRow, StyledSpace, StyledTypography, } from 'components/Styled'; import * as StyledStyles from 'components/Styled/styles'; import GanttChart, { ITraceMetaData } from 'container/GantChart'; import { getNodeById } from 'container/GantChart/utils'; import Timeline from 'container/Timeline'; import TraceFlameGraph from 'container/TraceFlameGraph'; import dayjs from 'dayjs'; import useUrlQuery from 'hooks/useUrlQuery'; import { spanServiceNameToColorMapping } from 'lib/getRandomColor'; import history from 'lib/history'; import { map } from 'lodash-es'; import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants'; import React, { useEffect, useMemo, useState } from 'react'; import { ITraceForest, PayloadProps } from 'types/api/trace/getTraceItem'; import { getSpanTreeMetadata } from 'utils/getSpanTreeMetadata'; import { spanToTreeUtil } from 'utils/spanToTree'; import MissingSpansMessage from './Missingtrace'; import SelectedSpanDetails from './SelectedSpanDetails'; import * as styles from './styles'; import { FlameGraphMissingSpansContainer, GanttChartWrapper } from './styles'; import { formUrlParams, getSortedData, getTreeLevelsCount, IIntervalUnit, INTERVAL_UNITS, } from './utils'; function TraceDetail({ response }: TraceDetailProps): JSX.Element { const spanServiceColors = useMemo( () => spanServiceNameToColorMapping(response[0].events), [response], ); const urlQuery = useUrlQuery(); const [spanId] = useState(urlQuery.get('spanId')); const [intervalUnit, setIntervalUnit] = useState( INTERVAL_UNITS[0], ); // const [searchSpanString, setSearchSpanString] = useState(''); const [activeHoverId, setActiveHoverId] = useState(''); const [activeSelectedId, setActiveSelectedId] = useState(spanId || ''); const { levelDown, levelUp } = useMemo( () => ({ levelDown: urlQuery.get('levelDown'), levelUp: urlQuery.get('levelUp'), }), [urlQuery], ); const [treesData, setTreesData] = useState( spanToTreeUtil(response[0].events), ); const { treesData: tree, ...traceMetaData } = useMemo(() => { const sortedTreesData: ITraceForest = { spanTree: map(treesData.spanTree, (tree) => getSortedData(tree)), missingSpanTree: map( treesData.missingSpanTree, (tree) => getSortedData(tree) || [], ), }; // Note: Handle undefined /*eslint-disable */ return getSpanTreeMetadata(sortedTreesData, spanServiceColors); /* eslint-enable */ }, [treesData, spanServiceColors]); const [globalTraceMetadata] = useState({ ...traceMetaData, }); useEffect(() => { if (activeSelectedId) { history.replace({ pathname: history.location.pathname, search: `${formUrlParams({ spanId: activeSelectedId, levelUp, levelDown, })}`, }); } }, [activeSelectedId, levelDown, levelUp]); const getSelectedNode = useMemo(() => { return getNodeById(activeSelectedId, treesData); }, [activeSelectedId, treesData]); // const onSearchHandler = (value: string) => { // setSearchSpanString(value); // setTreeData(spanToTreeUtil(response[0].events)); // }; const onFocusSelectedSpanHandler = (): void => { const treeNode = getNodeById(activeSelectedId, tree); if (treeNode) { setTreesData(treeNode); } }; const onResetHandler = (): void => { setTreesData(spanToTreeUtil(response[0].events)); }; const hasMissingSpans = useMemo( (): boolean => tree.missingSpanTree && Array.isArray(tree.missingSpanTree) && tree.missingSpanTree.length > 0, [tree], ); return ( Trace Details {traceMetaData.totalSpans} Span {hasMissingSpans && } {map(tree.spanTree, (tree) => { return ( ); })} {hasMissingSpans && ( {map(tree.missingSpanTree, (tree) => { return ( ); })} )} {tree && traceMetaData.globalStart && dayjs(traceMetaData.globalStart).format('hh:mm:ss a MM/DD')} {map([...tree.spanTree, ...tree.missingSpanTree], (tree) => ( ))} {/* {map(tree.missingSpanTree, (tree) => ( ))} */} tree)} /> ); } interface TraceDetailProps { response: PayloadProps; } export default TraceDetail;