mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-15 23:01:28 +08:00

* feat: funnels list page basic UI * feat: get funnels list data from mock API, and handle data, loading and empty states * feat: implement funnel rename * chore: move useFunnels to hooks/TracesFunnels * feat: create traces funnels details basic page + funnel -> details redirection * fix: properly display created at in funnels list item + preventDefault * chore: add tab bar to trace funnel details page * chore: traces funnel details page overall skeleton * chore: traces funnel details results skeleton * fix: hide step count for add button only * feat: funnel details page steps and configuration (#7424) * chore: add a new tab for traces funnels * feat: funnels list page basic UI * feat: get funnels list data from mock API, and handle data, loading and empty states * feat: implement funnel rename * refactor: overall improvements * feat: implement sorting in traces funnels list page * feat: add sort column key and order to url params * chore: move useFunnels to hooks/TracesFunnels * feat: implement traces funnels search and refactor search and sort by extracting to custom hooks * chore: overall improvements to rename trace funnel modal * chore: make the rename input auto-focusable * feat: handle create funnel modal * feat: delete funnel modal and functionality * fix: fix the layout shift in funnel item caused by getContainer={false} * chore: overall improvements and use live api in traces funnels * feat: create traces funnels details basic page + funnel -> details redirection * fix: funnels traces light mode UI * fix: properly display created at in funnels list item + preventDefault * refactor: extract FunnelItemPopover into a separate component * chore: hide funnel tab from traces explorer * chore: add check to display trace funnels tab only in dev environment * chore: improve funnels modals light mode * chore: overall improvements * fix: properly pass funnel details link * chore: address PR review changes * chore: add tab bar to trace funnel details page * feat: funnel step UI with service, span, and where filters * feat: build radio button component * refactor: use the SignozRadioButton in funnel results -> step transitions radio buttons * feat: inter step config (i.e. latency type) UI * chore: improve steps header styles by removing divider width * feat: funnel steps title, description, popover UI + pass data from API * chore: update FilterSelect component to conditionally add url params and accept on change * fix: fix funnel step where clause and update the state variables for filters * chore: add support for isMultiple and fix the type in FilterSelect * feat: centralize the steps state management in StepsContent * fix: move steps state up + pass steps count from state * feat: implement auto save for updating the steps whenever any step changes * feat: implement auto save for validating steps if service name or span names change * feat: impelement funnel step removal * feat: implement add details modal for funnel steps * fix: fix the overflowing time range picker * feat: funnel details empty state * feat: add support for saving funnel description * chore: overall improvements * fix: fix the light mode styles * fix: fix the failing build + broken search UI * refactor: remove the reference of useLocation from traceFunnel item in TraceModulePage constant * fix: fix the issue of update steps getting triggered on initial render if we have filters * fix: fix the edge case of stale state causing filters to be re-added after removing * feat: funnel details page results (#7451) * feat: funnel metrics table component * feat: funnel metrics and steps transition metrics components UI * feat: funnel table component * feat: slowest traces and traces with error components * fix: overall light theme fixes * fix: fix the warning * chore: add empty and loading states to FunnelMetricsTable * feat: get overall funnel metrics from the API * fix: fix the empty state of funnel metrics table * feat: get data for slowest traces and traces with errors * fix: link trace id to trace details page * fix: get data for funnel step transition metrics and refactor the existing data fetching logic * refactor: add funnel context + overall refactoring and optimizations * refactor: move steps states to funnel context + handle empty and run funnel disabled states * feat: handle run funnel * fix: improve empty state * chore: rename isValidateStepsMutationLoading -> isValidateStepsLoading * chore: improve query key * fix: display loading state if funnel results are fetching * refactor: move steps validation fetching and states to the context API * fix: display loading state in funnel results while steps validation is fetching * fix: call validate steps API only on changing the service name or span name of any step * refactor: move validateStepsQuery key out of useEffect and update the dependencies * chore: centralize hasIncompleteSteps and run validate only if steps have service and spans * fix: handle all empty fields state + overall improvements * fix: handle long where query tags * feat: build the funnel result graph component * feat: build the funnel result graph component * feat: handle loading, error, empty states in funnel graph * fix: don't display change percentage if % is 0 * refactor: overall improvements * feat: get funnel steps graph data from API + move logic to custom hook * fix: improve empty and error states * fix: handle funnel graph legends width using css * fix: redirect to trace funnels list page on clicking delete from funnel details * fix: update the query cache while updating steps * fix: implement debounced search for funnel list search * fix: refetch steps graph data query on clicking run funnel / sync button * fix: improve the step footer spacing * chore: add gap between divider to inter-step-config * fix: handle loading state while fetching * feat: add span to funnel flow (from trace details page) (#7477) * chore: display add to funnel icon on hovering any span in trace details page * chore: add className to funnel item actions popover * feat: add funnels tab to trace details v2 tab bar * feat: add span to funnel flow * chore: hide actions popover button from funnel item in span -> funnel flows * chore: improve the funnel details UI in add span to funnel modal * fix: display empty state + don't redirect to funnels list on delete success + overall improvements * chore: add null check * fix: display add to funnel button based on feature flag * fix: display funnels tab in trace details based on feature flag * fix: remove maxTagCount * feat: change ms to ns * chore: address review comments * chore: remove feature flag and display trace funnels only in dev envirnoment * fix: handle restoring steps if updating funnel steps fail * refactor: update the get and delete funnel endpoints to adjust to the BE changes (#7697) * refactor: address review comments * fix: handle nested funnel response structure to fix missing funnel_id… (#7740) * fix: handle nested funnel response structure to fix missing funnel_id in updates Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> * chore: remove console.og Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> * chore: revert explicitly passing funnelId to updateFunnelSteps --------- Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> Co-authored-by: ahmadshaheer <ashaheerki@gmail.com> * chore: fix api endpoint Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> * refactor: incorporate the recent funnel details API changes (#7760) * chore: trace funnels feedback changes (#7772) * chore: change the copy from x traces to valid traces found / not found * chore: add open funnel button in add span to funnel modal * feat: display buttons for adding step details and funnel description + copy to clipboard * feat: highlight funnel graph column based on selected (total / error span) from the legend items * chore: trace funnel changes (#7780) * refactor: handle funnels list search on frontend * refactor: use funnel steps update API for adding / updating step title and description * feat: allow selecting user's typed option in trace funnel service and span name dropdowns * chore: properly render the -> between steps in funnel results * fix: sync funnel step name with add details modal text fields --------- Signed-off-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com> Co-authored-by: Yunus M <myounis.ar@live.com> Co-authored-by: Shivanshu Raj Shrivastava <shivanshu1333@gmail.com>
264 lines
6.3 KiB
TypeScript
264 lines
6.3 KiB
TypeScript
import { Color } from '@signozhq/design-tokens';
|
|
import { FunnelStepGraphMetrics } from 'api/traceFunnels';
|
|
import { Chart, ChartConfiguration } from 'chart.js';
|
|
import ChangePercentagePill from 'components/ChangePercentagePill/ChangePercentagePill';
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
const CHART_CONFIG: Partial<ChartConfiguration> = {
|
|
type: 'bar',
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
scales: {
|
|
x: {
|
|
stacked: true,
|
|
grid: {
|
|
display: false,
|
|
},
|
|
ticks: {
|
|
font: {
|
|
family: "'Geist Mono', monospace",
|
|
},
|
|
},
|
|
},
|
|
y: {
|
|
stacked: true,
|
|
beginAtZero: true,
|
|
grid: {
|
|
color: 'rgba(192, 193, 195, 0.04)',
|
|
},
|
|
ticks: {
|
|
font: {
|
|
family: "'Geist Mono', monospace",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
plugins: {
|
|
legend: {
|
|
display: false,
|
|
},
|
|
tooltip: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
};
|
|
|
|
interface UseFunnelGraphProps {
|
|
data: FunnelStepGraphMetrics | undefined;
|
|
hoveredBar?: { index: number; type: 'total' | 'error' } | null;
|
|
}
|
|
|
|
interface UseFunnelGraph {
|
|
successSteps: number[];
|
|
errorSteps: number[];
|
|
totalSteps: number;
|
|
canvasRef: React.RefObject<HTMLCanvasElement>;
|
|
renderLegendItem: (
|
|
step: number,
|
|
successSpans: number,
|
|
errorSpans: number,
|
|
prevTotalSpans: number,
|
|
legendHoverHandlers?: {
|
|
onTotalHover: () => void;
|
|
onErrorHover: () => void;
|
|
onLegendLeave: () => void;
|
|
},
|
|
) => JSX.Element;
|
|
}
|
|
|
|
function useFunnelGraph({
|
|
data,
|
|
hoveredBar,
|
|
}: UseFunnelGraphProps): UseFunnelGraph {
|
|
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
const chartRef = useRef<Chart | null>(null);
|
|
const [localHoveredBar, setLocalHoveredBar] = useState<{
|
|
index: number;
|
|
type: 'total' | 'error';
|
|
} | null>(null);
|
|
|
|
const getPercentageChange = useCallback(
|
|
(current: number, previous: number): number => {
|
|
if (previous === 0) return 0;
|
|
return Math.abs(Math.round(((current - previous) / previous) * 100));
|
|
},
|
|
[],
|
|
);
|
|
|
|
interface StepGraphData {
|
|
successSteps: number[];
|
|
errorSteps: number[];
|
|
totalSteps: number;
|
|
}
|
|
const getStepGraphData = useCallback((): StepGraphData => {
|
|
const successSteps: number[] = [];
|
|
const errorSteps: number[] = [];
|
|
let stepCount = 1;
|
|
|
|
if (!data) return { successSteps, errorSteps, totalSteps: 0 };
|
|
|
|
while (
|
|
data[`total_s${stepCount}_spans`] !== undefined &&
|
|
data[`total_s${stepCount}_errored_spans`] !== undefined
|
|
) {
|
|
const totalSpans = data[`total_s${stepCount}_spans`];
|
|
const erroredSpans = data[`total_s${stepCount}_errored_spans`];
|
|
const successSpans = totalSpans - erroredSpans;
|
|
|
|
successSteps.push(successSpans);
|
|
errorSteps.push(erroredSpans);
|
|
stepCount += 1;
|
|
}
|
|
|
|
return {
|
|
successSteps,
|
|
errorSteps,
|
|
totalSteps: stepCount - 1,
|
|
};
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
if (!canvasRef.current) return;
|
|
|
|
if (chartRef.current) {
|
|
chartRef.current.destroy();
|
|
}
|
|
|
|
const ctx = canvasRef.current.getContext('2d');
|
|
if (!ctx) return;
|
|
|
|
const { successSteps, errorSteps, totalSteps } = getStepGraphData();
|
|
|
|
chartRef.current = new Chart(ctx, {
|
|
...CHART_CONFIG,
|
|
data: {
|
|
labels: Array.from({ length: totalSteps }, (_, i) => String(i + 1)),
|
|
datasets: [
|
|
{
|
|
label: 'Success spans',
|
|
data: successSteps,
|
|
backgroundColor: successSteps.map(() => Color.BG_ROBIN_500),
|
|
stack: 'Stack 0',
|
|
borderRadius: 2,
|
|
borderSkipped: false,
|
|
},
|
|
{
|
|
label: 'Error spans',
|
|
data: errorSteps,
|
|
backgroundColor: errorSteps.map(() => Color.BG_CHERRY_500),
|
|
stack: 'Stack 0',
|
|
borderRadius: 2,
|
|
borderSkipped: false,
|
|
borderWidth: {
|
|
top: 2,
|
|
bottom: 2,
|
|
},
|
|
borderColor: 'rgba(0, 0, 0, 0)',
|
|
},
|
|
],
|
|
},
|
|
options: CHART_CONFIG.options,
|
|
} as ChartConfiguration);
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [data]);
|
|
|
|
useEffect(() => {
|
|
const chart = chartRef.current;
|
|
if (!chart) return;
|
|
|
|
const { successSteps, errorSteps } = getStepGraphData();
|
|
|
|
if (chart.data.datasets && chart.data.datasets.length >= 2) {
|
|
chart.data.datasets[0].backgroundColor = successSteps.map((_, i) =>
|
|
localHoveredBar &&
|
|
localHoveredBar.index === i &&
|
|
localHoveredBar.type === 'total'
|
|
? '#2655ff'
|
|
: Color.BG_ROBIN_500,
|
|
);
|
|
|
|
chart.data.datasets[1].backgroundColor = errorSteps.map((_, i) =>
|
|
localHoveredBar &&
|
|
localHoveredBar.index === i &&
|
|
localHoveredBar.type === 'error'
|
|
? '#ff1018'
|
|
: Color.BG_CHERRY_500,
|
|
);
|
|
|
|
chart.update();
|
|
}
|
|
}, [localHoveredBar, getStepGraphData]);
|
|
|
|
useEffect(() => {
|
|
setLocalHoveredBar(hoveredBar ?? null);
|
|
}, [hoveredBar]);
|
|
|
|
const renderLegendItem = useCallback(
|
|
(
|
|
step: number,
|
|
successSpans: number,
|
|
errorSpans: number,
|
|
prevTotalSpans: number,
|
|
legendHoverHandlers?: {
|
|
onTotalHover: () => void;
|
|
onErrorHover: () => void;
|
|
onLegendLeave: () => void;
|
|
},
|
|
): JSX.Element => {
|
|
const totalSpans = successSpans + errorSpans;
|
|
|
|
return (
|
|
<div key={step} className="funnel-graph__legend-column">
|
|
<div
|
|
className="legend-item"
|
|
onMouseEnter={legendHoverHandlers?.onTotalHover}
|
|
onMouseLeave={legendHoverHandlers?.onLegendLeave}
|
|
>
|
|
<div className="legend-item__left">
|
|
<span className="legend-item__dot legend-item--total" />
|
|
<span className="legend-item__label">Total spans</span>
|
|
</div>
|
|
<div className="legend-item__right">
|
|
<span className="legend-item__value">{totalSpans}</span>
|
|
{step > 1 && (
|
|
<ChangePercentagePill
|
|
direction={totalSpans < prevTotalSpans ? -1 : 1}
|
|
percentage={getPercentageChange(totalSpans, prevTotalSpans)}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div
|
|
className="legend-item"
|
|
onMouseEnter={legendHoverHandlers?.onErrorHover}
|
|
onMouseLeave={legendHoverHandlers?.onLegendLeave}
|
|
>
|
|
<div className="legend-item__left">
|
|
<span className="legend-item__dot legend-item--error" />
|
|
<span className="legend-item__label">Error spans</span>
|
|
</div>
|
|
<div className="legend-item__right">
|
|
<span className="legend-item__value">{errorSpans}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
},
|
|
[getPercentageChange],
|
|
);
|
|
|
|
const { successSteps, errorSteps, totalSteps } = getStepGraphData();
|
|
|
|
return {
|
|
successSteps,
|
|
errorSteps,
|
|
totalSteps,
|
|
canvasRef,
|
|
renderLegendItem,
|
|
};
|
|
}
|
|
|
|
export default useFunnelGraph;
|