import './SaveView.styles.scss'; import { Color } from '@signozhq/design-tokens'; import { Button, ColorPicker, Input, Modal, Table, TableProps, Typography, } from 'antd'; import { getViewDetailsUsingViewKey, showErrorNotification, } from 'components/ExplorerCard/utils'; import { getRandomColor } from 'container/ExplorerOptions/utils'; import { useDeleteView } from 'hooks/saveViews/useDeleteView'; import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; import { useUpdateView } from 'hooks/saveViews/useUpdateView'; import useErrorNotification from 'hooks/useErrorNotification'; import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange'; import { useNotifications } from 'hooks/useNotifications'; import { CalendarClock, Check, Compass, PenLine, Search, Trash2, X, } from 'lucide-react'; import { ChangeEvent, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useLocation } from 'react-router-dom'; import { ICompositeMetricQuery } from 'types/api/alerts/compositeQuery'; import { ViewProps } from 'types/api/saveViews/types'; import { DataSource } from 'types/common/queryBuilder'; import { ROUTES_VS_SOURCEPAGE, SOURCEPAGE_VS_ROUTES } from './constants'; import { deleteViewHandler } from './utils'; function SaveView(): JSX.Element { const { pathname } = useLocation(); const sourcepage = ROUTES_VS_SOURCEPAGE[pathname]; const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const [activeViewKey, setActiveViewKey] = useState(''); const [newViewName, setNewViewName] = useState(''); const [color, setColor] = useState(Color.BG_SIENNA_500); const [isEditModalOpen, setIsEditModalOpen] = useState(false); const [activeViewName, setActiveViewName] = useState(''); const [ activeCompositeQuery, setActiveCompositeQuery, ] = useState(null); const [searchValue, setSearchValue] = useState(''); const [dataSource, setDataSource] = useState([]); const { t } = useTranslation(['explorer']); const hideDeleteViewModal = (): void => { setIsDeleteModalOpen(false); }; const handleDeleteModelOpen = (uuid: string, name: string): void => { setActiveViewKey(uuid); setActiveViewName(name); setIsDeleteModalOpen(true); }; const hideEditViewModal = (): void => { setIsEditModalOpen(false); }; const handleEditModelOpen = (view: ViewProps, color: string): void => { setActiveViewKey(view.uuid); setColor(color); setActiveViewName(view.name); setNewViewName(view.name); setActiveCompositeQuery(view.compositeQuery); setIsEditModalOpen(true); }; const { notifications } = useNotifications(); const { data: viewsData, isLoading, error, isRefetching, refetch: refetchAllView, } = useGetAllViews(sourcepage as DataSource); useEffect(() => { setDataSource(viewsData?.data.data || []); }, [viewsData?.data.data]); useErrorNotification(error); const handleSearch = (e: ChangeEvent): void => { setSearchValue(e.target.value); const filteredData = viewsData?.data.data.filter((view) => view.name.toLowerCase().includes(e.target.value.toLowerCase()), ); setDataSource(filteredData || []); }; const clearSearch = (): void => { setSearchValue(''); }; const { mutateAsync: deleteViewAsync, isLoading: isDeleteLoading, } = useDeleteView(activeViewKey); const onDeleteHandler = (): void => { deleteViewHandler({ deleteViewAsync, notifications, refetchAllView, viewId: activeViewKey, hideDeleteViewModal, clearSearch, }); }; const { mutateAsync: updateViewAsync, isLoading: isViewUpdating, } = useUpdateView({ compositeQuery: activeCompositeQuery || ({} as ICompositeMetricQuery), viewKey: activeViewKey, extraData: JSON.stringify({ color }), sourcePage: sourcepage || DataSource.LOGS, viewName: newViewName, }); const onUpdateQueryHandler = (): void => { updateViewAsync( { compositeQuery: activeCompositeQuery || ({} as ICompositeMetricQuery), viewKey: activeViewKey, extraData: JSON.stringify({ color }), sourcePage: sourcepage, viewName: activeViewName, }, { onSuccess: () => { notifications.success({ message: 'View Updated Successfully', }); hideEditViewModal(); refetchAllView(); }, onError: (err) => { showErrorNotification(notifications, err); }, }, ); }; const { handleExplorerTabChange } = useHandleExplorerTabChange(); const handleRedirectQuery = (view: ViewProps): void => { const currentViewDetails = getViewDetailsUsingViewKey( view.uuid, viewsData?.data.data, ); if (!currentViewDetails) return; const { query, name, uuid, panelType: currentPanelType } = currentViewDetails; if (sourcepage) { handleExplorerTabChange( currentPanelType, { query, name, uuid, }, SOURCEPAGE_VS_ROUTES[sourcepage], ); } }; const columns: TableProps['columns'] = [ { title: 'Save View', key: 'view', render: (view: ViewProps): JSX.Element => { const extraData = view.extraData !== '' ? JSON.parse(view.extraData) : ''; let bgColor = getRandomColor(); if (extraData !== '') { bgColor = extraData.color; } const timeOptions: Intl.DateTimeFormatOptions = { hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false, }; const formattedTime = new Date(view.createdAt).toLocaleTimeString( 'en-US', timeOptions, ); const dateOptions: Intl.DateTimeFormatOptions = { month: 'short', day: 'numeric', year: 'numeric', }; const formattedDate = new Date(view.createdAt).toLocaleDateString( 'en-US', dateOptions, ); // Combine time and date const formattedDateAndTime = `${formattedTime} ⎯ ${formattedDate}`; return (
{' '} {view.name}
handleEditModelOpen(view, bgColor)} /> handleRedirectQuery(view)} /> handleDeleteModelOpen(view.uuid, view.name)} />
{view.createdBy.substring(0, 1).toUpperCase()}
{view.createdBy}
{formattedDateAndTime}
); }, }, ]; return (
Views Manage your saved views for logs. } value={searchValue} onChange={handleSearch} /> Delete view} open={isDeleteModalOpen} closable={false} onCancel={hideDeleteViewModal} footer={[ , , ]} > {t('delete_confirm_message', { viewName: activeViewName, })} Edit view details} open={isEditModalOpen} closable={false} onCancel={hideEditViewModal} footer={[ , ]} > Label
setColor(hex)} /> setNewViewName(e.target.value)} />
); } export default SaveView;