mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 18:59:10 +08:00
feat: add support page (#3768)
* feat: add support page * feat: handle chat, slack connect and book a call functionality
This commit is contained in:
parent
6e20fbb174
commit
814431e3a8
@ -71,6 +71,7 @@
|
|||||||
"less": "^4.1.2",
|
"less": "^4.1.2",
|
||||||
"less-loader": "^10.2.0",
|
"less-loader": "^10.2.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"lucide-react": "0.288.0",
|
||||||
"mini-css-extract-plugin": "2.4.5",
|
"mini-css-extract-plugin": "2.4.5",
|
||||||
"papaparse": "5.4.1",
|
"papaparse": "5.4.1",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
|
@ -36,5 +36,6 @@
|
|||||||
"PASSWORD_RESET": "SigNoz | Password Reset",
|
"PASSWORD_RESET": "SigNoz | Password Reset",
|
||||||
"LIST_LICENSES": "SigNoz | List of Licenses",
|
"LIST_LICENSES": "SigNoz | List of Licenses",
|
||||||
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
||||||
|
"SUPPORT": "SigNoz | Support",
|
||||||
"DEFAULT": "Open source Observability Platform | SigNoz"
|
"DEFAULT": "Open source Observability Platform | SigNoz"
|
||||||
}
|
}
|
||||||
|
@ -36,5 +36,6 @@
|
|||||||
"PASSWORD_RESET": "SigNoz | Password Reset",
|
"PASSWORD_RESET": "SigNoz | Password Reset",
|
||||||
"LIST_LICENSES": "SigNoz | List of Licenses",
|
"LIST_LICENSES": "SigNoz | List of Licenses",
|
||||||
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
||||||
|
"SUPPORT": "SigNoz | Support",
|
||||||
"DEFAULT": "Open source Observability Platform | SigNoz"
|
"DEFAULT": "Open source Observability Platform | SigNoz"
|
||||||
}
|
}
|
||||||
|
@ -23,16 +23,16 @@ import { AppState } from 'store/reducers';
|
|||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
|
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
|
||||||
import AppReducer, { User } from 'types/reducer/app';
|
import AppReducer, { User } from 'types/reducer/app';
|
||||||
import { extractDomain, isCloudUser } from 'utils/app';
|
import { extractDomain, isCloudUser, isEECloudUser } from 'utils/app';
|
||||||
import { trackPageView } from 'utils/segmentAnalytics';
|
import { trackPageView } from 'utils/segmentAnalytics';
|
||||||
|
|
||||||
import PrivateRoute from './Private';
|
import PrivateRoute from './Private';
|
||||||
import defaultRoutes from './routes';
|
import defaultRoutes, { AppRoutes, SUPPORT_ROUTE } from './routes';
|
||||||
|
|
||||||
function App(): JSX.Element {
|
function App(): JSX.Element {
|
||||||
const themeConfig = useThemeConfig();
|
const themeConfig = useThemeConfig();
|
||||||
const { data } = useLicense();
|
const { data } = useLicense();
|
||||||
const [routes, setRoutes] = useState(defaultRoutes);
|
const [routes, setRoutes] = useState<AppRoutes[]>(defaultRoutes);
|
||||||
const { role, isLoggedIn: isLoggedInState, user, org } = useSelector<
|
const { role, isLoggedIn: isLoggedInState, user, org } = useSelector<
|
||||||
AppState,
|
AppState,
|
||||||
AppReducer
|
AppReducer
|
||||||
@ -136,6 +136,13 @@ function App(): JSX.Element {
|
|||||||
const newRoutes = routes.filter((route) => route?.path !== ROUTES.BILLING);
|
const newRoutes = routes.filter((route) => route?.path !== ROUTES.BILLING);
|
||||||
setRoutes(newRoutes);
|
setRoutes(newRoutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCloudUserVal || isEECloudUser()) {
|
||||||
|
const newRoutes = [...routes, SUPPORT_ROUTE];
|
||||||
|
|
||||||
|
setRoutes(newRoutes);
|
||||||
|
}
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [isLoggedInState, isOnBasicPlan, user]);
|
}, [isLoggedInState, isOnBasicPlan, user]);
|
||||||
|
|
||||||
|
@ -158,6 +158,10 @@ export const BillingPage = Loadable(
|
|||||||
() => import(/* webpackChunkName: "BillingPage" */ 'pages/Billing'),
|
() => import(/* webpackChunkName: "BillingPage" */ 'pages/Billing'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const SupportPage = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "SupportPage" */ 'pages/Support'),
|
||||||
|
);
|
||||||
|
|
||||||
export const WorkspaceBlocked = Loadable(
|
export const WorkspaceBlocked = Loadable(
|
||||||
() =>
|
() =>
|
||||||
import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'),
|
import(/* webpackChunkName: "WorkspaceLocked" */ 'pages/WorkspaceLocked'),
|
||||||
|
@ -34,6 +34,7 @@ import {
|
|||||||
SignupPage,
|
SignupPage,
|
||||||
SomethingWentWrong,
|
SomethingWentWrong,
|
||||||
StatusPage,
|
StatusPage,
|
||||||
|
SupportPage,
|
||||||
TraceDetail,
|
TraceDetail,
|
||||||
TraceFilter,
|
TraceFilter,
|
||||||
TracesExplorer,
|
TracesExplorer,
|
||||||
@ -287,7 +288,6 @@ const routes: AppRoutes[] = [
|
|||||||
key: 'PIPELINES',
|
key: 'PIPELINES',
|
||||||
isPrivate: true,
|
isPrivate: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: ROUTES.BILLING,
|
path: ROUTES.BILLING,
|
||||||
exact: true,
|
exact: true,
|
||||||
@ -304,6 +304,14 @@ const routes: AppRoutes[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const SUPPORT_ROUTE: AppRoutes = {
|
||||||
|
path: ROUTES.SUPPORT,
|
||||||
|
exact: true,
|
||||||
|
component: SupportPage,
|
||||||
|
key: 'SUPPORT',
|
||||||
|
isPrivate: true,
|
||||||
|
};
|
||||||
|
|
||||||
export interface AppRoutes {
|
export interface AppRoutes {
|
||||||
component: RouteProps['component'];
|
component: RouteProps['component'];
|
||||||
path: RouteProps['path'];
|
path: RouteProps['path'];
|
||||||
|
@ -39,6 +39,7 @@ const ROUTES = {
|
|||||||
TRACE_EXPLORER: '/trace-explorer',
|
TRACE_EXPLORER: '/trace-explorer',
|
||||||
PIPELINES: '/pipelines',
|
PIPELINES: '/pipelines',
|
||||||
BILLING: '/billing',
|
BILLING: '/billing',
|
||||||
|
SUPPORT: '/support',
|
||||||
WORKSPACE_LOCKED: '/workspace-locked',
|
WORKSPACE_LOCKED: '/workspace-locked',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import { FeatureKeys } from 'constants/features';
|
|||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
|
import { LifeBuoy } from 'lucide-react';
|
||||||
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
import { useCallback, useLayoutEffect, useMemo, useState } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useDispatch, useSelector } from 'react-redux';
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
@ -13,7 +14,7 @@ import { useLocation } from 'react-router-dom';
|
|||||||
import { sideBarCollapse } from 'store/actions/app';
|
import { sideBarCollapse } from 'store/actions/app';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import { checkVersionState, isCloudUser } from 'utils/app';
|
import { checkVersionState, isCloudUser, isEECloudUser } from 'utils/app';
|
||||||
|
|
||||||
import { routeConfig, styles } from './config';
|
import { routeConfig, styles } from './config';
|
||||||
import { getQueryString } from './helper';
|
import { getQueryString } from './helper';
|
||||||
@ -45,6 +46,8 @@ function SideNav(): JSX.Element {
|
|||||||
|
|
||||||
const { data } = useLicense();
|
const { data } = useLicense();
|
||||||
|
|
||||||
|
let secondaryMenuItems: MenuItem[] = [];
|
||||||
|
|
||||||
const isOnBasicPlan =
|
const isOnBasicPlan =
|
||||||
data?.payload?.licenses?.some(
|
data?.payload?.licenses?.some(
|
||||||
(license) =>
|
(license) =>
|
||||||
@ -112,7 +115,16 @@ function SideNav(): JSX.Element {
|
|||||||
|
|
||||||
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
||||||
|
|
||||||
const secondaryMenuItems: MenuItem[] = [
|
if (isCloudUser() || isEECloudUser()) {
|
||||||
|
secondaryMenuItems = [
|
||||||
|
{
|
||||||
|
key: SecondaryMenuItemKey.Support,
|
||||||
|
label: 'Support',
|
||||||
|
icon: <LifeBuoy />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
} else {
|
||||||
|
secondaryMenuItems = [
|
||||||
{
|
{
|
||||||
key: SecondaryMenuItemKey.Version,
|
key: SecondaryMenuItemKey.Version,
|
||||||
icon: !isLatestVersion ? (
|
icon: !isLatestVersion ? (
|
||||||
@ -137,6 +149,7 @@ function SideNav(): JSX.Element {
|
|||||||
onClick: onClickSlackHandler,
|
onClick: onClickSlackHandler,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
}
|
||||||
|
|
||||||
const activeMenuKey = useMemo(() => getActiveMenuKeyFromPath(pathname), [
|
const activeMenuKey = useMemo(() => getActiveMenuKeyFromPath(pathname), [
|
||||||
pathname,
|
pathname,
|
||||||
@ -159,6 +172,7 @@ function SideNav(): JSX.Element {
|
|||||||
mode="vertical"
|
mode="vertical"
|
||||||
style={styles}
|
style={styles}
|
||||||
items={secondaryMenuItems}
|
items={secondaryMenuItems}
|
||||||
|
onClick={onClickMenuHandler}
|
||||||
/>
|
/>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
|
@ -18,4 +18,5 @@ export interface SidebarItem {
|
|||||||
export enum SecondaryMenuItemKey {
|
export enum SecondaryMenuItemKey {
|
||||||
Slack = 'slack',
|
Slack = 'slack',
|
||||||
Version = 'version',
|
Version = 'version',
|
||||||
|
Support = 'support',
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,6 @@ export const StyledPrimaryMenu = styled(Menu)`
|
|||||||
export const StyledSecondaryMenu = styled(Menu)`
|
export const StyledSecondaryMenu = styled(Menu)`
|
||||||
&&& {
|
&&& {
|
||||||
:not(.ant-menu-inline-collapsed) > .ant-menu-item {
|
:not(.ant-menu-inline-collapsed) > .ant-menu-item {
|
||||||
padding-inline: 48px;
|
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
@ -25,6 +25,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.LIVE_LOGS]: 'Live View',
|
[ROUTES.LIVE_LOGS]: 'Live View',
|
||||||
[ROUTES.PIPELINES]: 'Pipelines',
|
[ROUTES.PIPELINES]: 'Pipelines',
|
||||||
[ROUTES.BILLING]: 'Billing',
|
[ROUTES.BILLING]: 'Billing',
|
||||||
|
[ROUTES.SUPPORT]: 'Support',
|
||||||
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -85,6 +85,7 @@ export const routesToSkip = [
|
|||||||
ROUTES.LIST_ALL_ALERT,
|
ROUTES.LIST_ALL_ALERT,
|
||||||
ROUTES.PIPELINES,
|
ROUTES.PIPELINES,
|
||||||
ROUTES.BILLING,
|
ROUTES.BILLING,
|
||||||
|
ROUTES.SUPPORT,
|
||||||
ROUTES.WORKSPACE_LOCKED,
|
ROUTES.WORKSPACE_LOCKED,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -33,15 +33,19 @@ function TopNav(): JSX.Element | null {
|
|||||||
[location.pathname],
|
[location.pathname],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const hideBreadcrumbs = location.pathname === ROUTES.SUPPORT;
|
||||||
|
|
||||||
if (isSignUpPage || isDisabled) {
|
if (isSignUpPage || isDisabled) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
|
{!hideBreadcrumbs && (
|
||||||
<Col span={16}>
|
<Col span={16}>
|
||||||
<ShowBreadcrumbs />
|
<ShowBreadcrumbs />
|
||||||
</Col>
|
</Col>
|
||||||
|
)}
|
||||||
|
|
||||||
{!isRouteToSkip && (
|
{!isRouteToSkip && (
|
||||||
<Col span={8}>
|
<Col span={8}>
|
||||||
|
53
frontend/src/pages/Support/Support.styles.scss
Normal file
53
frontend/src/pages/Support/Support.styles.scss
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
.support-page-container {
|
||||||
|
color: white;
|
||||||
|
padding-left: 48px;
|
||||||
|
padding-right: 48px;
|
||||||
|
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-channels {
|
||||||
|
margin: 48px 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-channel {
|
||||||
|
flex: 0 0 calc(33.333% - 32px);
|
||||||
|
min-height: 200px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.support-channel-title {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.support-channel-action {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 24px;
|
||||||
|
left: 24px;
|
||||||
|
width: calc(100% - 48px);
|
||||||
|
|
||||||
|
button {
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1440px) {
|
||||||
|
.support-channel {
|
||||||
|
min-height: 240px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 1440px) {
|
||||||
|
.support-page-container {
|
||||||
|
width: 80%;
|
||||||
|
}
|
||||||
|
}
|
169
frontend/src/pages/Support/Support.tsx
Normal file
169
frontend/src/pages/Support/Support.tsx
Normal file
@ -0,0 +1,169 @@
|
|||||||
|
import './Support.styles.scss';
|
||||||
|
|
||||||
|
import { Button, Card, Typography } from 'antd';
|
||||||
|
import {
|
||||||
|
Book,
|
||||||
|
Cable,
|
||||||
|
Calendar,
|
||||||
|
Github,
|
||||||
|
MessageSquare,
|
||||||
|
Slack,
|
||||||
|
} from 'lucide-react';
|
||||||
|
|
||||||
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
|
interface Channel {
|
||||||
|
key: any;
|
||||||
|
name?: string;
|
||||||
|
icon?: JSX.Element;
|
||||||
|
title?: string;
|
||||||
|
url: any;
|
||||||
|
btnText?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const channelsMap = {
|
||||||
|
documentation: 'documentation',
|
||||||
|
github: 'github',
|
||||||
|
slack_community: 'slack_community',
|
||||||
|
chat: 'chat',
|
||||||
|
schedule_call: 'schedule_call',
|
||||||
|
slack_connect: 'slack_connect',
|
||||||
|
};
|
||||||
|
|
||||||
|
const supportChannels = [
|
||||||
|
{
|
||||||
|
key: 'documentation',
|
||||||
|
name: 'Documentation',
|
||||||
|
icon: <Book />,
|
||||||
|
title: 'Find answers in the documentation.',
|
||||||
|
url: 'https://signoz.io/docs/',
|
||||||
|
btnText: 'Visit docs',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'github',
|
||||||
|
name: 'Github',
|
||||||
|
icon: <Github />,
|
||||||
|
title: 'Create an issue on GitHub to report bugs or request new features.',
|
||||||
|
url: 'https://github.com/SigNoz/signoz/issues',
|
||||||
|
btnText: 'Create issue',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'slack_community',
|
||||||
|
name: 'Slack Community',
|
||||||
|
icon: <Slack />,
|
||||||
|
title: 'Get support from the SigNoz community on Slack.',
|
||||||
|
url: 'https://signoz.io/slack',
|
||||||
|
btnText: 'Join Slack',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'chat',
|
||||||
|
name: 'Chat',
|
||||||
|
icon: <MessageSquare />,
|
||||||
|
title: 'Get quick support directly from the team.',
|
||||||
|
url: '',
|
||||||
|
btnText: 'Launch chat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'schedule_call',
|
||||||
|
name: 'Schedule a call',
|
||||||
|
icon: <Calendar />,
|
||||||
|
title: 'Schedule a call with the founders.',
|
||||||
|
url: 'https://calendly.com/pranay-signoz/signoz-intro-calls',
|
||||||
|
btnText: 'Schedule call',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'slack_connect',
|
||||||
|
name: 'Slack Connect',
|
||||||
|
icon: <Cable />,
|
||||||
|
title: 'Get a dedicated support channel for your team.',
|
||||||
|
url: '',
|
||||||
|
btnText: 'Request Slack connect',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function Support(): JSX.Element {
|
||||||
|
const handleChannelWithRedirects = (url: string): void => {
|
||||||
|
window.open(url, '_blank');
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSlackConnectRequest = (): void => {
|
||||||
|
const recipient = 'support@signoz.io';
|
||||||
|
const subject = 'Slack Connect Request';
|
||||||
|
const body = `I'd like to request a dedicated Slack Connect channel for me and my team. Users (emails) to include besides mine:`;
|
||||||
|
|
||||||
|
// Create the mailto link
|
||||||
|
const mailtoLink = `mailto:${recipient}?subject=${encodeURIComponent(
|
||||||
|
subject,
|
||||||
|
)}&body=${encodeURIComponent(body)}`;
|
||||||
|
|
||||||
|
// Open the default email client
|
||||||
|
window.location.href = mailtoLink;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChat = (): void => {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
if (window.Intercom) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
window.Intercom('show');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleChannelClick = (channel: Channel): void => {
|
||||||
|
switch (channel.key) {
|
||||||
|
case channelsMap.documentation:
|
||||||
|
case channelsMap.github:
|
||||||
|
case channelsMap.slack_community:
|
||||||
|
case channelsMap.schedule_call:
|
||||||
|
handleChannelWithRedirects(channel.url);
|
||||||
|
break;
|
||||||
|
case channelsMap.chat:
|
||||||
|
handleChat();
|
||||||
|
break;
|
||||||
|
case channelsMap.slack_connect:
|
||||||
|
handleSlackConnectRequest();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
handleChannelWithRedirects('https://signoz.io/slack');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="support-page-container">
|
||||||
|
<div className="support-page-header">
|
||||||
|
<Title level={3}> Support </Title>
|
||||||
|
<Text style={{ fontSize: 14 }}>
|
||||||
|
We are here to help in case of questions or issues. Pick the channel that
|
||||||
|
is most convenient for you.
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="support-channels">
|
||||||
|
{supportChannels.map(
|
||||||
|
(channel): JSX.Element => (
|
||||||
|
<Card className="support-channel" key={channel.key}>
|
||||||
|
<div className="support-channel-content">
|
||||||
|
<Title ellipsis level={5} className="support-channel-title">
|
||||||
|
{channel.icon}
|
||||||
|
{channel.name}{' '}
|
||||||
|
</Title>
|
||||||
|
<Text> {channel.title} </Text>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="support-channel-action">
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
onClick={(): void => handleChannelClick(channel)}
|
||||||
|
>
|
||||||
|
<Text ellipsis>{channel.btnText} </Text>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
3
frontend/src/pages/Support/index.tsx
Normal file
3
frontend/src/pages/Support/index.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import Support from './Support';
|
||||||
|
|
||||||
|
export default Support;
|
@ -18,6 +18,12 @@ export const isCloudUser = (): boolean => {
|
|||||||
return hostname?.endsWith('signoz.cloud');
|
return hostname?.endsWith('signoz.cloud');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const isEECloudUser = (): boolean => {
|
||||||
|
const { hostname } = window.location;
|
||||||
|
|
||||||
|
return hostname?.endsWith('signoz.io');
|
||||||
|
};
|
||||||
|
|
||||||
export const checkVersionState = (
|
export const checkVersionState = (
|
||||||
currentVersion: string,
|
currentVersion: string,
|
||||||
latestVersion: string,
|
latestVersion: string,
|
||||||
|
@ -81,5 +81,6 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
|||||||
GET_STARTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
GET_STARTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
WORKSPACE_LOCKED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
BILLING: ['ADMIN', 'EDITOR', 'VIEWER'],
|
BILLING: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
|
SUPPORT: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
SOMETHING_WENT_WRONG: ['ADMIN', 'EDITOR', 'VIEWER'],
|
SOMETHING_WENT_WRONG: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||||
};
|
};
|
||||||
|
@ -9882,6 +9882,11 @@ lru-cache@^6.0.0:
|
|||||||
dependencies:
|
dependencies:
|
||||||
yallist "^4.0.0"
|
yallist "^4.0.0"
|
||||||
|
|
||||||
|
lucide-react@0.288.0:
|
||||||
|
version "0.288.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/lucide-react/-/lucide-react-0.288.0.tgz#cc9fda209fe4ec6e572efca38f7d3e3cde7422eb"
|
||||||
|
integrity sha512-ikhb/9LOkq9orPoLV9lLC4UYyoXQycBhIgH7H59ahOkk0mkcAqkD52m84RXedE/qVqZHW8rEJquInT4xGmsNqw==
|
||||||
|
|
||||||
lz-string@^1.4.4:
|
lz-string@^1.4.4:
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user