feat: open left nav items in new tab on cmd ctrl click (#4561)

This commit is contained in:
Yunus M 2024-02-17 14:59:49 +05:30 committed by GitHub
parent b10f17de78
commit 0c59953cb5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 128 additions and 51 deletions

View File

@ -20,7 +20,7 @@ import { UPDATE_USER_IS_FETCH } from 'types/actions/app';
import AppReducer from 'types/reducer/app'; import AppReducer from 'types/reducer/app';
import { routePermission } from 'utils/permission'; import { routePermission } from 'utils/permission';
import routes from './routes'; import routes, { LIST_LICENSES } from './routes';
import afterLogin from './utils'; import afterLogin from './utils';
function PrivateRoute({ children }: PrivateRouteProps): JSX.Element { function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
@ -29,7 +29,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
const mapRoutes = useMemo( const mapRoutes = useMemo(
() => () =>
new Map( new Map(
routes.map((e) => { [...routes, LIST_LICENSES].map((e) => {
const currentPath = matchPath(pathname, { const currentPath = matchPath(pathname, {
path: e.path, path: e.path,
}); });

View File

@ -100,7 +100,10 @@ function TableView({
value: JSON.stringify(flattenLogData[key]), value: JSON.stringify(flattenLogData[key]),
})); }));
const onTraceHandler = (record: DataType) => (): void => { const onTraceHandler = (
record: DataType,
event: React.MouseEvent<HTMLDivElement, MouseEvent>,
) => (): void => {
if (flattenLogData === null) return; if (flattenLogData === null) return;
const traceId = flattenLogData[record.field]; const traceId = flattenLogData[record.field];
@ -119,8 +122,13 @@ function TableView({
const route = spanId ? `${basePath}?spanId=${spanId}` : basePath; const route = spanId ? `${basePath}?spanId=${spanId}` : basePath;
if (event.ctrlKey || event.metaKey) {
// open the trace in new tab
window.open(route, '_blank');
} else {
history.push(route); history.push(route);
} }
}
}; };
if (!dataSource) { if (!dataSource) {
@ -148,17 +156,20 @@ function TableView({
{traceId && ( {traceId && (
<Tooltip title="Inspect in Trace"> <Tooltip title="Inspect in Trace">
<div <Button
style={{ cursor: 'pointer' }} className="periscope-btn"
role="presentation" onClick={(
onClick={onTraceHandler(record)} event: React.MouseEvent<HTMLDivElement, MouseEvent>,
): void => {
onTraceHandler(record, event);
}}
> >
<LinkOutlined <LinkOutlined
style={{ style={{
width: '15px', width: '15px',
}} }}
/> />
</div> </Button>
</Tooltip> </Tooltip>
)} )}
</Space> </Space>

View File

@ -1,9 +1,20 @@
/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import './LogsError.styles.scss'; import './LogsError.styles.scss';
import { Typography } from 'antd'; import { Typography } from 'antd';
import history from 'lib/history';
import { ArrowRight } from 'lucide-react'; import { ArrowRight } from 'lucide-react';
import { isCloudUser } from 'utils/app';
export default function LogsError(): JSX.Element { export default function LogsError(): JSX.Element {
const handleContactSupport = (): void => {
if (isCloudUser()) {
history.push('/support');
} else {
window.open('https://signoz.io/slack', '_blank');
}
};
return ( return (
<div className="logs-error-container"> <div className="logs-error-container">
<div className="logs-error-content"> <div className="logs-error-content">
@ -16,10 +27,12 @@ export default function LogsError(): JSX.Element {
<span className="aww-snap">Aw snap :/ </span> Something went wrong. Please <span className="aww-snap">Aw snap :/ </span> Something went wrong. Please
try again or contact support. try again or contact support.
</Typography.Text> </Typography.Text>
<section className="contact-support">
<div className="contact-support" onClick={handleContactSupport}>
<Typography.Link className="text">Contact Support </Typography.Link> <Typography.Link className="text">Contact Support </Typography.Link>
<ArrowRight size={14} /> <ArrowRight size={14} />
</section> </div>
</div> </div>
</div> </div>
); );

View File

@ -9,13 +9,17 @@ export default function NoLogs(): JSX.Element {
<div className="no-logs-container-content"> <div className="no-logs-container-content">
<img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" /> <img className="eyes-emoji" src="/Images/eyesEmoji.svg" alt="eyes emoji" />
<Typography className="no-logs-text"> <Typography className="no-logs-text">
No logs yet.{' '} No logs yet.
<span className="sub-text"> <span className="sub-text">
When we receive logs, they would show up here When we receive logs, they would show up here
</span> </span>
</Typography> </Typography>
<Typography.Link className="send-logs-link"> <Typography.Link
className="send-logs-link"
href="https://signoz.io/docs/userguide/logs/"
target="_blank"
>
Sending Logs to SigNoz <ArrowUpRight size={16} /> Sending Logs to SigNoz <ArrowUpRight size={16} />
</Typography.Link> </Typography.Link>
</div> </div>

View File

@ -7,6 +7,7 @@
height: 36px; height: 36px;
margin-bottom: 4px; margin-bottom: 4px;
cursor: pointer;
&.active { &.active {
.nav-item-active-marker { .nav-item-active-marker {

View File

@ -16,13 +16,16 @@ export default function NavItem({
isCollapsed: boolean; isCollapsed: boolean;
item: SidebarItem; item: SidebarItem;
isActive: boolean; isActive: boolean;
onClick: () => void; onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
}): JSX.Element { }): JSX.Element {
const { label, icon } = item; const { label, icon } = item;
return ( return (
<Tooltip title={isCollapsed ? label : ''} placement="right"> <Tooltip title={isCollapsed ? label : ''} placement="right">
<div className={cx('nav-item', isActive ? 'active' : '')} onClick={onClick}> <div
className={cx('nav-item', isActive ? 'active' : '')}
onClick={(event): void => onClick(event)}
>
<div className="nav-item-active-marker" /> <div className="nav-item-active-marker" />
<div className="nav-item-data"> <div className="nav-item-data">
<div className="nav-item-icon">{icon}</div> <div className="nav-item-icon">{icon}</div>

View File

@ -19,7 +19,7 @@ import {
RocketIcon, RocketIcon,
UserCircle, UserCircle,
} from 'lucide-react'; } from 'lucide-react';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { MouseEvent, useCallback, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import { useLocation } from 'react-router-dom'; import { useLocation } from 'react-router-dom';
@ -40,7 +40,7 @@ import defaultMenuItems, {
trySignozCloudMenuItem, trySignozCloudMenuItem,
} from './menuItems'; } from './menuItems';
import NavItem from './NavItem/NavItem'; import NavItem from './NavItem/NavItem';
import { SecondaryMenuItemKey } from './sideNav.types'; import { SecondaryMenuItemKey, SidebarItem } from './sideNav.types';
import { getActiveMenuKeyFromPath } from './sideNav.utils'; import { getActiveMenuKeyFromPath } from './sideNav.utils';
interface UserManagementMenuItems { interface UserManagementMenuItems {
@ -88,10 +88,6 @@ function SideNav({
window.open('https://signoz.io/slack', '_blank'); window.open('https://signoz.io/slack', '_blank');
}; };
const onClickVersionHandler = (): void => {
history.push(ROUTES.VERSION);
};
const isLatestVersion = checkVersionState(currentVersion, latestVersion); const isLatestVersion = checkVersionState(currentVersion, latestVersion);
const [inviteMembers] = useComponentPermission(['invite_members'], role); const [inviteMembers] = useComponentPermission(['invite_members'], role);
@ -164,24 +160,50 @@ function SideNav({
); );
}; };
const onClickShortcuts = (): void => { const isCtrlMetaKey = (e: MouseEvent): boolean => e.ctrlKey || e.metaKey;
history.push(`/shortcuts`);
const openInNewTab = (path: string): void => {
window.open(path, '_blank');
}; };
const onClickGetStarted = (): void => { const onClickShortcuts = (e: MouseEvent): void => {
if (isCtrlMetaKey(e)) {
openInNewTab('/shortcuts');
} else {
history.push(`/shortcuts`);
}
};
const onClickGetStarted = (event: MouseEvent): void => {
if (isCtrlMetaKey(event)) {
openInNewTab('/get-started');
} else {
history.push(`/get-started`); history.push(`/get-started`);
}
};
const onClickVersionHandler = (event: MouseEvent): void => {
if (isCtrlMetaKey(event)) {
openInNewTab(ROUTES.VERSION);
} else {
history.push(ROUTES.VERSION);
}
}; };
const onClickHandler = useCallback( const onClickHandler = useCallback(
(key: string) => { (key: string, event: MouseEvent | null) => {
const params = new URLSearchParams(search); const params = new URLSearchParams(search);
const availableParams = routeConfig[key]; const availableParams = routeConfig[key];
const queryString = getQueryString(availableParams || [], params); const queryString = getQueryString(availableParams || [], params);
if (pathname !== key) { if (pathname !== key) {
if (event && isCtrlMetaKey(event)) {
openInNewTab(`${key}?${queryString.join('&')}`);
} else {
history.push(`${key}?${queryString.join('&')}`); history.push(`${key}?${queryString.join('&')}`);
} }
}
}, },
[pathname, search], [pathname, search],
); );
@ -220,16 +242,19 @@ function SideNav({
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [currentVersion, latestVersion]); }, [currentVersion, latestVersion]);
const handleUserManagentMenuItemClick = (key: string): void => { const handleUserManagentMenuItemClick = (
key: string,
event: MouseEvent,
): void => {
switch (key) { switch (key) {
case SecondaryMenuItemKey.Slack: case SecondaryMenuItemKey.Slack:
onClickSlackHandler(); onClickSlackHandler();
break; break;
case SecondaryMenuItemKey.Version: case SecondaryMenuItemKey.Version:
onClickVersionHandler(); onClickVersionHandler(event);
break; break;
default: default:
onClickHandler(key); onClickHandler(key, event);
break; break;
} }
}; };
@ -255,29 +280,41 @@ function SideNav({
? ROUTES.ORG_SETTINGS ? ROUTES.ORG_SETTINGS
: ROUTES.SETTINGS; : ROUTES.SETTINGS;
const handleMenuItemClick = (event: MouseEvent, item: SidebarItem): void => {
if (item.key === ROUTES.SETTINGS) {
if (isCtrlMetaKey(event)) {
openInNewTab(settingsRoute);
} else {
history.push(settingsRoute);
}
} else if (item) {
onClickHandler(item?.key as string, event);
}
};
useEffect(() => { useEffect(() => {
registerShortcut(GlobalShortcuts.SidebarCollapse, onCollapse); registerShortcut(GlobalShortcuts.SidebarCollapse, onCollapse);
registerShortcut(GlobalShortcuts.NavigateToServices, () => registerShortcut(GlobalShortcuts.NavigateToServices, () =>
onClickHandler(ROUTES.APPLICATION), onClickHandler(ROUTES.APPLICATION, null),
); );
registerShortcut(GlobalShortcuts.NavigateToTraces, () => registerShortcut(GlobalShortcuts.NavigateToTraces, () =>
onClickHandler(ROUTES.TRACE), onClickHandler(ROUTES.TRACE, null),
); );
registerShortcut(GlobalShortcuts.NavigateToLogs, () => registerShortcut(GlobalShortcuts.NavigateToLogs, () =>
onClickHandler(ROUTES.LOGS), onClickHandler(ROUTES.LOGS, null),
); );
registerShortcut(GlobalShortcuts.NavigateToDashboards, () => registerShortcut(GlobalShortcuts.NavigateToDashboards, () =>
onClickHandler(ROUTES.ALL_DASHBOARD), onClickHandler(ROUTES.ALL_DASHBOARD, null),
); );
registerShortcut(GlobalShortcuts.NavigateToAlerts, () => registerShortcut(GlobalShortcuts.NavigateToAlerts, () =>
onClickHandler(ROUTES.LIST_ALL_ALERT), onClickHandler(ROUTES.LIST_ALL_ALERT, null),
); );
registerShortcut(GlobalShortcuts.NavigateToExceptions, () => registerShortcut(GlobalShortcuts.NavigateToExceptions, () =>
onClickHandler(ROUTES.ALL_ERROR), onClickHandler(ROUTES.ALL_ERROR, null),
); );
return (): void => { return (): void => {
@ -297,9 +334,9 @@ function SideNav({
<div <div
className="brand-logo" className="brand-logo"
// eslint-disable-next-line react/no-unknown-property // eslint-disable-next-line react/no-unknown-property
onClick={(): void => { onClick={(event: MouseEvent): void => {
// Current home page // Current home page
onClickHandler(ROUTES.APPLICATION); onClickHandler(ROUTES.APPLICATION, event);
}} }}
> >
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" /> <img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
@ -314,7 +351,12 @@ function SideNav({
{isCloudUserVal && ( {isCloudUserVal && (
<div className="get-started-nav-items"> <div className="get-started-nav-items">
<Button className="get-started-btn" onClick={onClickGetStarted}> <Button
className="get-started-btn"
onClick={(event: MouseEvent): void => {
onClickGetStarted(event);
}}
>
<RocketIcon size={16} /> <RocketIcon size={16} />
{!collapsed && <> Get Started </>} {!collapsed && <> Get Started </>}
@ -329,12 +371,8 @@ function SideNav({
key={item.key || index} key={item.key || index}
item={item} item={item}
isActive={activeMenuKey === item.key} isActive={activeMenuKey === item.key}
onClick={(): void => { onClick={(event): void => {
if (item.key === ROUTES.SETTINGS) { handleMenuItemClick(event, item);
history.push(settingsRoute);
} else if (item) {
onClickHandler(item?.key as string);
}
}} }}
/> />
))} ))}
@ -366,8 +404,8 @@ function SideNav({
key={item?.key || index} key={item?.key || index}
item={item} item={item}
isActive={activeMenuKey === item?.key} isActive={activeMenuKey === item?.key}
onClick={(): void => { onClick={(event: MouseEvent): void => {
handleUserManagentMenuItemClick(item?.key as string); handleUserManagentMenuItemClick(item?.key as string, event);
}} }}
/> />
), ),
@ -379,8 +417,12 @@ function SideNav({
key={inviteMemberMenuItem.key} key={inviteMemberMenuItem.key}
item={inviteMemberMenuItem} item={inviteMemberMenuItem}
isActive={activeMenuKey === inviteMemberMenuItem?.key} isActive={activeMenuKey === inviteMemberMenuItem?.key}
onClick={(): void => { onClick={(event: React.MouseEvent): void => {
if (isCtrlMetaKey(event)) {
openInNewTab(`${inviteMemberMenuItem.key}`);
} else {
history.push(`${inviteMemberMenuItem.key}`); history.push(`${inviteMemberMenuItem.key}`);
}
}} }}
/> />
)} )}
@ -391,8 +433,11 @@ function SideNav({
key={ROUTES.MY_SETTINGS} key={ROUTES.MY_SETTINGS}
item={userSettingsMenuItem} item={userSettingsMenuItem}
isActive={activeMenuKey === userSettingsMenuItem?.key} isActive={activeMenuKey === userSettingsMenuItem?.key}
onClick={(): void => { onClick={(event: MouseEvent): void => {
handleUserManagentMenuItemClick(userSettingsMenuItem?.key as string); handleUserManagentMenuItemClick(
userSettingsMenuItem?.key as string,
event,
);
}} }}
/> />
)} )}