Explorer Toolbar maximised and minimised (#4721)

This commit is contained in:
Rajat Dabade 2024-03-26 17:09:13 +05:30 committed by GitHub
parent 83f68f13db
commit 4c91dbcff0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 376 additions and 98 deletions

View File

@ -16,4 +16,5 @@ export enum LOCALSTORAGE {
CHAT_SUPPORT = 'CHAT_SUPPORT',
IS_IDENTIFIED_USER = 'IS_IDENTIFIED_USER',
DASHBOARD_VARIABLES = 'DASHBOARD_VARIABLES',
SHOW_EXPLORER_TOOLBAR = 'SHOW_EXPLORER_TOOLBAR',
}

View File

@ -0,0 +1,39 @@
import { useEffect, useState } from 'react';
import ExplorerOptions, { ExplorerOptionsProps } from './ExplorerOptions';
import { getExplorerToolBarVisibility } from './utils';
type ExplorerOptionsWrapperProps = Omit<
ExplorerOptionsProps,
'isExplorerOptionDrop'
>;
function ExplorerOptionWrapper({
disabled,
query,
isLoading,
onExport,
sourcepage,
}: ExplorerOptionsWrapperProps): JSX.Element {
const [isExplorerOptionHidden, setIsExplorerOptionHidden] = useState(false);
useEffect(() => {
const toolbarVisibility = getExplorerToolBarVisibility(sourcepage);
setIsExplorerOptionHidden(!toolbarVisibility);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
return (
<ExplorerOptions
disabled={disabled}
query={query}
isLoading={isLoading}
onExport={onExport}
sourcepage={sourcepage}
isExplorerOptionHidden={isExplorerOptionHidden}
setIsExplorerOptionHidden={setIsExplorerOptionHidden}
/>
);
}
export default ExplorerOptionWrapper;

View File

@ -3,8 +3,8 @@
}
.explorer-update {
position: fixed;
bottom: 16px;
left: calc(50% - 225px);
bottom: 24px;
left: calc(50% - 250px);
display: flex;
align-items: center;
gap: 12px;
@ -37,21 +37,24 @@
}
}
.explorer-options {
display: flex;
gap: 16px;
position: fixed;
bottom: 24px;
left: calc(50% + 240px);
padding: 10px 12px;
border-radius: 50px;
transform: translate(calc(-50% - 120px), 0);
transition: left 0.2s linear;
border: 1px solid var(--bg-slate-400);
border-radius: 50px;
background: rgba(22, 24, 29, 0.6);
box-shadow: 4px 4px 16px 4px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(20px);
position: fixed;
bottom: 16px;
left: calc(50% + 240px);
transform: translate(calc(-50% - 120px), 0);
transition: left 0.2s linear;
cursor: default;
display: flex;
gap: 16px;
z-index: 1;
.ant-select-selector {
padding: 0 !important;
}
@ -236,9 +239,9 @@
.lightMode {
.explorer-options {
background: transparent;
box-shadow: none;
border: 1px solid var(--bg-vanilla-300);
background: rgba(255, 255, 255, 0.8);
box-shadow: 4px 4px 16px 4px rgba(255, 255, 255, 0.55);
backdrop-filter: blur(20px);
hr {

View File

@ -1,3 +1,4 @@
/* eslint-disable react/jsx-props-no-spreading */
import './ExplorerOptions.styles.scss';
import { Color } from '@signozhq/design-tokens';
@ -30,8 +31,24 @@ import useErrorNotification from 'hooks/useErrorNotification';
import { useHandleExplorerTabChange } from 'hooks/useHandleExplorerTabChange';
import { useNotifications } from 'hooks/useNotifications';
import { mapCompositeQueryFromQuery } from 'lib/newQueryBuilder/queryBuilderMappers/mapCompositeQueryFromQuery';
import { Check, ConciergeBell, Disc3, Plus, X, XCircle } from 'lucide-react';
import { CSSProperties, useCallback, useMemo, useRef, useState } from 'react';
import {
Check,
ConciergeBell,
Disc3,
PanelBottomClose,
Plus,
X,
XCircle,
} from 'lucide-react';
import {
CSSProperties,
Dispatch,
SetStateAction,
useCallback,
useMemo,
useRef,
useState,
} from 'react';
import { useSelector } from 'react-redux';
import { useHistory } from 'react-router-dom';
import { AppState } from 'store/reducers';
@ -41,11 +58,13 @@ import { DataSource } from 'types/common/queryBuilder';
import AppReducer from 'types/reducer/app';
import { USER_ROLES } from 'types/roles';
import ExplorerOptionsHideArea from './ExplorerOptionsHideArea';
import {
DATASOURCE_VS_ROUTES,
generateRGBAFromHex,
getRandomColor,
saveNewViewHandler,
setExplorerToolBarVisibility,
} from './utils';
const allowedRoles = [USER_ROLES.ADMIN, USER_ROLES.AUTHOR, USER_ROLES.EDITOR];
@ -57,6 +76,8 @@ function ExplorerOptions({
onExport,
query,
sourcepage,
isExplorerOptionHidden = false,
setIsExplorerOptionHidden,
}: ExplorerOptionsProps): JSX.Element {
const [isExport, setIsExport] = useState<boolean>(false);
const [isSaveModalOpen, setIsSaveModalOpen] = useState(false);
@ -257,11 +278,18 @@ function ExplorerOptions({
[isDarkMode],
);
const hideToolbar = (): void => {
setExplorerToolBarVisibility(false, sourcepage);
if (setIsExplorerOptionHidden) {
setIsExplorerOptionHidden(true);
}
};
const isEditDeleteSupported = allowedRoles.includes(role as string);
return (
<>
{isQueryUpdated && (
{isQueryUpdated && !isExplorerOptionHidden && (
<div
className={cx(
isEditDeleteSupported ? '' : 'hide-update',
@ -289,87 +317,103 @@ function ExplorerOptions({
</Tooltip>
</div>
)}
<div
className="explorer-options"
style={{
background: extraData
? `linear-gradient(90deg, rgba(0,0,0,0) -5%, ${rgbaColor} 9%, rgba(0,0,0,0) 30%)`
: 'transparent',
backdropFilter: 'blur(20px)',
}}
>
<div className="view-options">
<Select<string, { key: string; value: string }>
showSearch
placeholder="Select a view"
loading={viewsIsLoading || isRefetching}
value={viewName || undefined}
onSelect={handleSelect}
style={{
minWidth: 170,
}}
dropdownStyle={dropdownStyle}
className="views-dropdown"
allowClear={{
clearIcon: <XCircle size={16} style={{ marginTop: '-3px' }} />,
}}
onClear={handleClearSelect}
ref={ref}
>
{viewsData?.data?.data?.map((view) => {
const extraData =
view.extraData !== '' ? JSON.parse(view.extraData) : '';
let bgColor = getRandomColor();
if (extraData !== '') {
bgColor = extraData.color;
}
return (
<Select.Option key={view.uuid} value={view.name}>
<div className="render-options">
<span
className="dot"
style={{
background: bgColor,
boxShadow: `0px 0px 6px 0px ${bgColor}`,
}}
/>{' '}
{view.name}
</div>
</Select.Option>
);
})}
</Select>
<Button
shape="round"
onClick={handleSaveViewModalToggle}
className={isEditDeleteSupported ? '' : 'hidden'}
disabled={viewsIsLoading || isRefetching}
>
<Disc3 size={16} /> Save this view
</Button>
</div>
<hr className={isEditDeleteSupported ? '' : 'hidden'} />
<div className={cx('actions', isEditDeleteSupported ? '' : 'hidden')}>
<Tooltip title="Create Alerts">
<Button
disabled={disabled}
shape="circle"
onClick={onCreateAlertsHandler}
{!isExplorerOptionHidden && (
<div
className="explorer-options"
style={{
background: extraData
? `linear-gradient(90deg, rgba(0,0,0,0) -5%, ${rgbaColor} 9%, rgba(0,0,0,0) 30%)`
: 'transparent',
}}
>
<div className="view-options">
<Select<string, { key: string; value: string }>
showSearch
placeholder="Select a view"
loading={viewsIsLoading || isRefetching}
value={viewName || undefined}
onSelect={handleSelect}
style={{
minWidth: 170,
}}
dropdownStyle={dropdownStyle}
className="views-dropdown"
allowClear={{
clearIcon: <XCircle size={16} style={{ marginTop: '-3px' }} />,
}}
onClear={handleClearSelect}
ref={ref}
>
<ConciergeBell size={16} />
</Button>
</Tooltip>
{viewsData?.data?.data?.map((view) => {
const extraData =
view.extraData !== '' ? JSON.parse(view.extraData) : '';
let bgColor = getRandomColor();
if (extraData !== '') {
bgColor = extraData.color;
}
return (
<Select.Option key={view.uuid} value={view.name}>
<div className="render-options">
<span
className="dot"
style={{
background: bgColor,
boxShadow: `0px 0px 6px 0px ${bgColor}`,
}}
/>{' '}
{view.name}
</div>
</Select.Option>
);
})}
</Select>
<Tooltip title="Add to Dashboard">
<Button disabled={disabled} shape="circle" onClick={onAddToDashboard}>
<Plus size={16} />
<Button
shape="round"
onClick={handleSaveViewModalToggle}
className={isEditDeleteSupported ? '' : 'hidden'}
disabled={viewsIsLoading || isRefetching}
>
<Disc3 size={16} /> Save this view
</Button>
</Tooltip>
</div>
<hr className={isEditDeleteSupported ? '' : 'hidden'} />
<div className={cx('actions', isEditDeleteSupported ? '' : 'hidden')}>
<Tooltip title="Create Alerts">
<Button
disabled={disabled}
shape="circle"
onClick={onCreateAlertsHandler}
>
<ConciergeBell size={16} />
</Button>
</Tooltip>
<Tooltip title="Add to Dashboard">
<Button disabled={disabled} shape="circle" onClick={onAddToDashboard}>
<Plus size={16} />
</Button>
</Tooltip>
<Tooltip title="Hide">
<Button disabled={disabled} shape="circle" onClick={hideToolbar}>
<PanelBottomClose size={16} />
</Button>
</Tooltip>
</div>
</div>
</div>
)}
<ExplorerOptionsHideArea
isExplorerOptionHidden={isExplorerOptionHidden}
setIsExplorerOptionHidden={setIsExplorerOptionHidden}
sourcepage={sourcepage}
isQueryUpdated={isQueryUpdated}
handleClearSelect={handleClearSelect}
onUpdateQueryHandler={onUpdateQueryHandler}
/>
<Modal
className="save-view-modal"
@ -427,8 +471,14 @@ export interface ExplorerOptionsProps {
query: Query | null;
disabled: boolean;
sourcepage: DataSource;
isExplorerOptionHidden?: boolean;
setIsExplorerOptionHidden?: Dispatch<SetStateAction<boolean>>;
}
ExplorerOptions.defaultProps = { isLoading: false };
ExplorerOptions.defaultProps = {
isLoading: false,
isExplorerOptionHidden: false,
setIsExplorerOptionHidden: undefined,
};
export default ExplorerOptions;

View File

@ -0,0 +1,55 @@
.explorer-option-droppable-container {
position: fixed;
bottom: 0;
width: -webkit-fill-available;
height: 24px;
display: flex;
justify-content: center;
border-radius: 10px 10px 0px 0px;
// box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
// backdrop-filter: blur(20px);
.explorer-actions-btn {
display: flex;
gap: 8px;
margin-right: 8px;
.action-btn {
display: flex;
justify-content: center;
align-items: center;
border-radius: 10px 10px 0px 0px;
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(20px);
height: 24px !important;
border: none;
}
}
.explorer-show-btn {
border-radius: 10px 10px 0px 0px;
border: 1px solid var(--bg-slate-400);
background: rgba(22, 24, 29, 0.40);
box-shadow: 0px 4px 16px 0px rgba(0, 0, 0, 0.25);
backdrop-filter: blur(20px);
align-self: center;
padding: 8px 12px;
height: 24px !important;
.menu-bar {
border-radius: 50px;
background: var(--bg-slate-200);
height: 4px;
width: 50px;
}
}
}
.lightMode {
.explorer-option-droppable-container {
.explorer-show-btn {
background: var(--bg-vanilla-200);
}
}
}

View File

@ -0,0 +1,78 @@
/* eslint-disable no-nested-ternary */
import './ExplorerOptionsHideArea.styles.scss';
import { Color } from '@signozhq/design-tokens';
import { Button, Tooltip } from 'antd';
import { Disc3, X } from 'lucide-react';
import { Dispatch, SetStateAction } from 'react';
import { DataSource } from 'types/common/queryBuilder';
import { setExplorerToolBarVisibility } from './utils';
interface DroppableAreaProps {
isQueryUpdated: boolean;
isExplorerOptionHidden?: boolean;
sourcepage: DataSource;
setIsExplorerOptionHidden?: Dispatch<SetStateAction<boolean>>;
handleClearSelect: () => void;
onUpdateQueryHandler: () => void;
}
function ExplorerOptionsHideArea({
isQueryUpdated,
isExplorerOptionHidden,
sourcepage,
setIsExplorerOptionHidden,
handleClearSelect,
onUpdateQueryHandler,
}: DroppableAreaProps): JSX.Element {
const handleShowExplorerOption = (): void => {
if (setIsExplorerOptionHidden) {
setIsExplorerOptionHidden(false);
setExplorerToolBarVisibility(true, sourcepage);
}
};
return (
<div className="explorer-option-droppable-container">
{isExplorerOptionHidden && (
<>
{isQueryUpdated && (
<div className="explorer-actions-btn">
<Tooltip title="Clear this view">
<Button
onClick={handleClearSelect}
className="action-btn"
style={{ background: Color.BG_CHERRY_500 }}
icon={<X size={14} color={Color.BG_INK_500} />}
/>
</Tooltip>
<Tooltip title="Update this View">
<Button
onClick={onUpdateQueryHandler}
className="action-btn"
style={{ background: Color.BG_ROBIN_500 }}
icon={<Disc3 size={14} color={Color.BG_INK_500} />}
/>
</Tooltip>
</div>
)}
<Button
// style={{ alignSelf: 'center', marginRight: 'calc(10% - 20px)' }}
className="explorer-show-btn"
onClick={handleShowExplorerOption}
>
<div className="menu-bar" />
</Button>
</>
)}
</div>
);
}
ExplorerOptionsHideArea.defaultProps = {
isExplorerOptionHidden: undefined,
setIsExplorerOptionHidden: undefined,
};
export default ExplorerOptionsHideArea;

View File

@ -1,5 +1,6 @@
import { Color } from '@signozhq/design-tokens';
import { showErrorNotification } from 'components/ExplorerCard/utils';
import { LOCALSTORAGE } from 'constants/localStorage';
import { QueryParams } from 'constants/query';
import ROUTES from 'constants/routes';
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
@ -67,3 +68,54 @@ export const generateRGBAFromHex = (hex: string, opacity: number): string =>
hex.slice(3, 5),
16,
)}, ${parseInt(hex.slice(5, 7), 16)}, ${opacity})`;
export const getExplorerToolBarVisibility = (dataSource: string): boolean => {
try {
const showExplorerToolbar = localStorage.getItem(
LOCALSTORAGE.SHOW_EXPLORER_TOOLBAR,
);
if (showExplorerToolbar === null) {
const parsedShowExplorerToolbar: {
[DataSource.LOGS]: boolean;
[DataSource.TRACES]: boolean;
[DataSource.METRICS]: boolean;
} = {
[DataSource.METRICS]: true,
[DataSource.TRACES]: true,
[DataSource.LOGS]: true,
};
localStorage.setItem(
LOCALSTORAGE.SHOW_EXPLORER_TOOLBAR,
JSON.stringify(parsedShowExplorerToolbar),
);
return true;
}
const parsedShowExplorerToolbar = JSON.parse(showExplorerToolbar || '{}');
return parsedShowExplorerToolbar[dataSource];
} catch (error) {
console.error(error);
return false;
}
};
export const setExplorerToolBarVisibility = (
value: boolean,
dataSource: string,
): void => {
try {
const showExplorerToolbar = localStorage.getItem(
LOCALSTORAGE.SHOW_EXPLORER_TOOLBAR,
);
if (showExplorerToolbar) {
const parsedShowExplorerToolbar = JSON.parse(showExplorerToolbar);
parsedShowExplorerToolbar[dataSource] = value;
localStorage.setItem(
LOCALSTORAGE.SHOW_EXPLORER_TOOLBAR,
JSON.stringify(parsedShowExplorerToolbar),
);
return;
}
} catch (error) {
console.error(error);
}
};

View File

@ -15,7 +15,7 @@ import {
} from 'constants/queryBuilder';
import { DEFAULT_PER_PAGE_VALUE } from 'container/Controls/config';
import Download from 'container/DownloadV2/DownloadV2';
import ExplorerOptions from 'container/ExplorerOptions/ExplorerOptions';
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import GoToTop from 'container/GoToTop';
import LogsExplorerChart from 'container/LogsExplorerChart';
import LogsExplorerList from 'container/LogsExplorerList';
@ -659,7 +659,7 @@ function LogsExplorerViews({
<GoToTop />
<ExplorerOptions
<ExplorerOptionWrapper
disabled={!stagedQuery}
query={exportDefaultQuery}
isLoading={isUpdateDashboardLoading}

View File

@ -5,7 +5,7 @@ import axios from 'axios';
import ExplorerCard from 'components/ExplorerCard/ExplorerCard';
import { AVAILABLE_EXPORT_PANEL_TYPES } from 'constants/panelTypes';
import { initialQueriesMap, PANEL_TYPES } from 'constants/queryBuilder';
import ExplorerOptions from 'container/ExplorerOptions/ExplorerOptions';
import ExplorerOptionWrapper from 'container/ExplorerOptions/ExplorerOptionWrapper';
import ExportPanel from 'container/ExportPanel';
import RightToolbarActions from 'container/QueryBuilder/components/ToolbarActions/RightToolbarActions';
import DateTimeSelector from 'container/TopNav/DateTimeSelectionV2';
@ -208,12 +208,12 @@ function TracesExplorer(): JSX.Element {
onChange={handleExplorerTabChange}
/>
</Container>
<ExplorerOptions
<ExplorerOptionWrapper
disabled={!stagedQuery}
query={exportDefaultQuery}
isLoading={isLoading}
onExport={handleExport}
sourcepage={DataSource.TRACES}
onExport={handleExport}
/>
</>
</ErrorBoundary>