Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
55928b2
feat: funnels list page basic UI
ahmadshaheer Mar 16, 2025
685f103
feat: get funnels list data from mock API, and handle data, loading a…
ahmadshaheer Mar 16, 2025
be2b21a
feat: implement funnel rename
ahmadshaheer Mar 16, 2025
220465c
chore: move useFunnels to hooks/TracesFunnels
ahmadshaheer Mar 17, 2025
ee39d26
feat: create traces funnels details basic page + funnel -> details re…
ahmadshaheer Mar 17, 2025
458b3e7
fix: properly display created at in funnels list item + preventDefault
ahmadshaheer Mar 17, 2025
030b9ce
chore: add tab bar to trace funnel details page
ahmadshaheer Mar 19, 2025
291708b
chore: traces funnel details page overall skeleton
ahmadshaheer Mar 19, 2025
d32dcef
chore: traces funnel details results skeleton
ahmadshaheer Mar 19, 2025
0ea7109
fix: hide step count for add button only
ahmadshaheer Mar 19, 2025
8d348c6
feat: funnel details page steps and configuration (#7424)
ahmadshaheer Mar 31, 2025
051fe6c
chore: add null check
YounixM Mar 31, 2025
bf04e35
fix: display add to funnel button based on feature flag
ahmadshaheer Apr 3, 2025
b4f0265
fix: display funnels tab in trace details based on feature flag
ahmadshaheer Apr 3, 2025
9a5b786
fix: remove maxTagCount
ahmadshaheer Apr 6, 2025
d6a9686
feat: change ms to ns
ahmadshaheer Apr 8, 2025
cbab8c2
chore: address review comments
ahmadshaheer Apr 8, 2025
d745ed0
chore: remove feature flag and display trace funnels only in dev envi…
ahmadshaheer Apr 16, 2025
ffc8f79
fix: handle restoring steps if updating funnel steps fail
ahmadshaheer Apr 21, 2025
b1f350a
refactor: update the get and delete funnel endpoints to adjust to the…
ahmadshaheer Apr 22, 2025
f3c896f
refactor: address review comments
ahmadshaheer Apr 22, 2025
5f28489
fix: handle nested funnel response structure to fix missing funnel_id…
shivanshuraj1333 Apr 27, 2025
ed03082
chore: fix api endpoint
shivanshuraj1333 Apr 28, 2025
5ff5ea7
refactor: incorporate the recent funnel details API changes (#7760)
ahmadshaheer May 6, 2025
ca39aba
chore: trace funnels feedback changes (#7772)
ahmadshaheer May 6, 2025
4d6a053
chore: trace funnel changes (#7780)
ahmadshaheer May 6, 2025
86c0bdb
Merge branch 'main' into feat/funnel-details-page
ahmadshaheer May 11, 2025
2a8aba5
Merge branch 'main' into feat/funnel-details-page
sawhil May 11, 2025
3a12c95
Merge branch 'main' into feat/funnel-details-page
ahmadshaheer May 11, 2025
d1ed777
chore: fix the failing tests
ahmadshaheer May 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@
"less": "^4.1.2",
"less-loader": "^10.2.0",
"lodash-es": "^4.17.21",
"lucide-react": "0.427.0",
"lucide-react": "0.498.0",
"mini-css-extract-plugin": "2.4.5",
"motion": "12.4.13",
"overlayscrollbars": "^2.8.1",
Expand Down
2 changes: 2 additions & 0 deletions frontend/public/Icons/empty-funnel-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/public/Icons/funnel-add.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions frontend/public/Icons/solid-info-circle.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion frontend/src/AppRoutes/pageComponents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,10 @@ export const TracesFunnels = Loadable(
import(/* webpackChunkName: "Traces Funnels" */ 'pages/TracesModulePage'),
);
export const TracesFunnelDetails = Loadable(
// eslint-disable-next-line sonarjs/no-identical-functions
() =>
import(
/* webpackChunkName: "Traces Funnel Details" */ 'pages/TracesFunnelDetails'
/* webpackChunkName: "Traces Funnel Details" */ 'pages/TracesModulePage'
),
);

Expand Down
286 changes: 258 additions & 28 deletions frontend/src/api/traceFunnels/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CreateFunnelPayload,
CreateFunnelResponse,
FunnelData,
FunnelStepData,
} from 'types/api/traceFunnels';

const FUNNELS_BASE_PATH = '/trace-funnels';
Expand All @@ -13,7 +14,7 @@ export const createFunnel = async (
payload: CreateFunnelPayload,
): Promise<SuccessResponse<CreateFunnelResponse> | ErrorResponse> => {
const response: AxiosResponse = await axios.post(
`${FUNNELS_BASE_PATH}/new-funnel`,
`${FUNNELS_BASE_PATH}/new`,
payload,
);

Expand All @@ -25,60 +26,45 @@ export const createFunnel = async (
};
};

interface GetFunnelsListParams {
search?: string;
}

export const getFunnelsList = async ({
search = '',
}: GetFunnelsListParams = {}): Promise<
export const getFunnelsList = async (): Promise<
SuccessResponse<FunnelData[]> | ErrorResponse
> => {
const params = new URLSearchParams();
if (search.length) {
params.set('search', search);
}

const response: AxiosResponse = await axios.get(
`${FUNNELS_BASE_PATH}/list${
params.toString() ? `?${params.toString()}` : ''
}`,
);
const response: AxiosResponse = await axios.get(`${FUNNELS_BASE_PATH}/list`);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
payload: response.data.data,
};
};

export const getFunnelById = async (
funnelId: string,
funnelId?: string,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.get(
`${FUNNELS_BASE_PATH}/get/${funnelId}`,
`${FUNNELS_BASE_PATH}/${funnelId}`,
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
payload: response.data.data,
};
};

interface RenameFunnelPayload {
id: string;
export interface RenameFunnelPayload {
funnel_id: string;
funnel_name: string;
timestamp: number;
}

export const renameFunnel = async (
payload: RenameFunnelPayload,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.put(
`${FUNNELS_BASE_PATH}/${payload.id}/update`,
{ funnel_name: payload.funnel_name },
`${FUNNELS_BASE_PATH}/${payload.funnel_id}`,
payload,
);

return {
Expand All @@ -97,7 +83,7 @@ export const deleteFunnel = async (
payload: DeleteFunnelPayload,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.delete(
`${FUNNELS_BASE_PATH}/delete/${payload.id}`,
`${FUNNELS_BASE_PATH}/${payload.id}`,
);

return {
Expand All @@ -107,3 +93,247 @@ export const deleteFunnel = async (
payload: response.data,
};
};

export interface UpdateFunnelStepsPayload {
funnel_id: string;
steps: FunnelStepData[];
timestamp: number;
}

export const updateFunnelSteps = async (
payload: UpdateFunnelStepsPayload,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.put(
`${FUNNELS_BASE_PATH}/steps/update`,
payload,
);

return {
statusCode: 200,
error: null,
message: 'Funnel steps updated successfully',
payload: response.data.data,
};
};

export interface ValidateFunnelPayload {
start_time: number;
end_time: number;
}

export interface ValidateFunnelResponse {
status: string;
data: Array<{
timestamp: string;
data: {
trace_id: string;
};
}> | null;
}

export const validateFunnelSteps = async (
funnelId: string,
payload: ValidateFunnelPayload,
signal?: AbortSignal,
): Promise<SuccessResponse<ValidateFunnelResponse> | ErrorResponse> => {
const response = await axios.post(
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/validate`,
payload,
{ signal },
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
};
};

export interface UpdateFunnelStepDetailsPayload {
funnel_id: string;
steps: Array<{
step_name: string;
description: string;
}>;
updated_at: number;
}

interface UpdateFunnelDescriptionPayload {
funnel_id: string;
description: string;
}

export const saveFunnelDescription = async (
payload: UpdateFunnelDescriptionPayload,
): Promise<SuccessResponse<FunnelData> | ErrorResponse> => {
const response: AxiosResponse = await axios.post(
`${FUNNELS_BASE_PATH}/save`,
payload,
);

return {
statusCode: 200,
error: null,
message: 'Funnel description updated successfully',
payload: response.data,
};
};

export interface FunnelOverviewPayload {
start_time: number;
end_time: number;
step_start?: number;
step_end?: number;
}

export interface FunnelOverviewResponse {
status: string;
data: Array<{
timestamp: string;
data: {
avg_duration: number;
avg_rate: number;
conversion_rate: number | null;
errors: number;
p99_latency: number;
};
}>;
}

export const getFunnelOverview = async (
funnelId: string,
payload: FunnelOverviewPayload,
signal?: AbortSignal,
): Promise<SuccessResponse<FunnelOverviewResponse> | ErrorResponse> => {
const response = await axios.post(
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/overview`,
payload,
{
signal,
},
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
};
};

export interface SlowTracesPayload {
start_time: number;
end_time: number;
step_a_order: number;
step_b_order: number;
}

export interface SlowTraceData {
status: string;
data: Array<{
timestamp: string;
data: {
duration_ms: string;
span_count: number;
trace_id: string;
};
}>;
}

export const getFunnelSlowTraces = async (
funnelId: string,
payload: SlowTracesPayload,
signal?: AbortSignal,
): Promise<SuccessResponse<SlowTraceData> | ErrorResponse> => {
const response = await axios.post(
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/slow-traces`,
payload,
{
signal,
},
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
};
};
export interface ErrorTracesPayload {
start_time: number;
end_time: number;
step_a_order: number;
step_b_order: number;
}

export interface ErrorTraceData {
status: string;
data: Array<{
timestamp: string;
data: {
duration_ms: string;
span_count: number;
trace_id: string;
};
}>;
}

export const getFunnelErrorTraces = async (
funnelId: string,
payload: ErrorTracesPayload,
signal?: AbortSignal,
): Promise<SuccessResponse<ErrorTraceData> | ErrorResponse> => {
const response: AxiosResponse = await axios.post(
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/error-traces`,
payload,
{
signal,
},
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
};
};

export interface FunnelStepsPayload {
start_time: number;
end_time: number;
}

export interface FunnelStepGraphMetrics {
[key: `total_s${number}_spans`]: number;
[key: `total_s${number}_errored_spans`]: number;
}

export interface FunnelStepsResponse {
status: string;
data: Array<{
timestamp: string;
data: FunnelStepGraphMetrics;
}>;
}

export const getFunnelSteps = async (
funnelId: string,
payload: FunnelStepsPayload,
signal?: AbortSignal,
): Promise<SuccessResponse<FunnelStepsResponse> | ErrorResponse> => {
const response = await axios.post(
`${FUNNELS_BASE_PATH}/${funnelId}/analytics/steps`,
payload,
{ signal },
);

return {
statusCode: 200,
error: null,
message: '',
payload: response.data,
};
};
Loading
Loading