201 lines
5.7 KiB
TypeScript
Raw Normal View History

Fe: Feat/trace detail (#764) * feat: new trace detail page flame graph * feat: new trace detail page layout * test: trace detail is wip * chore: trace details in wip * feat: trace detail page timeline component * chore: spantoTree is updated * chore: gantchart is updated * chore: onClick is added * chore: isSpanPresentInSearchString util is added * chore: trace graph is updated * chore: added the hack to work * feat: is span present util is added * chore: in span ms is added * chore: tooltip is updated * WIP: chore: trace details changes are updated * feat: getTraceItem is added * feat: trace detail page is updated * feat: trace detail styling changes * feat: trace detail page is updated * feat: implement span hover, select, focus and reset * feat: reset focus * feat: spanId as query table and unfurling * feat: trace details is updated * chore: remove lodash * chore: remove lodash * feat: trace details is updated * feat: new trace detail page styling changes * feat: new trace detail page styling changes * feat: improved styling * feat: remove horizontal scrolling * feat: new trace detail page modify caret icon * chore styles are updated * Revert "chore: Trace styles" * chore styles are updated * feat: timeline normalisation * chore: remove mock data * chore: sort tree data util is added and selected span component is updated * chore: trace changes are updated * chore: trace changes are updated * chore: trace changes are updated * feat: refactored time units for new trace detail page * chore: remove mockdata * feat: new trace detail page themeing and interval loop fix * chore: error tag is updated * chore: error tag is updated * chore: error tag is updated * chore: error tag is updated * chore: console is removed * fix: error tag expand button * chore: expanded panel is updated * feat: scroll span from gantt chart intoview * chore: trace detail is removed Co-authored-by: Pranshu Chittora <pranshu@signoz.io>
2022-03-03 19:04:23 +05:30
import React, { useEffect, useMemo, useState } from 'react';
import { Col, Divider, Row, Typography, Space, Button } from 'antd';
import { FilterOutlined } from '@ant-design/icons';
import GanttChart from 'container/GantChart';
import { getNodeById } from 'container/GantChart/utils';
import Timeline from 'container/Timeline';
import TraceFlameGraph from 'container/TraceFlameGraph';
import dayjs from 'dayjs';
import { spanServiceNameToColorMapping } from 'lib/getRandomColor';
import { getSortedData } from './utils';
import { ITraceTree, PayloadProps } from 'types/api/trace/getTraceItem';
import { getSpanTreeMetadata } from 'utils/getSpanTreeMetadata';
import { spanToTreeUtil } from 'utils/spanToTree';
import SelectedSpanDetails from './SelectedSpanDetails';
import useUrlQuery from 'hooks/useUrlQuery';
import styles from './TraceGraph.module.css';
import history from 'lib/history';
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants';
import { INTERVAL_UNITS } from './utils';
const TraceDetail = ({ response }: TraceDetailProps): JSX.Element => {
const spanServiceColors = useMemo(
() => spanServiceNameToColorMapping(response[0].events),
[response],
);
const urlQuery = useUrlQuery();
const [spanId, _setSpanId] = useState<string | null>(urlQuery.get('spanId'));
const [intervalUnit, setIntervalUnit] = useState(INTERVAL_UNITS[0]);
const [searchSpanString, setSearchSpanString] = useState('');
const [activeHoverId, setActiveHoverId] = useState<string>('');
const [activeSelectedId, setActiveSelectedId] = useState<string>(spanId || '');
const [treeData, setTreeData] = useState<ITraceTree>(
spanToTreeUtil(response[0].events),
);
const { treeData: tree, ...traceMetaData } = useMemo(() => {
return getSpanTreeMetadata(getSortedData(treeData), spanServiceColors);
}, [treeData]);
const [globalTraceMetadata, _setGlobalTraceMetadata] = useState<object>({
...traceMetaData,
});
useEffect(() => {
if (activeSelectedId) {
history.replace({
pathname: history.location.pathname,
search: `?spanId=${activeSelectedId}`,
});
}
}, [activeSelectedId]);
const getSelectedNode = useMemo(() => {
return getNodeById(activeSelectedId, treeData);
}, [activeSelectedId, treeData]);
const onSearchHandler = (value: string) => {
setSearchSpanString(value);
setTreeData(spanToTreeUtil(response[0].events));
};
const onFocusSelectedSpanHandler = () => {
const treeNode = getNodeById(activeSelectedId, tree);
if (treeNode) {
setTreeData(treeNode);
}
};
const onResetHandler = () => {
setTreeData(spanToTreeUtil(response[0].events));
};
return (
<Row style={{ flex: 1 }}>
<Col flex={'auto'} style={{ display: 'flex', flexDirection: 'column' }}>
<Row className={styles['trace-detail-content-spacing']}>
<Col
flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}
style={{ alignItems: 'center', display: 'flex', flexDirection: 'column' }}
>
<Typography.Title level={5} style={{ margin: 0 }}>
Trace Details
</Typography.Title>
<Typography.Text style={{ margin: 0 }}>
{traceMetaData.totalSpans} Span
</Typography.Text>
</Col>
<Col flex={'auto'}>
<TraceFlameGraph
treeData={tree}
traceMetaData={traceMetaData}
hoveredSpanId={activeHoverId}
selectedSpanId={activeSelectedId}
onSpanHover={setActiveHoverId}
onSpanSelect={setActiveSelectedId}
intervalUnit={intervalUnit}
/>
</Col>
</Row>
<Row style={{ marginTop: '2rem' }}>
<Col
flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}
style={{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
{dayjs(traceMetaData.globalStart / 1e6).format('hh:mm:ssa MM/DD')}
</Col>
<Col
flex="auto"
style={{ overflow: 'visible' }}
className={styles['trace-detail-content-spacing']}
>
<Timeline
globalTraceMetadata={globalTraceMetadata}
traceMetaData={traceMetaData}
intervalUnit={intervalUnit}
setIntervalUnit={setIntervalUnit}
/>
</Col>
<Divider style={{ height: '100%', margin: '0' }} />
</Row>
<Row
className={styles['trace-detail-content-spacing']}
style={{ margin: '1.5rem 1rem 0.5rem' }}
>
<Col flex={`${SPAN_DETAILS_LEFT_COL_WIDTH}px`}>
{/* <Search
placeholder="Type to filter.."
allowClear
onSearch={onSearchHandler}
style={{ width: 200 }}
/> */}
</Col>
<Col flex={'auto'}>
<Space
style={{
float: 'right',
}}
>
<Button onClick={onFocusSelectedSpanHandler} icon={<FilterOutlined />}>
Focus on selected span
</Button>
<Button type="default" onClick={onResetHandler}>
Reset Focus
</Button>
</Space>
</Col>
</Row>
<div
className={styles['trace-detail-content-spacing']}
style={{
display: 'flex',
flexDirection: 'column',
position: 'relative',
flex: 1,
overflowY: 'auto',
overflowX: 'hidden',
}}
>
<GanttChart
traceMetaData={traceMetaData}
data={tree}
activeSelectedId={activeSelectedId}
activeHoverId={activeHoverId}
setActiveHoverId={setActiveHoverId}
setActiveSelectedId={setActiveSelectedId}
spanId={spanId || ''}
intervalUnit={intervalUnit}
/>
</div>
</Col>
<Col>
<Divider style={{ height: '100%', margin: '0' }} type="vertical" />
</Col>
<Col
md={5}
sm={5}
style={{
height: '100%',
position: 'relative',
display: 'flex',
flexDirection: 'column',
}}
>
<SelectedSpanDetails tree={getSelectedNode} />
</Col>
</Row>
);
};
interface TraceDetailProps {
response: PayloadProps;
}
export default TraceDetail;