Shaheer Kochai 2c87d96d75
feat: trace funnels list page (#7324)
* chore: add a new tab for traces funnels

* feat: funnels list page basic UI

* feat: learn more component

* feat: get funnels list data from mock API, and handle data, loading and empty states

* chore(SignozModal): add width prop and improve button styles

* 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
2025-03-22 09:13:18 +00:00

104 lines
2.7 KiB
TypeScript

import '../RenameFunnel/RenameFunnel.styles.scss';
import { Input } from 'antd';
import SignozModal from 'components/SignozModal/SignozModal';
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
import ROUTES from 'constants/routes';
import { useCreateFunnel } from 'hooks/TracesFunnels/useFunnels';
import { useNotifications } from 'hooks/useNotifications';
import { useSafeNavigate } from 'hooks/useSafeNavigate';
import { Check, X } from 'lucide-react';
import { useState } from 'react';
import { useQueryClient } from 'react-query';
import { generatePath } from 'react-router-dom';
interface CreateFunnelProps {
isOpen: boolean;
onClose: () => void;
}
function CreateFunnel({ isOpen, onClose }: CreateFunnelProps): JSX.Element {
const [funnelName, setFunnelName] = useState<string>('');
const createFunnelMutation = useCreateFunnel();
const { notifications } = useNotifications();
const queryClient = useQueryClient();
const { safeNavigate } = useSafeNavigate();
const handleCreate = (): void => {
createFunnelMutation.mutate(
{
funnel_name: funnelName,
creation_timestamp: new Date().getTime(),
},
{
onSuccess: (data) => {
notifications.success({
message: 'Funnel created successfully',
});
setFunnelName('');
queryClient.invalidateQueries([REACT_QUERY_KEY.GET_FUNNELS_LIST]);
onClose();
if (data?.payload?.funnel_id) {
safeNavigate(
generatePath(ROUTES.TRACES_FUNNELS_DETAIL, {
funnelId: data.payload.funnel_id,
}),
);
}
},
onError: () => {
notifications.error({
message: 'Failed to create funnel',
});
},
},
);
};
const handleCancel = (): void => {
setFunnelName('');
onClose();
};
return (
<SignozModal
open={isOpen}
title="Create new funnel"
width={384}
onCancel={handleCancel}
rootClassName="funnel-modal"
cancelText="Cancel"
okText="Create Funnel"
okButtonProps={{
icon: <Check size={14} />,
loading: createFunnelMutation.isLoading,
type: 'primary',
className: 'funnel-modal__ok-btn',
onClick: handleCreate,
disabled: !funnelName.trim(),
}}
cancelButtonProps={{
icon: <X size={14} />,
type: 'text',
className: 'funnel-modal__cancel-btn',
onClick: handleCancel,
}}
getContainer={document.getElementById('root') || undefined}
destroyOnClose
>
<div className="funnel-modal-content">
<span className="funnel-modal-content__label">Enter funnel name</span>
<Input
className="funnel-modal-content__input"
value={funnelName}
onChange={(e): void => setFunnelName(e.target.value)}
placeholder="Eg. checkout dropoff funnel"
autoFocus
/>
</div>
</SignozModal>
);
}
export default CreateFunnel;