mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 12:18:58 +08:00
feat: added shortcuts page in the side nav (#4506)
* feat: added shortcuts page in the side nav * fix: update shortcuts for add to dashboard and alerts * fix: cmd+enter should stage and run query * chore: refactor the shortcuts utils * feat: support run query even when input is focussed * fix: dropdown visibility change * feat: add shortcuts for sideNav * feat: auto focus logs explorer search bar with hotkey * fix: update the shortcuts for sideNav and dependencies * fix: remove dashboard and alert shortcuts * fix: minor typo changes
This commit is contained in:
parent
0e331dd177
commit
3a20862d0c
@ -41,5 +41,6 @@
|
|||||||
"SUPPORT": "SigNoz | Support",
|
"SUPPORT": "SigNoz | Support",
|
||||||
"LOGS_SAVE_VIEWS": "SigNoz | Logs Save Views",
|
"LOGS_SAVE_VIEWS": "SigNoz | Logs Save Views",
|
||||||
"TRACES_SAVE_VIEWS": "SigNoz | Traces Save Views",
|
"TRACES_SAVE_VIEWS": "SigNoz | Traces Save Views",
|
||||||
"DEFAULT": "Open source Observability Platform | SigNoz"
|
"DEFAULT": "Open source Observability Platform | SigNoz",
|
||||||
|
"SHORTCUTS": "SigNoz | Shortcuts"
|
||||||
}
|
}
|
||||||
|
@ -182,3 +182,7 @@ export const WorkspaceBlocked = Loadable(
|
|||||||
() =>
|
() =>
|
||||||
import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'),
|
import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const ShortcutsPage = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'),
|
||||||
|
);
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
|
import Shortcuts from 'pages/Shortcuts/Shortcuts';
|
||||||
import WorkspaceBlocked from 'pages/WorkspaceLocked';
|
import WorkspaceBlocked from 'pages/WorkspaceLocked';
|
||||||
import { RouteProps } from 'react-router-dom';
|
import { RouteProps } from 'react-router-dom';
|
||||||
|
|
||||||
@ -319,6 +320,13 @@ const routes: AppRoutes[] = [
|
|||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
key: 'WORKSPACE_LOCKED',
|
key: 'WORKSPACE_LOCKED',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.SHORTCUTS,
|
||||||
|
exact: true,
|
||||||
|
component: Shortcuts,
|
||||||
|
isPrivate: true,
|
||||||
|
key: 'SHORTCUTS',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SUPPORT_ROUTE: AppRoutes = {
|
export const SUPPORT_ROUTE: AppRoutes = {
|
||||||
|
@ -44,6 +44,7 @@ const ROUTES = {
|
|||||||
LOGS_SAVE_VIEWS: '/logs-save-views',
|
LOGS_SAVE_VIEWS: '/logs-save-views',
|
||||||
TRACES_SAVE_VIEWS: '/traces-save-views',
|
TRACES_SAVE_VIEWS: '/traces-save-views',
|
||||||
WORKSPACE_LOCKED: '/workspace-locked',
|
WORKSPACE_LOCKED: '/workspace-locked',
|
||||||
|
SHORTCUTS: '/shortcuts',
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export default ROUTES;
|
export default ROUTES;
|
||||||
|
@ -1,3 +1,19 @@
|
|||||||
export const GlobalShortcuts = {
|
export const GlobalShortcuts = {
|
||||||
SidebarCollapse: '\\+meta',
|
SidebarCollapse: '\\+meta',
|
||||||
|
NavigateToServices: 's+shift',
|
||||||
|
NavigateToTraces: 't+shift',
|
||||||
|
NavigateToLogs: 'l+shift',
|
||||||
|
NavigateToDashboards: 'd+shift',
|
||||||
|
NavigateToAlerts: 'a+shift',
|
||||||
|
NavigateToExceptions: 'e+shift',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GlobalShortcutsDescription = {
|
||||||
|
SidebarCollapse: 'Collpase the sidebar',
|
||||||
|
NavigateToServices: 'Navigate to Services page',
|
||||||
|
NavigateToTraces: 'Navigate to Traces page',
|
||||||
|
NavigateToLogs: 'Navigate to logs page',
|
||||||
|
NavigateToDashboards: 'Navigate to dashboards page',
|
||||||
|
NavigateToAlerts: 'Navigate to alerts page',
|
||||||
|
NavigateToExceptions: 'Navigate to Exceptions page',
|
||||||
};
|
};
|
||||||
|
@ -0,0 +1,9 @@
|
|||||||
|
export const LogsExplorerShortcuts = {
|
||||||
|
StageAndRunQuery: 'enter+meta',
|
||||||
|
FocusTheSearchBar: 's',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LogsExplorerShortcutsDescription = {
|
||||||
|
StageAndRunQuery: 'Stage and Run the current query',
|
||||||
|
FocusTheSearchBar: 'Shift the focus to the filter bar',
|
||||||
|
};
|
@ -1,7 +1,10 @@
|
|||||||
import './ToolbarActions.styles.scss';
|
import './ToolbarActions.styles.scss';
|
||||||
|
|
||||||
import { Button } from 'antd';
|
import { Button } from 'antd';
|
||||||
|
import { LogsExplorerShortcuts } from 'constants/shortcuts/logsExplorerShortcuts';
|
||||||
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import { Play } from 'lucide-react';
|
import { Play } from 'lucide-react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
interface RightToolbarActionsProps {
|
interface RightToolbarActionsProps {
|
||||||
onStageRunQuery: () => void;
|
onStageRunQuery: () => void;
|
||||||
@ -10,6 +13,16 @@ interface RightToolbarActionsProps {
|
|||||||
export default function RightToolbarActions({
|
export default function RightToolbarActions({
|
||||||
onStageRunQuery,
|
onStageRunQuery,
|
||||||
}: RightToolbarActionsProps): JSX.Element {
|
}: RightToolbarActionsProps): JSX.Element {
|
||||||
|
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerShortcut(LogsExplorerShortcuts.StageAndRunQuery, onStageRunQuery);
|
||||||
|
|
||||||
|
return (): void => {
|
||||||
|
deregisterShortcut(LogsExplorerShortcuts.StageAndRunQuery);
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [onStageRunQuery]);
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<Button
|
<Button
|
||||||
|
@ -2,12 +2,16 @@ import './QueryBuilderSearch.styles.scss';
|
|||||||
|
|
||||||
import { Select, Spin, Tag, Tooltip } from 'antd';
|
import { Select, Spin, Tag, Tooltip } from 'antd';
|
||||||
import { OPERATORS } from 'constants/queryBuilder';
|
import { OPERATORS } from 'constants/queryBuilder';
|
||||||
|
import { LogsExplorerShortcuts } from 'constants/shortcuts/logsExplorerShortcuts';
|
||||||
import { getDataTypes } from 'container/LogDetailedView/utils';
|
import { getDataTypes } from 'container/LogDetailedView/utils';
|
||||||
|
import { useKeyboardHotkeys } from 'hooks/hotkeys/useKeyboardHotkeys';
|
||||||
import {
|
import {
|
||||||
useAutoComplete,
|
useAutoComplete,
|
||||||
WhereClauseConfig,
|
WhereClauseConfig,
|
||||||
} from 'hooks/queryBuilder/useAutoComplete';
|
} from 'hooks/queryBuilder/useAutoComplete';
|
||||||
import { useFetchKeysAndValues } from 'hooks/queryBuilder/useFetchKeysAndValues';
|
import { useFetchKeysAndValues } from 'hooks/queryBuilder/useFetchKeysAndValues';
|
||||||
|
import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
||||||
|
import type { BaseSelectRef } from 'rc-select';
|
||||||
import {
|
import {
|
||||||
KeyboardEvent,
|
KeyboardEvent,
|
||||||
ReactElement,
|
ReactElement,
|
||||||
@ -15,6 +19,8 @@ import {
|
|||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
useMemo,
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import {
|
import {
|
||||||
BaseAutocompleteData,
|
BaseAutocompleteData,
|
||||||
@ -63,12 +69,18 @@ function QueryBuilderSearch({
|
|||||||
searchKey,
|
searchKey,
|
||||||
} = useAutoComplete(query, whereClauseConfig);
|
} = useAutoComplete(query, whereClauseConfig);
|
||||||
|
|
||||||
|
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||||
|
const selectRef = useRef<BaseSelectRef>(null);
|
||||||
const { sourceKeys, handleRemoveSourceKey } = useFetchKeysAndValues(
|
const { sourceKeys, handleRemoveSourceKey } = useFetchKeysAndValues(
|
||||||
searchValue,
|
searchValue,
|
||||||
query,
|
query,
|
||||||
searchKey,
|
searchKey,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||||
|
|
||||||
|
const { handleRunQuery } = useQueryBuilder();
|
||||||
|
|
||||||
const onTagRender = ({
|
const onTagRender = ({
|
||||||
value,
|
value,
|
||||||
closable,
|
closable,
|
||||||
@ -119,6 +131,13 @@ function QueryBuilderSearch({
|
|||||||
const onInputKeyDownHandler = (event: KeyboardEvent<Element>): void => {
|
const onInputKeyDownHandler = (event: KeyboardEvent<Element>): void => {
|
||||||
if (isMulti || event.key === 'Backspace') handleKeyDown(event);
|
if (isMulti || event.key === 'Backspace') handleKeyDown(event);
|
||||||
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
if (isExistsNotExistsOperator(searchValue)) handleKeyDown(event);
|
||||||
|
|
||||||
|
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
handleRunQuery();
|
||||||
|
setIsOpen(false);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDeselect = useCallback(
|
const handleDeselect = useCallback(
|
||||||
@ -185,6 +204,18 @@ function QueryBuilderSearch({
|
|||||||
/* eslint-disable react-hooks/exhaustive-deps */
|
/* eslint-disable react-hooks/exhaustive-deps */
|
||||||
}, [sourceKeys]);
|
}, [sourceKeys]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerShortcut(LogsExplorerShortcuts.FocusTheSearchBar, () => {
|
||||||
|
// set timeout is needed here else the select treats the hotkey as input value
|
||||||
|
setTimeout(() => {
|
||||||
|
selectRef.current?.focus();
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
return (): void =>
|
||||||
|
deregisterShortcut(LogsExplorerShortcuts.FocusTheSearchBar);
|
||||||
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
@ -192,11 +223,14 @@ function QueryBuilderSearch({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select
|
<Select
|
||||||
|
ref={selectRef}
|
||||||
getPopupContainer={popupContainer}
|
getPopupContainer={popupContainer}
|
||||||
virtual
|
virtual
|
||||||
showSearch
|
showSearch
|
||||||
tagRender={onTagRender}
|
tagRender={onTagRender}
|
||||||
filterOption={false}
|
filterOption={false}
|
||||||
|
open={isOpen}
|
||||||
|
onDropdownVisibleChange={setIsOpen}
|
||||||
autoClearSearchValue={false}
|
autoClearSearchValue={false}
|
||||||
mode="multiple"
|
mode="multiple"
|
||||||
placeholder={placeholder}
|
placeholder={placeholder}
|
||||||
@ -213,6 +247,7 @@ function QueryBuilderSearch({
|
|||||||
onInputKeyDown={onInputKeyDownHandler}
|
onInputKeyDown={onInputKeyDownHandler}
|
||||||
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
notFoundContent={isFetching ? <Spin size="small" /> : null}
|
||||||
suffixIcon={suffixIcon}
|
suffixIcon={suffixIcon}
|
||||||
|
showAction={['focus']}
|
||||||
>
|
>
|
||||||
{options.map((option) => (
|
{options.map((option) => (
|
||||||
<Select.Option key={option.label} value={option.value}>
|
<Select.Option key={option.label} value={option.value}>
|
||||||
|
@ -35,6 +35,7 @@ import defaultMenuItems, {
|
|||||||
helpSupportMenuItem,
|
helpSupportMenuItem,
|
||||||
inviteMemberMenuItem,
|
inviteMemberMenuItem,
|
||||||
manageLicenseMenuItem,
|
manageLicenseMenuItem,
|
||||||
|
shortcutMenuItem,
|
||||||
slackSupportMenuItem,
|
slackSupportMenuItem,
|
||||||
trySignozCloudMenuItem,
|
trySignozCloudMenuItem,
|
||||||
} from './menuItems';
|
} from './menuItems';
|
||||||
@ -147,15 +148,6 @@ function SideNav({
|
|||||||
|
|
||||||
const { t } = useTranslation('');
|
const { t } = useTranslation('');
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
registerShortcut(GlobalShortcuts.SidebarCollapse, onCollapse);
|
|
||||||
|
|
||||||
return (): void => {
|
|
||||||
deregisterShortcut(GlobalShortcuts.SidebarCollapse);
|
|
||||||
};
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const isLicenseActive =
|
const isLicenseActive =
|
||||||
licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status ===
|
licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status ===
|
||||||
LICENSE_PLAN_STATUS.VALID;
|
LICENSE_PLAN_STATUS.VALID;
|
||||||
@ -172,6 +164,10 @@ function SideNav({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClickShortcuts = (): void => {
|
||||||
|
history.push(`/shortcuts`);
|
||||||
|
};
|
||||||
|
|
||||||
const onClickGetStarted = (): void => {
|
const onClickGetStarted = (): void => {
|
||||||
history.push(`/get-started`);
|
history.push(`/get-started`);
|
||||||
};
|
};
|
||||||
@ -259,6 +255,42 @@ function SideNav({
|
|||||||
? ROUTES.ORG_SETTINGS
|
? ROUTES.ORG_SETTINGS
|
||||||
: ROUTES.SETTINGS;
|
: ROUTES.SETTINGS;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
registerShortcut(GlobalShortcuts.SidebarCollapse, onCollapse);
|
||||||
|
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToServices, () =>
|
||||||
|
onClickHandler(ROUTES.APPLICATION),
|
||||||
|
);
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToTraces, () =>
|
||||||
|
onClickHandler(ROUTES.TRACE),
|
||||||
|
);
|
||||||
|
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToLogs, () =>
|
||||||
|
onClickHandler(ROUTES.LOGS),
|
||||||
|
);
|
||||||
|
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToDashboards, () =>
|
||||||
|
onClickHandler(ROUTES.ALL_DASHBOARD),
|
||||||
|
);
|
||||||
|
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToAlerts, () =>
|
||||||
|
onClickHandler(ROUTES.LIST_ALL_ALERT),
|
||||||
|
);
|
||||||
|
registerShortcut(GlobalShortcuts.NavigateToExceptions, () =>
|
||||||
|
onClickHandler(ROUTES.ALL_ERROR),
|
||||||
|
);
|
||||||
|
|
||||||
|
return (): void => {
|
||||||
|
deregisterShortcut(GlobalShortcuts.SidebarCollapse);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToServices);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToTraces);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToLogs);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToDashboards);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToAlerts);
|
||||||
|
deregisterShortcut(GlobalShortcuts.NavigateToExceptions);
|
||||||
|
};
|
||||||
|
}, [deregisterShortcut, onClickHandler, onCollapse, registerShortcut]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cx('sideNav', collapsed ? 'collapsed' : '')}>
|
<div className={cx('sideNav', collapsed ? 'collapsed' : '')}>
|
||||||
<div className="brand">
|
<div className="brand">
|
||||||
@ -309,6 +341,14 @@ function SideNav({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="secondary-nav-items">
|
<div className="secondary-nav-items">
|
||||||
|
<NavItem
|
||||||
|
isCollapsed={collapsed}
|
||||||
|
key="keyboardShortcuts"
|
||||||
|
item={shortcutMenuItem}
|
||||||
|
isActive={false}
|
||||||
|
onClick={onClickShortcuts}
|
||||||
|
/>
|
||||||
|
|
||||||
{licenseData && !isLicenseActive && (
|
{licenseData && !isLicenseActive && (
|
||||||
<NavItem
|
<NavItem
|
||||||
isCollapsed={collapsed}
|
isCollapsed={collapsed}
|
||||||
|
@ -8,6 +8,7 @@ import {
|
|||||||
Cloudy,
|
Cloudy,
|
||||||
DraftingCompass,
|
DraftingCompass,
|
||||||
FileKey2,
|
FileKey2,
|
||||||
|
Layers2,
|
||||||
LayoutGrid,
|
LayoutGrid,
|
||||||
MessageSquare,
|
MessageSquare,
|
||||||
Receipt,
|
Receipt,
|
||||||
@ -44,6 +45,12 @@ export const helpSupportMenuItem = {
|
|||||||
icon: <MessageSquare size={16} />,
|
icon: <MessageSquare size={16} />,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const shortcutMenuItem = {
|
||||||
|
key: ROUTES.SHORTCUTS,
|
||||||
|
label: 'Keyboard Shortcuts',
|
||||||
|
icon: <Layers2 size={16} />,
|
||||||
|
};
|
||||||
|
|
||||||
export const slackSupportMenuItem = {
|
export const slackSupportMenuItem = {
|
||||||
key: SecondaryMenuItemKey.Slack,
|
key: SecondaryMenuItemKey.Slack,
|
||||||
label: 'Slack Support',
|
label: 'Slack Support',
|
||||||
|
@ -133,6 +133,7 @@ export const routesToSkip = [
|
|||||||
ROUTES.LOGS_PIPELINES,
|
ROUTES.LOGS_PIPELINES,
|
||||||
ROUTES.TRACES_EXPLORER,
|
ROUTES.TRACES_EXPLORER,
|
||||||
ROUTES.TRACES_SAVE_VIEWS,
|
ROUTES.TRACES_SAVE_VIEWS,
|
||||||
|
ROUTES.SHORTCUTS,
|
||||||
];
|
];
|
||||||
|
|
||||||
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
||||||
|
24
frontend/src/pages/Shortcuts/Shortcuts.styles.scss
Normal file
24
frontend/src/pages/Shortcuts/Shortcuts.styles.scss
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
.keyboard-shortcuts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 1rem;
|
||||||
|
gap: 50px;
|
||||||
|
|
||||||
|
.shortcut-section {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.shortcut-section-heading {
|
||||||
|
color: rgba(255, 255, 255, 0.85);
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1.3636363636363635;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shortcut-section-table {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
frontend/src/pages/Shortcuts/Shortcuts.tsx
Normal file
35
frontend/src/pages/Shortcuts/Shortcuts.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import './Shortcuts.styles.scss';
|
||||||
|
|
||||||
|
import { Table, Typography } from 'antd';
|
||||||
|
|
||||||
|
import { ALL_SHORTCUTS, generateTableData, shortcutColumns } from './utils';
|
||||||
|
|
||||||
|
function Shortcuts(): JSX.Element {
|
||||||
|
function getShortcutTable(shortcutSection: string): JSX.Element {
|
||||||
|
const tableData = generateTableData(shortcutSection);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="shortcut-section">
|
||||||
|
<Typography.Text className="shortcut-section-heading">
|
||||||
|
{shortcutSection}
|
||||||
|
</Typography.Text>
|
||||||
|
<Table
|
||||||
|
columns={shortcutColumns}
|
||||||
|
dataSource={tableData}
|
||||||
|
pagination={false}
|
||||||
|
className="shortcut-section-table"
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="keyboard-shortcuts">
|
||||||
|
{Object.keys(ALL_SHORTCUTS).map((shortcutSection) =>
|
||||||
|
getShortcutTable(shortcutSection),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Shortcuts;
|
3
frontend/src/pages/Shortcuts/index.ts
Normal file
3
frontend/src/pages/Shortcuts/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Shortcuts from './Shortcuts';
|
||||||
|
|
||||||
|
export default Shortcuts;
|
54
frontend/src/pages/Shortcuts/utils.ts
Normal file
54
frontend/src/pages/Shortcuts/utils.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { TableProps } from 'antd';
|
||||||
|
import {
|
||||||
|
GlobalShortcuts,
|
||||||
|
GlobalShortcutsDescription,
|
||||||
|
} from 'constants/shortcuts/globalShortcuts';
|
||||||
|
import {
|
||||||
|
LogsExplorerShortcuts,
|
||||||
|
LogsExplorerShortcutsDescription,
|
||||||
|
} from 'constants/shortcuts/logsExplorerShortcuts';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
||||||
|
export const ALL_SHORTCUTS: Record<string, Record<string, string>> = {
|
||||||
|
'Global Shortcuts': GlobalShortcuts,
|
||||||
|
'Logs Explorer Shortcuts': LogsExplorerShortcuts,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const ALL_SHORTCUTS_DESCRIPTION: Record<
|
||||||
|
string,
|
||||||
|
Record<string, string>
|
||||||
|
> = {
|
||||||
|
'Global Shortcuts': GlobalShortcutsDescription,
|
||||||
|
'Logs Explorer Shortcuts': LogsExplorerShortcutsDescription,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const shortcutColumns = [
|
||||||
|
{
|
||||||
|
title: 'Keyboard Shortcut',
|
||||||
|
dataIndex: 'shortcutKey',
|
||||||
|
key: 'shortcutKey',
|
||||||
|
width: '30%',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Description',
|
||||||
|
dataIndex: 'shortcutDescription',
|
||||||
|
key: 'shortcutDescription',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
interface ShortcutRow {
|
||||||
|
shortcutKey: string;
|
||||||
|
shortcutDescription: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function generateTableData(
|
||||||
|
shortcutSection: string,
|
||||||
|
): TableProps<ShortcutRow>['dataSource'] {
|
||||||
|
const shortcuts = ALL_SHORTCUTS[shortcutSection];
|
||||||
|
const shortcutsDescription = ALL_SHORTCUTS_DESCRIPTION[shortcutSection];
|
||||||
|
return Object.keys(shortcuts).map((shortcutName) => ({
|
||||||
|
key: `${shortcuts[shortcutName]} ${shortcutName}`,
|
||||||
|
shortcutKey: shortcuts[shortcutName],
|
||||||
|
shortcutDescription: shortcutsDescription[shortcutName],
|
||||||
|
}));
|
||||||
|
}
|
@ -90,4 +90,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
|||||||
TRACES_SAVE_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
TRACES_SAVE_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
LOGS_BASE: [],
|
LOGS_BASE: [],
|
||||||
OLD_LOGS_EXPLORER: [],
|
OLD_LOGS_EXPLORER: [],
|
||||||
|
SHORTCUTS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user