Yunus M ab4f6adb19
Logs explorer design update (#4352)
* feat: logs explorer - new design

* feat: update styles

* feat: added new toolbar for logs explorer (#4336)

* feat: logs list view changes (#4348)

* feat: logs list view changes

* fix: list view and toolbar styles

* feat: side btns

* feat: added auto refresh handler

* feat: handle popover close for btn click date time

* feat: extract the common log actions btn component

* feat: update the button for log line actions

* fix: event propagation from context button

* feat: use styles from ui-library

* Query builder design update (#4359)

* feat: QB design update

* fix: add functionality and light mode styles

* fix: ts issues

* fix: update all css color variables to correct names

* fix: lint errors

* feat: new table view for logs explorer list section  (#4353)

* feat: table view changes for logs list

* feat: code refactor to support log line actions

* feat: code refactor to support log line actions

* fix: the positioning of the btns

* feat: fix the table onclick

* fix: header issue

* fix: on hover

* fix: type issue

* fix: eslint error

* fix: type errors (#4360)

* feat: handle light theme for logs explorer design changes (#4363)

* feat: handle light theme for list tables and dateTime selection

* feat: handle light theme for popover

* fix: address review comments

* feat: date time custom time modal to render inside the new popover (#4366)

* feat: single calender for range picker

* fix: edgecases

* feat: integrate date time selector across app

* fix: remove dangling border after element removal

* feat: handle qb design changes across the application

* feat: handle light theme

* feat: handle light theme

* fix: virtuoso scroll refresh issue

* feat: handle new typing changes for date time picker v2 (#4386)

Co-authored-by: Yunus M <myounis.ar@live.com>

* chore: styles improvement across new design (#4389)

* fix: improve date time styles

* feat: table view changes according to new design

* fix: button visibility in clickhouse and promQL headers (#4390)

* feat: change the tabs to new design buttons for query builder

* Settings theme change (#4368)

* feat: settings theme change

* [Refactor]: New design for Log details page (#4362)

New design for Log details page 

Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
Co-authored-by: Yunus M <myounis.ar@live.com>

* feat: save view for new design (#4392)

* feat: save view for new design

* refactor: done with save view

* feat: update styles for logs detail view (#4407)

* feat: update styles for logs detail view

* feat: update styles for logs detail view

* feat: add raw view attributes in the logs list view (#4422)

* feat: add raw view attributes in the logs list view

* feat: add raw view attributes in the logs list view

* fix: raw attributes

* fix: logs UI improvements (#4426)

* fix: remove fixed times from the date time picker v2

* fix: added old logs explorer CTA in new designs

* feat: handle active logs indicator update

* fix: address review comments

* fix: old logs explorer page

* fix: remove info text and add relative time buttons

* fix: update logs explorer tab designs

* fix: update logs explorer tab designs

* fix: update logs explorer tab designs

* refactor: New design for Save views. (#4435)

* feat: [GH-4436]: date range enhancements (#4448)

* feat: [GH-4436]: when selecting custom time range it should be from start of day to end of date

* fix: custom time width and refresh text visibility issues (#4428)

---------

Co-authored-by: Yunus M <myounis.ar@live.com>

* feat: update ui (#4449)

* feat: added loading and error states for logs design (#4452)

* feat: added loading and error states for logs design

* feat: added error states for table view and time series view

* feat: handle error and loading states

* feat: loading states

* [Refactor]: Tab Switch deplay issue and UI improvement for Clickhouse (#4409)

* fix: switching between logs display tabs (#4457)

* [Feat]: View in Traces (#4450)

* refactor: datetime selector beside run query removed add to dashboard

* refactor: added tab for traces view details page

* refactor: done with the save view in traces

* fix: the gittery effect when navigatigating from views

* refactor: view tab view title light mode support

* refactor: removed console

* fix: gittery effect when switch view from views tabs

* refactor: separate traces routes

* refactor: remove query params

* chore: fix tsc issues

* fix: jest config issues

* fix: update TODO and remove extra braces

* feat: handle loading states and incorporate ui feedback (#4479)

* UI feedback updates (#4482)

* feat: handle loading and fix ui issues

* feat: ui updates

* fix: logs explorer issues (#4483)

* fix: logs explorer issues

* fix: jest test cases

* feat: support custom times unique to pages new design changes (#4485)

* fix: loading states for list log view (#4486)

* fix: logs search view query fix, logs details view - attribute tags alignment fix (#4489)

* fix: delete empty file

* fix: chart loading when scrolling logs (#4495)

* fix: chart should not load when scrolling the logs as it is already fetched

* fix: make the search bar as default rather than advanced options

* fix: rename show context to show in context

* fix: query range api not triggering on default select first load (#4498)

* Refactor: Log Explorer UI changes.  (#4502)

* refactor: used selected view enum

* refactor: updated hight of switch old button and tab border

* refactor: import fixes

* refactor: query builder border and button groups

* refactor: removed hypen from refreshed

* refactor: show delete button only when there is more than one query

* refactor: sqaure up the query build button groups

* refactor: updated css

* fix: additional filter color button shadow

* refactor: removed commented code and used selected panel enum

* refactor: updated typecheck script

* refactor: used enum selected view (#4504)

* fix: retain the current query on date time change (#4510)

* feat: added new icon for promQL and added tooltips for dashboards and alerts (#4512)

* feat: added new icon for promQL and added tooltips for dashboards and alerts

* fix: styles at 1440 px zoom

* fix: rename clickhouse to clickHouse

---------

Co-authored-by: Vikrant Gupta <54737045+Vikrant2520@users.noreply.github.com>
Co-authored-by: Vikrant Gupta <vikrant.thomso@gmail.com>
Co-authored-by: Rajat Dabade <rajat@signoz.io>
2024-02-12 00:23:19 +05:30

358 lines
9.1 KiB
TypeScript

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<string>('');
const [newViewName, setNewViewName] = useState<string>('');
const [color, setColor] = useState(Color.BG_SIENNA_500);
const [isEditModalOpen, setIsEditModalOpen] = useState(false);
const [activeViewName, setActiveViewName] = useState<string>('');
const [
activeCompositeQuery,
setActiveCompositeQuery,
] = useState<ICompositeMetricQuery | null>(null);
const [searchValue, setSearchValue] = useState<string>('');
const [dataSource, setDataSource] = useState<ViewProps[]>([]);
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<HTMLInputElement>): 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<ViewProps>['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 (
<div className="column-render">
<div className="title-with-action">
<div className="save-view-title">
<span
className="dot"
style={{
background: bgColor,
boxShadow: `0px 0px 6px 0px ${bgColor}`,
}}
/>{' '}
<Typography.Text>{view.name}</Typography.Text>
</div>
<div className="action-btn">
<PenLine
size={14}
onClick={(): void => handleEditModelOpen(view, bgColor)}
/>
<Compass size={14} onClick={(): void => handleRedirectQuery(view)} />
<Trash2
size={14}
color={Color.BG_CHERRY_500}
onClick={(): void => handleDeleteModelOpen(view.uuid, view.name)}
/>
</div>
</div>
<div className="view-details">
<div className="view-tag">
<Typography.Text className="tag-text">
{view.createdBy.substring(0, 1).toUpperCase()}
</Typography.Text>
</div>
<Typography.Text className="view-created-by">
{view.createdBy}
</Typography.Text>
<div className="view-created-at">
<CalendarClock size={14} />
<Typography.Text>{formattedDateAndTime}</Typography.Text>
</div>
</div>
</div>
);
},
},
];
return (
<div className="save-view-container">
<div className="save-view-content">
<Typography.Title className="title">Views</Typography.Title>
<Typography.Text className="subtitle">
Manage your saved views for logs.
</Typography.Text>
<Input
placeholder="Search for views..."
prefix={<Search size={12} color={Color.BG_VANILLA_400} />}
value={searchValue}
onChange={handleSearch}
/>
<Table
columns={columns}
dataSource={dataSource}
loading={isLoading || isRefetching}
showHeader={false}
pagination={{ pageSize: 5 }}
/>
</div>
<Modal
className="delete-view-modal"
title={<span className="title">Delete view</span>}
open={isDeleteModalOpen}
closable={false}
onCancel={hideDeleteViewModal}
footer={[
<Button
key="cancel"
onClick={hideDeleteViewModal}
className="cancel-btn"
icon={<X size={16} />}
>
Cancel
</Button>,
<Button
key="submit"
icon={<Trash2 size={16} />}
onClick={onDeleteHandler}
className="delete-btn"
disabled={isDeleteLoading}
>
Delete view
</Button>,
]}
>
<Typography.Text className="delete-text">
{t('delete_confirm_message', {
viewName: activeViewName,
})}
</Typography.Text>
</Modal>
<Modal
className="save-view-modal"
title={<span className="title">Edit view details</span>}
open={isEditModalOpen}
closable={false}
onCancel={hideEditViewModal}
footer={[
<Button
key="submit"
icon={<Check size={16} color={Color.BG_VANILLA_100} />}
onClick={onUpdateQueryHandler}
disabled={isViewUpdating}
>
Save changes
</Button>,
]}
>
<Typography.Text>Label</Typography.Text>
<div className="save-view-input">
<ColorPicker
value={color}
onChange={(value, hex): void => setColor(hex)}
/>
<Input
placeholder="e.g. Crash landing view"
value={newViewName}
onChange={(e): void => setNewViewName(e.target.value)}
/>
</div>
</Modal>
</div>
);
}
export default SaveView;