feat: add support for single step funnels while creating from span details (#8492)

* feat: add support for single step funnels while creating from span details

* fix: fix the UI for loading state
This commit is contained in:
Shaheer Kochai 2025-07-22 17:43:09 +04:30 committed by GitHub
parent 24d6d83575
commit b91407416b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 109 additions and 65 deletions

View File

@ -1,5 +1,12 @@
// Modal base styles
.add-span-to-funnel-modal-container {
.add-span-to-funnel-modal {
&__loading-spinner {
display: flex;
align-items: center;
justify-content: center;
height: 400px;
}
&-container {
.ant-modal {
&-content,
&-header {
@ -63,6 +70,7 @@
}
}
}
}
}
// Main modal styles
@ -89,7 +97,7 @@
}
.steps-content {
height: 500px;
max-height: 500px;
}
}
}

View File

@ -99,6 +99,7 @@ function AddSpanToFunnelModal({
const [triggerSave, setTriggerSave] = useState<boolean>(false);
const [isUnsavedChanges, setIsUnsavedChanges] = useState<boolean>(false);
const [triggerDiscard, setTriggerDiscard] = useState<boolean>(false);
const [isCreatedFromSpan, setIsCreatedFromSpan] = useState<boolean>(false);
const handleSearch = (e: ChangeEvent<HTMLInputElement>): void => {
setSearchQuery(e.target.value);
@ -126,6 +127,7 @@ function AddSpanToFunnelModal({
const handleFunnelClick = (funnel: FunnelData): void => {
setSelectedFunnelId(funnel.funnel_id);
setActiveView(ModalView.DETAILS);
setIsCreatedFromSpan(false);
};
const handleBack = (): void => {
@ -133,6 +135,7 @@ function AddSpanToFunnelModal({
setSelectedFunnelId(undefined);
setIsUnsavedChanges(false);
setTriggerSave(false);
setIsCreatedFromSpan(false);
};
const handleCreateNewClick = (): void => {
@ -188,6 +191,7 @@ function AddSpanToFunnelModal({
if (funnelId) {
setSelectedFunnelId(funnelId);
setActiveView(ModalView.DETAILS);
setIsCreatedFromSpan(true);
}
setIsCreateModalOpen(false);
}}
@ -206,15 +210,18 @@ function AddSpanToFunnelModal({
<ArrowLeft size={14} />
All funnels
</Button>
<div className="traces-funnel-details">
<div className="traces-funnel-details__steps-config">
<Spin
style={{ height: 400 }}
className="add-span-to-funnel-modal__loading-spinner"
spinning={isFunnelDetailsLoading || isFunnelDetailsFetching}
indicator={<LoadingOutlined spin />}
>
<div className="traces-funnel-details">
<div className="traces-funnel-details__steps-config">
{selectedFunnelId && funnelDetails?.payload && (
<FunnelProvider funnelId={selectedFunnelId}>
<FunnelProvider
funnelId={selectedFunnelId}
hasSingleStep={isCreatedFromSpan}
>
<FunnelDetailsView
funnel={funnelDetails.payload}
span={span}
@ -225,10 +232,10 @@ function AddSpanToFunnelModal({
/>
</FunnelProvider>
)}
</div>
</div>
</Spin>
</div>
</div>
</div>
);
return (

View File

@ -1,7 +1,7 @@
import { FunnelStepData, LatencyOptions } from 'types/api/traceFunnels';
import { v4 } from 'uuid';
export const initialStepsData: FunnelStepData[] = [
export const createInitialStepsData = (): FunnelStepData[] => [
{
id: v4(),
step_order: 1,
@ -12,7 +12,6 @@ export const initialStepsData: FunnelStepData[] = [
op: 'and',
},
latency_pointer: 'start',
latency_type: undefined,
has_errors: false,
},
{
@ -30,6 +29,21 @@ export const initialStepsData: FunnelStepData[] = [
},
];
export const createSingleStepData = (): FunnelStepData[] => [
{
id: v4(),
step_order: 1,
service_name: '',
span_name: '',
filters: {
items: [],
op: 'and',
},
latency_pointer: 'start',
has_errors: false,
},
];
export const LatencyPointers: {
value: FunnelStepData['latency_pointer'];
key: string;

View File

@ -10,7 +10,10 @@ import { normalizeSteps } from 'hooks/TracesFunnels/useFunnelConfiguration';
import { useValidateFunnelSteps } from 'hooks/TracesFunnels/useFunnels';
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
import { isEqual } from 'lodash-es';
import { initialStepsData } from 'pages/TracesFunnelDetails/constants';
import {
createInitialStepsData,
createSingleStepData,
} from 'pages/TracesFunnelDetails/constants';
import {
createContext,
Dispatch,
@ -68,9 +71,11 @@ const FunnelContext = createContext<FunnelContextType | undefined>(undefined);
export function FunnelProvider({
children,
funnelId,
hasSingleStep = false,
}: {
children: React.ReactNode;
funnelId: string;
hasSingleStep?: boolean;
}): JSX.Element {
const { selectedTime } = useSelector<AppState, GlobalReducer>(
(state) => state.globalTime,
@ -89,7 +94,13 @@ export function FunnelProvider({
funnelId,
]);
const funnel = data?.payload;
const initialSteps = funnel?.steps?.length ? funnel.steps : initialStepsData;
const defaultSteps = useMemo(
() => (hasSingleStep ? createSingleStepData() : createInitialStepsData()),
[hasSingleStep],
);
const initialSteps = funnel?.steps?.length ? funnel.steps : defaultSteps;
const [steps, setSteps] = useState<FunnelStepData[]>(initialSteps);
const [triggerSave, setTriggerSave] = useState<boolean>(false);
const [isUpdatingFunnel, setIsUpdatingFunnel] = useState<boolean>(false);
@ -155,7 +166,7 @@ export function FunnelProvider({
setSteps((prev) => [
...prev,
{
...initialStepsData[0],
...createInitialStepsData()[0],
id: v4(),
step_order: prev.length + 1,
},
@ -296,6 +307,10 @@ export function FunnelProvider({
);
}
FunnelProvider.defaultProps = {
hasSingleStep: false,
};
export function useFunnelContext(): FunnelContextType {
const context = useContext(FunnelContext);
if (context === undefined) {