diff --git a/frontend/src/api/traceFunnels/index.ts b/frontend/src/api/traceFunnels/index.ts index d1cb0a205784..63623eccaaf8 100644 --- a/frontend/src/api/traceFunnels/index.ts +++ b/frontend/src/api/traceFunnels/index.ts @@ -119,6 +119,7 @@ export const updateFunnelSteps = async ( export interface ValidateFunnelPayload { start_time: number; end_time: number; + steps: FunnelStepData[]; } export interface ValidateFunnelResponse { @@ -132,12 +133,11 @@ export interface ValidateFunnelResponse { } export const validateFunnelSteps = async ( - funnelId: string, payload: ValidateFunnelPayload, signal?: AbortSignal, ): Promise | ErrorResponse> => { const response = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/validate`, + `${FUNNELS_BASE_PATH}/analytics/validate`, payload, { signal }, ); @@ -185,6 +185,7 @@ export interface FunnelOverviewPayload { end_time: number; step_start?: number; step_end?: number; + steps: FunnelStepData[]; } export interface FunnelOverviewResponse { @@ -202,12 +203,11 @@ export interface FunnelOverviewResponse { } export const getFunnelOverview = async ( - funnelId: string, payload: FunnelOverviewPayload, signal?: AbortSignal, ): Promise | ErrorResponse> => { const response = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/overview`, + `${FUNNELS_BASE_PATH}/analytics/overview`, payload, { signal, @@ -235,12 +235,11 @@ export interface SlowTraceData { } export const getFunnelSlowTraces = async ( - funnelId: string, payload: FunnelOverviewPayload, signal?: AbortSignal, ): Promise | ErrorResponse> => { const response = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/slow-traces`, + `${FUNNELS_BASE_PATH}/analytics/slow-traces`, payload, { signal, @@ -273,7 +272,7 @@ export const getFunnelErrorTraces = async ( signal?: AbortSignal, ): Promise | ErrorResponse> => { const response: AxiosResponse = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/error-traces`, + `${FUNNELS_BASE_PATH}/analytics/error-traces`, payload, { signal, @@ -291,6 +290,7 @@ export const getFunnelErrorTraces = async ( export interface FunnelStepsPayload { start_time: number; end_time: number; + steps: FunnelStepData[]; } export interface FunnelStepGraphMetrics { @@ -307,12 +307,11 @@ export interface FunnelStepsResponse { } export const getFunnelSteps = async ( - funnelId: string, payload: FunnelStepsPayload, signal?: AbortSignal, ): Promise | ErrorResponse> => { const response = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/steps`, + `${FUNNELS_BASE_PATH}/analytics/steps`, payload, { signal }, ); @@ -330,6 +329,7 @@ export interface FunnelStepsOverviewPayload { end_time: number; step_start?: number; step_end?: number; + steps: FunnelStepData[]; } export interface FunnelStepsOverviewResponse { @@ -341,12 +341,11 @@ export interface FunnelStepsOverviewResponse { } export const getFunnelStepsOverview = async ( - funnelId: string, payload: FunnelStepsOverviewPayload, signal?: AbortSignal, ): Promise | ErrorResponse> => { const response = await axios.post( - `${FUNNELS_BASE_PATH}/${funnelId}/analytics/steps/overview`, + `${FUNNELS_BASE_PATH}/analytics/steps/overview`, payload, { signal }, ); diff --git a/frontend/src/constants/localStorage.ts b/frontend/src/constants/localStorage.ts index ee3f1000d622..1511bf3b4b25 100644 --- a/frontend/src/constants/localStorage.ts +++ b/frontend/src/constants/localStorage.ts @@ -30,5 +30,5 @@ export enum LOCALSTORAGE { SHOW_EXCEPTIONS_QUICK_FILTERS = 'SHOW_EXCEPTIONS_QUICK_FILTERS', BANNER_DISMISSED = 'BANNER_DISMISSED', QUICK_FILTERS_SETTINGS_ANNOUNCEMENT = 'QUICK_FILTERS_SETTINGS_ANNOUNCEMENT', - UNEXECUTED_FUNNELS = 'UNEXECUTED_FUNNELS', + FUNNEL_STEPS = 'FUNNEL_STEPS', } diff --git a/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.styles.scss b/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.styles.scss index 1e90d6b50bbc..f7712562673e 100644 --- a/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.styles.scss +++ b/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.styles.scss @@ -241,6 +241,15 @@ &-title { color: var(--bg-ink-500); } + + &-footer { + border-top-color: var(--bg-vanilla-300); + background: var(--bg-vanilla-100); + .add-span-to-funnel-modal__discard-button { + background: var(--bg-vanilla-200); + color: var(--bg-ink-500); + } + } } } diff --git a/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.tsx b/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.tsx index 4e4f89c5586c..92d3f5d7801d 100644 --- a/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.tsx +++ b/frontend/src/container/TraceWaterfall/AddSpanToFunnelModal/AddSpanToFunnelModal.tsx @@ -72,7 +72,6 @@ function FunnelDetailsView({ funnel={funnel} isTraceDetailsPage span={span} - disableAutoSave triggerAutoSave={triggerAutoSave} showNotifications={showNotifications} /> @@ -143,13 +142,19 @@ function AddSpanToFunnelModal({ const handleSaveFunnel = (): void => { setTriggerSave(true); // Reset trigger after a brief moment to allow the save to be processed - setTimeout(() => setTriggerSave(false), 100); + setTimeout(() => { + setTriggerSave(false); + onClose(); + }, 100); }; const handleDiscard = (): void => { setTriggerDiscard(true); // Reset trigger after a brief moment - setTimeout(() => setTriggerDiscard(false), 100); + setTimeout(() => { + setTriggerDiscard(false); + onClose(); + }, 100); }; const renderListView = (): JSX.Element => ( @@ -239,9 +244,6 @@ function AddSpanToFunnelModal({ footer={ activeView === ModalView.DETAILS ? [ - , - ) : ( - - )} + ); diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelGraph.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelGraph.tsx index fc4364116034..3e1318014a55 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelGraph.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelGraph.tsx @@ -29,13 +29,20 @@ Chart.register( ); function FunnelGraph(): JSX.Element { - const { funnelId } = useFunnelContext(); + const { funnelId, startTime, endTime, steps } = useFunnelContext(); + + const payload = { + start_time: startTime, + end_time: endTime, + steps, + }; + const { data: stepsData, isLoading, isFetching, isError, - } = useFunnelStepsGraphData(funnelId); + } = useFunnelStepsGraphData(funnelId, payload); const data = useMemo(() => stepsData?.payload?.data?.[0]?.data, [ stepsData?.payload?.data, diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelResults.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelResults.tsx index 2b2ce59c362a..2050424013ad 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelResults.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelResults.tsx @@ -16,7 +16,6 @@ function FunnelResults(): JSX.Element { isValidateStepsLoading, hasIncompleteStepFields, hasAllEmptyStepFields, - hasFunnelBeenExecuted, funnelId, } = useFunnelContext(); @@ -47,14 +46,6 @@ function FunnelResults(): JSX.Element { /> ); } - if (!hasFunnelBeenExecuted) { - return ( - - ); - } return (
diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelTopTracesTable.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelTopTracesTable.tsx index 51797a13dc61..b6fab8a99b3f 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelTopTracesTable.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/FunnelTopTracesTable.tsx @@ -7,6 +7,7 @@ import { useFunnelContext } from 'pages/TracesFunnels/FunnelContext'; import { useMemo } from 'react'; import { UseQueryResult } from 'react-query'; import { ErrorResponse, SuccessResponse } from 'types/api'; +import { FunnelStepData } from 'types/api/traceFunnels'; import FunnelTable from './FunnelTable'; import { topTracesTableColumns } from './utils'; @@ -24,6 +25,7 @@ interface FunnelTopTracesTableProps { SuccessResponse | ErrorResponse, Error >; + steps: FunnelStepData[]; } function FunnelTopTracesTable({ @@ -32,6 +34,7 @@ function FunnelTopTracesTable({ stepBOrder, title, tooltip, + steps, useQueryHook, }: FunnelTopTracesTableProps): JSX.Element { const { startTime, endTime } = useFunnelContext(); @@ -41,8 +44,9 @@ function FunnelTopTracesTable({ end_time: endTime, step_start: stepAOrder, step_end: stepBOrder, + steps, }), - [startTime, endTime, stepAOrder, stepBOrder], + [startTime, endTime, stepAOrder, stepBOrder, steps], ); const { data: response, isLoading, isFetching } = useQueryHook( diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/OverallMetrics.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/OverallMetrics.tsx index bcb473776ad9..d6fdfd3e13a1 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/OverallMetrics.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/OverallMetrics.tsx @@ -6,7 +6,7 @@ import FunnelMetricsTable from './FunnelMetricsTable'; function OverallMetrics(): JSX.Element { const { funnelId } = useParams<{ funnelId: string }>(); const { isLoading, metricsData, conversionRate, isError } = useFunnelMetrics({ - funnelId: funnelId || '', + funnelId, }); return ( diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/StepsTransitionResults.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/StepsTransitionResults.tsx index 26e549f5c1ee..a508e7c7db81 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/StepsTransitionResults.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/StepsTransitionResults.tsx @@ -52,11 +52,13 @@ function StepsTransitionResults(): JSX.Element { funnelId={funnelId} stepAOrder={stepAOrder} stepBOrder={stepBOrder} + steps={steps} />
diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopSlowestTraces.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopSlowestTraces.tsx index 8067fa5f7112..c36acae2955f 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopSlowestTraces.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopSlowestTraces.tsx @@ -1,4 +1,5 @@ import { useFunnelSlowTraces } from 'hooks/TracesFunnels/useFunnels'; +import { FunnelStepData } from 'types/api/traceFunnels'; import FunnelTopTracesTable from './FunnelTopTracesTable'; @@ -6,6 +7,7 @@ interface TopSlowestTracesProps { funnelId: string; stepAOrder: number; stepBOrder: number; + steps: FunnelStepData[]; } function TopSlowestTraces(props: TopSlowestTracesProps): JSX.Element { diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopTracesWithErrors.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopTracesWithErrors.tsx index 55dcf4db5d05..d3b8bcb7fd11 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopTracesWithErrors.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/TopTracesWithErrors.tsx @@ -1,4 +1,5 @@ import { useFunnelErrorTraces } from 'hooks/TracesFunnels/useFunnels'; +import { FunnelStepData } from 'types/api/traceFunnels'; import FunnelTopTracesTable from './FunnelTopTracesTable'; @@ -6,6 +7,7 @@ interface TopTracesWithErrorsProps { funnelId: string; stepAOrder: number; stepBOrder: number; + steps: FunnelStepData[]; } function TopTracesWithErrors(props: TopTracesWithErrorsProps): JSX.Element { diff --git a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/utils.tsx b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/utils.tsx index 6b4cbb3dd32d..0d4eeb598a68 100644 --- a/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/utils.tsx +++ b/frontend/src/pages/TracesFunnelDetails/components/FunnelResults/utils.tsx @@ -18,10 +18,4 @@ export const topTracesTableColumns = [ key: 'duration_ms', render: (value: string): string => getYAxisFormattedValue(value, 'ms'), }, - { - title: 'SPAN COUNT', - dataIndex: 'span_count', - key: 'span_count', - render: (value: number): string => value.toString(), - }, ]; diff --git a/frontend/src/pages/TracesFunnelDetails/constants.ts b/frontend/src/pages/TracesFunnelDetails/constants.ts index 4cad0b313133..33b539e4744c 100644 --- a/frontend/src/pages/TracesFunnelDetails/constants.ts +++ b/frontend/src/pages/TracesFunnelDetails/constants.ts @@ -14,8 +14,6 @@ export const initialStepsData: FunnelStepData[] = [ latency_pointer: 'start', latency_type: undefined, has_errors: false, - name: '', - description: '', }, { id: v4(), @@ -29,8 +27,6 @@ export const initialStepsData: FunnelStepData[] = [ latency_pointer: 'start', latency_type: LatencyOptions.P95, has_errors: false, - name: '', - description: '', }, ]; diff --git a/frontend/src/pages/TracesFunnels/FunnelContext.tsx b/frontend/src/pages/TracesFunnels/FunnelContext.tsx index 423f33966877..021ecf4da73c 100644 --- a/frontend/src/pages/TracesFunnels/FunnelContext.tsx +++ b/frontend/src/pages/TracesFunnels/FunnelContext.tsx @@ -1,15 +1,15 @@ import logEvent from 'api/common/logEvent'; import { ValidateFunnelResponse } from 'api/traceFunnels'; -import { LOCALSTORAGE } from 'constants/localStorage'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import { Time } from 'container/TopNav/DateTimeSelection/config'; import { CustomTimeType, Time as TimeV2, } from 'container/TopNav/DateTimeSelectionV2/config'; +import { normalizeSteps } from 'hooks/TracesFunnels/useFunnelConfiguration'; import { useValidateFunnelSteps } from 'hooks/TracesFunnels/useFunnels'; -import { useLocalStorage } from 'hooks/useLocalStorage'; import getStartEndRangeTime from 'lib/getStartEndRangeTime'; +import { isEqual } from 'lodash-es'; import { initialStepsData } from 'pages/TracesFunnelDetails/constants'; import { createContext, @@ -41,6 +41,9 @@ interface FunnelContextType { handleStepChange: (index: number, newStep: Partial) => void; handleStepRemoval: (index: number) => void; handleRunFunnel: () => void; + handleSaveFunnel: () => void; + triggerSave: boolean; + hasUnsavedChanges: boolean; validationResponse: | SuccessResponse | ErrorResponse @@ -54,8 +57,10 @@ interface FunnelContextType { spanName: string, ) => void; handleRestoreSteps: (oldSteps: FunnelStepData[]) => void; - hasFunnelBeenExecuted: boolean; - setHasFunnelBeenExecuted: Dispatch>; + isUpdatingFunnel: boolean; + setIsUpdatingFunnel: Dispatch>; + lastUpdatedSteps: FunnelStepData[]; + setLastUpdatedSteps: Dispatch>; } const FunnelContext = createContext(undefined); @@ -86,6 +91,19 @@ export function FunnelProvider({ const funnel = data?.payload; const initialSteps = funnel?.steps?.length ? funnel.steps : initialStepsData; const [steps, setSteps] = useState(initialSteps); + const [triggerSave, setTriggerSave] = useState(false); + const [isUpdatingFunnel, setIsUpdatingFunnel] = useState(false); + const [lastUpdatedSteps, setLastUpdatedSteps] = useState( + initialSteps, + ); + + // Check if there are unsaved changes by comparing with initial steps from API + const hasUnsavedChanges = useMemo(() => { + const normalizedCurrentSteps = normalizeSteps(steps); + const normalizedInitialSteps = normalizeSteps(lastUpdatedSteps); + return !isEqual(normalizedCurrentSteps, normalizedInitialSteps); + }, [steps, lastUpdatedSteps]); + const { hasIncompleteStepFields, hasAllEmptyStepFields } = useMemo( () => ({ hasAllEmptyStepFields: steps.every( @@ -98,15 +116,6 @@ export function FunnelProvider({ [steps], ); - const [unexecutedFunnels, setUnexecutedFunnels] = useLocalStorage( - LOCALSTORAGE.UNEXECUTED_FUNNELS, - [], - ); - - const [hasFunnelBeenExecuted, setHasFunnelBeenExecuted] = useState( - !unexecutedFunnels.includes(funnelId), - ); - const { data: validationResponse, isLoading: isValidationLoading, @@ -116,7 +125,13 @@ export function FunnelProvider({ selectedTime, startTime, endTime, - enabled: !!funnelId && !!selectedTime && !!startTime && !!endTime, + enabled: + !!funnelId && + !!selectedTime && + !!startTime && + !!endTime && + !hasIncompleteStepFields, + steps, }); const validTracesCount = useMemo( @@ -185,11 +200,7 @@ export function FunnelProvider({ const handleRunFunnel = useCallback(async (): Promise => { if (validTracesCount === 0) return; - if (!hasFunnelBeenExecuted) { - setUnexecutedFunnels(unexecutedFunnels.filter((id) => id !== funnelId)); - setHasFunnelBeenExecuted(true); - } queryClient.refetchQueries([ REACT_QUERY_KEY.GET_FUNNEL_OVERVIEW, funnelId, @@ -215,15 +226,13 @@ export function FunnelProvider({ funnelId, selectedTime, ]); - }, [ - funnelId, - hasFunnelBeenExecuted, - unexecutedFunnels, - queryClient, - selectedTime, - setUnexecutedFunnels, - validTracesCount, - ]); + }, [funnelId, queryClient, selectedTime, validTracesCount]); + + const handleSaveFunnel = useCallback(() => { + setTriggerSave(true); + // Reset the trigger after a brief moment to allow useFunnelConfiguration to pick it up + setTimeout(() => setTriggerSave(false), 100); + }, []); const value = useMemo( () => ({ @@ -239,14 +248,19 @@ export function FunnelProvider({ handleAddStep: addNewStep, handleStepRemoval, handleRunFunnel, + handleSaveFunnel, + triggerSave, validationResponse, isValidateStepsLoading: isValidationLoading || isValidationFetching, hasIncompleteStepFields, hasAllEmptyStepFields, handleReplaceStep, handleRestoreSteps, - hasFunnelBeenExecuted, - setHasFunnelBeenExecuted, + hasUnsavedChanges, + setIsUpdatingFunnel, + isUpdatingFunnel, + lastUpdatedSteps, + setLastUpdatedSteps, }), [ funnelId, @@ -260,6 +274,8 @@ export function FunnelProvider({ addNewStep, handleStepRemoval, handleRunFunnel, + handleSaveFunnel, + triggerSave, validationResponse, isValidationLoading, isValidationFetching, @@ -267,8 +283,11 @@ export function FunnelProvider({ hasAllEmptyStepFields, handleReplaceStep, handleRestoreSteps, - hasFunnelBeenExecuted, - setHasFunnelBeenExecuted, + hasUnsavedChanges, + setIsUpdatingFunnel, + isUpdatingFunnel, + lastUpdatedSteps, + setLastUpdatedSteps, ], ); diff --git a/frontend/src/pages/TracesFunnels/components/CreateFunnel/CreateFunnel.tsx b/frontend/src/pages/TracesFunnels/components/CreateFunnel/CreateFunnel.tsx index d5c5c4a26101..b98053ae90d5 100644 --- a/frontend/src/pages/TracesFunnels/components/CreateFunnel/CreateFunnel.tsx +++ b/frontend/src/pages/TracesFunnels/components/CreateFunnel/CreateFunnel.tsx @@ -4,11 +4,9 @@ import { Input } from 'antd'; import logEvent from 'api/common/logEvent'; import { AxiosError } from 'axios'; import SignozModal from 'components/SignozModal/SignozModal'; -import { LOCALSTORAGE } from 'constants/localStorage'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import ROUTES from 'constants/routes'; import { useCreateFunnel } from 'hooks/TracesFunnels/useFunnels'; -import { useLocalStorage } from 'hooks/useLocalStorage'; import { useNotifications } from 'hooks/useNotifications'; import { useSafeNavigate } from 'hooks/useSafeNavigate'; import { Check, X } from 'lucide-react'; @@ -34,11 +32,6 @@ function CreateFunnel({ const { safeNavigate } = useSafeNavigate(); const { pathname } = useLocation(); - const [unexecutedFunnels, setUnexecutedFunnels] = useLocalStorage( - LOCALSTORAGE.UNEXECUTED_FUNNELS, - [], - ); - const handleCreate = (): void => { createFunnelMutation.mutate( { @@ -61,9 +54,6 @@ function CreateFunnel({ queryClient.invalidateQueries([REACT_QUERY_KEY.GET_FUNNELS_LIST]); const funnelId = data?.payload?.funnel_id; - if (funnelId) { - setUnexecutedFunnels([...unexecutedFunnels, funnelId]); - } onClose(funnelId); if (funnelId && redirectToDetails) { diff --git a/frontend/src/pages/TracesFunnels/components/DeleteFunnel/DeleteFunnel.tsx b/frontend/src/pages/TracesFunnels/components/DeleteFunnel/DeleteFunnel.tsx index e599220fa45f..37e250392e4f 100644 --- a/frontend/src/pages/TracesFunnels/components/DeleteFunnel/DeleteFunnel.tsx +++ b/frontend/src/pages/TracesFunnels/components/DeleteFunnel/DeleteFunnel.tsx @@ -2,13 +2,16 @@ import '../RenameFunnel/RenameFunnel.styles.scss'; import './DeleteFunnel.styles.scss'; import SignozModal from 'components/SignozModal/SignozModal'; +import { LOCALSTORAGE } from 'constants/localStorage'; import { REACT_QUERY_KEY } from 'constants/reactQueryKeys'; import ROUTES from 'constants/routes'; import { useDeleteFunnel } from 'hooks/TracesFunnels/useFunnels'; +import { useLocalStorage } from 'hooks/useLocalStorage'; import { useNotifications } from 'hooks/useNotifications'; import { Trash2, X } from 'lucide-react'; import { useQueryClient } from 'react-query'; import { useHistory } from 'react-router-dom'; +import { FunnelStepData } from 'types/api/traceFunnels'; interface DeleteFunnelProps { isOpen: boolean; @@ -29,6 +32,13 @@ function DeleteFunnel({ const history = useHistory(); const { pathname } = history.location; + + // localStorage hook for funnel steps + const localStorageKey = `${LOCALSTORAGE.FUNNEL_STEPS}_${funnelId}`; + const [, , clearLocalStorageSavedSteps] = useLocalStorage< + FunnelStepData[] | null + >(localStorageKey, null); + const handleDelete = (): void => { deleteFunnelMutation.mutate( { @@ -39,6 +49,7 @@ function DeleteFunnel({ notifications.success({ message: 'Funnel deleted successfully', }); + clearLocalStorageSavedSteps(); onClose(); if (