mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 04:55:55 +08:00
feat: update sidebar and base theme styles (#4272)
* feat: update sidebar and base theme styles * feat: update sidebar items and styles * feat: wire up logs navigation and update user settings page * feat: update styles to handle light mode, add full view header * feat: update onboarding header and styles * feat: remove unused routes * feat: handle sidebar collapse * feat: show pointer on logo hover * feat: fix logs module navigations * feat: update logo click route * feat: update entity name color to primary in application and dashboard tables * feat: update sidebar item styles * feat: update collapse icon and styles * fix: name not updated in menu on change * fix: show invite members nav item * fix: open invite members modal on invite team member nav item click
This commit is contained in:
parent
bdd7778e58
commit
7d960b79dd
@ -36,6 +36,7 @@
|
||||
"@mdx-js/loader": "2.3.0",
|
||||
"@mdx-js/react": "2.3.0",
|
||||
"@monaco-editor/react": "^4.3.1",
|
||||
"@signozhq/design-tokens": "0.0.6",
|
||||
"@uiw/react-md-editor": "3.23.5",
|
||||
"@xstate/react": "^3.0.0",
|
||||
"ansi-to-html": "0.7.2",
|
||||
|
11
frontend/public/Logos/signoz-brand-logo.svg
Normal file
11
frontend/public/Logos/signoz-brand-logo.svg
Normal file
@ -0,0 +1,11 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_2048_2251)">
|
||||
<path opacity="0.9" d="M8.02226 15.9866C3.56539 15.9866 -6.10352e-05 12.4896 -6.10352e-05 8.11832C-6.10352e-05 3.79075 3.56539 0.25 8.02226 0.25H13.0584C14.7075 0.25 15.9999 1.56139 15.9999 3.13506V8.11832C15.9999 12.4896 12.4345 15.9866 8.02226 15.9866Z" fill="#F25733"/>
|
||||
<path d="M7.95919 4.71207C4.63025 4.71207 2.75514 7.46868 2.67693 7.58603C2.48413 7.87508 2.48413 8.24888 2.67707 8.53816C2.75514 8.65528 4.63025 11.4119 7.95919 11.4119C11.2881 11.4119 13.1633 8.65528 13.2414 8.53792C13.4342 8.24888 13.4342 7.87508 13.2413 7.58582C13.1632 7.46868 11.2881 4.71207 7.95919 4.71207ZM3.13771 8.23088C3.06925 8.12832 3.06925 7.99571 3.13771 7.89307C3.20059 7.79867 4.53564 5.83764 6.92256 5.36723C5.84092 5.78476 5.07127 6.83485 5.07127 8.062C5.07127 9.28912 5.84092 10.3392 6.92256 10.7567C4.53564 10.2863 3.20059 8.32528 3.13771 8.23088ZM6.62838 8.062C6.62838 8.21488 6.50443 8.3388 6.35151 8.3388C6.19859 8.3388 6.07465 8.21488 6.07465 8.062C6.07465 7.02287 6.92003 6.17748 7.95916 6.17748C8.11207 6.17748 8.23599 6.30141 8.23599 6.45434C8.23599 6.60727 8.11207 6.73119 7.95916 6.73119C7.22535 6.73119 6.62838 7.32815 6.62838 8.062ZM7.95919 8.73504C7.58803 8.73504 7.2861 8.43312 7.2861 8.062C7.2861 7.69085 7.58803 7.3889 7.95919 7.3889C8.33039 7.3889 8.63231 7.69083 8.63231 8.062C8.63231 8.43312 8.33039 8.73504 7.95919 8.73504ZM12.7806 8.23088C12.7178 8.32528 11.3827 10.2863 8.99583 10.7567C10.0775 10.3392 10.8471 9.28912 10.8471 8.062C10.8471 6.83487 10.0775 5.78477 8.99583 5.36724C11.3827 5.83768 12.7178 7.7987 12.7806 7.89307C12.8491 7.99571 12.8491 8.12832 12.7806 8.23088Z" fill="#F9F2F9"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_2048_2251">
|
||||
<rect width="16" height="16" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
After Width: | Height: | Size: 1.8 KiB |
@ -31,6 +31,7 @@
|
||||
"NOT_FOUND": "SigNoz | Page Not Found",
|
||||
"LOGS": "SigNoz | Logs",
|
||||
"LOGS_EXPLORER": "SigNoz | Logs Explorer",
|
||||
"OLD_LOGS_EXPLORER": "SigNoz | Old Logs Explorer",
|
||||
"LIVE_LOGS": "SigNoz | Live Logs",
|
||||
"LOGS_PIPELINES": "SigNoz | Logs Pipelines",
|
||||
"HOME_PAGE": "Open source Observability Platform | SigNoz",
|
||||
|
@ -112,17 +112,25 @@ export const MySettings = Loadable(
|
||||
);
|
||||
|
||||
export const Logs = Loadable(
|
||||
() => import(/* webpackChunkName: "Logs" */ 'pages/Logs'),
|
||||
() => import(/* webpackChunkName: "Logs" */ 'pages/LogsModulePage'),
|
||||
);
|
||||
|
||||
export const LogsExplorer = Loadable(
|
||||
() => import(/* webpackChunkName: "Logs Explorer" */ 'pages/LogsExplorer'),
|
||||
() => import(/* webpackChunkName: "Logs Explorer" */ 'pages/LogsModulePage'),
|
||||
);
|
||||
|
||||
export const OldLogsExplorer = Loadable(
|
||||
() => import(/* webpackChunkName: "Logs Explorer" */ 'pages/Logs'),
|
||||
);
|
||||
|
||||
export const LiveLogs = Loadable(
|
||||
() => import(/* webpackChunkName: "Live Logs" */ 'pages/LiveLogs'),
|
||||
);
|
||||
|
||||
export const PipelinePage = Loadable(
|
||||
() => import(/* webpackChunkName: "Pipelines" */ 'pages/LogsModulePage'),
|
||||
);
|
||||
|
||||
export const Login = Loadable(
|
||||
() => import(/* webpackChunkName: "Login" */ 'pages/Login'),
|
||||
);
|
||||
@ -151,10 +159,6 @@ export const LogsIndexToFields = Loadable(
|
||||
import(/* webpackChunkName: "LogsIndexToFields Page" */ 'pages/LogsSettings'),
|
||||
);
|
||||
|
||||
export const PipelinePage = Loadable(
|
||||
() => import(/* webpackChunkName: "Pipelines" */ 'pages/Pipelines'),
|
||||
);
|
||||
|
||||
export const BillingPage = Loadable(
|
||||
() => import(/* webpackChunkName: "BillingPage" */ 'pages/Billing'),
|
||||
);
|
||||
|
@ -23,6 +23,7 @@ import {
|
||||
LogsIndexToFields,
|
||||
MySettings,
|
||||
NewDashboardPage,
|
||||
OldLogsExplorer,
|
||||
Onboarding,
|
||||
OrganizationSettings,
|
||||
PasswordReset,
|
||||
@ -246,6 +247,13 @@ const routes: AppRoutes[] = [
|
||||
key: 'LOGS_EXPLORER',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.OLD_LOGS_EXPLORER,
|
||||
exact: true,
|
||||
component: OldLogsExplorer,
|
||||
key: 'OLD_LOGS_EXPLORER',
|
||||
isPrivate: true,
|
||||
},
|
||||
{
|
||||
path: ROUTES.LIVE_LOGS,
|
||||
exact: true,
|
||||
|
@ -48,8 +48,9 @@ export const RawLogContent = styled.div<RawLogContentProps>`
|
||||
line-clamp: ${linesPerRow};
|
||||
-webkit-box-orient: vertical;`};
|
||||
|
||||
font-size: 1rem;
|
||||
line-height: 2rem;
|
||||
font-size: 12px;
|
||||
line-height: 24px;
|
||||
padding: 4px;
|
||||
|
||||
cursor: ${({ $isActiveLog, $isReadOnly }): string =>
|
||||
$isActiveLog || $isReadOnly ? 'initial' : 'pointer'};
|
||||
|
@ -6,7 +6,6 @@ const ROUTES = {
|
||||
TRACE: '/trace',
|
||||
TRACE_DETAIL: '/trace/:id',
|
||||
TRACES_EXPLORER: '/traces-explorer',
|
||||
SETTINGS: '/settings',
|
||||
GET_STARTED: '/get-started',
|
||||
USAGE_EXPLORER: '/usage-explorer',
|
||||
APPLICATION: '/services',
|
||||
@ -23,15 +22,18 @@ const ROUTES = {
|
||||
ERROR_DETAIL: '/error-detail',
|
||||
VERSION: '/status',
|
||||
MY_SETTINGS: '/my-settings',
|
||||
SETTINGS: '/settings',
|
||||
ORG_SETTINGS: '/settings/org-settings',
|
||||
INGESTION_SETTINGS: '/settings/ingestion-settings',
|
||||
SOMETHING_WENT_WRONG: '/something-went-wrong',
|
||||
UN_AUTHORIZED: '/un-authorized',
|
||||
NOT_FOUND: '/not-found',
|
||||
LOGS: '/logs',
|
||||
LOGS_EXPLORER: '/logs-explorer',
|
||||
LIVE_LOGS: '/logs-explorer/live',
|
||||
LOGS_PIPELINES: '/pipelines',
|
||||
LOGS_BASE: '/logs',
|
||||
LOGS: '/logs/logs-explorer',
|
||||
OLD_LOGS_EXPLORER: '/logs/old-logs-explorer',
|
||||
LOGS_EXPLORER: '/logs/logs-explorer',
|
||||
LIVE_LOGS: '/logs/logs-explorer/live',
|
||||
LOGS_PIPELINES: '/logs/pipelines',
|
||||
HOME_PAGE: '/',
|
||||
PASSWORD_RESET: '/password-reset',
|
||||
LIST_LICENSES: '/licenses',
|
||||
|
@ -1,5 +1,6 @@
|
||||
const themeColors = {
|
||||
chartcolors: {
|
||||
robin: '#3F5ECC',
|
||||
dodgerBlue: '#2F80ED',
|
||||
mediumOrchid: '#BB6BD9',
|
||||
seaBuckthorn: '#F2994A',
|
||||
|
@ -15,6 +15,7 @@ export const ButtonContainer = styled.div`
|
||||
align-items: center;
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
`;
|
||||
|
||||
|
53
frontend/src/container/AppLayout/AppLayout.styles.scss
Normal file
53
frontend/src/container/AppLayout/AppLayout.styles.scss
Normal file
@ -0,0 +1,53 @@
|
||||
@import '@signozhq/design-tokens';
|
||||
|
||||
.app-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.app-content {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
.isDarkMode {
|
||||
.app-layout {
|
||||
.app-content {
|
||||
background: #0b0c0e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.isLightMode {
|
||||
.app-layout {
|
||||
.app-content {
|
||||
background: #ffffff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.trial-expiry-banner {
|
||||
padding: 8px;
|
||||
background-color: #f25733;
|
||||
color: white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.upgrade-link {
|
||||
padding: 0px;
|
||||
padding-right: 4px;
|
||||
display: inline !important;
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: white;
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 2px;
|
||||
|
||||
&:hover {
|
||||
color: white;
|
||||
text-decoration: underline;
|
||||
text-decoration-color: white;
|
||||
text-decoration-thickness: 2px;
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
}
|
@ -1,13 +1,22 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable jsx-a11y/anchor-is-valid */
|
||||
import './AppLayout.styles.scss';
|
||||
|
||||
import { Flex } from 'antd';
|
||||
import getDynamicConfigs from 'api/dynamicConfigs/getDynamicConfigs';
|
||||
import getUserLatestVersion from 'api/user/getLatestVersion';
|
||||
import getUserVersion from 'api/user/getVersion';
|
||||
import cx from 'classnames';
|
||||
import ROUTES from 'constants/routes';
|
||||
import Header from 'container/Header';
|
||||
import SideNav from 'container/SideNav';
|
||||
import TopNav from 'container/TopNav';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useLicense from 'hooks/useLicense';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||
import { ReactNode, useEffect, useMemo, useRef } from 'react';
|
||||
import { ReactNode, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { Helmet } from 'react-helmet-async';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -25,15 +34,20 @@ import {
|
||||
UPDATE_LATEST_VERSION_ERROR,
|
||||
} from 'types/actions/app';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { getFormattedDate, getRemainingDays } from 'utils/timeUtils';
|
||||
|
||||
import { ChildrenContainer, Layout, LayoutContent } from './styles';
|
||||
import { getRouteKey } from './utils';
|
||||
|
||||
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
const { isLoggedIn, user } = useSelector<AppState, AppReducer>(
|
||||
const { isLoggedIn, user, role } = useSelector<AppState, AppReducer>(
|
||||
(state) => state.app,
|
||||
);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
|
||||
const { data: licenseData, isFetching } = useLicense();
|
||||
|
||||
const { pathname } = useLocation();
|
||||
const { t } = useTranslation(['titles']);
|
||||
|
||||
@ -196,16 +210,58 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
const renderFullScreen =
|
||||
pathname === ROUTES.GET_STARTED || pathname === ROUTES.WORKSPACE_LOCKED;
|
||||
|
||||
const [showTrialExpiryBanner, setShowTrialExpiryBanner] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isFetching &&
|
||||
licenseData?.payload?.onTrial &&
|
||||
!licenseData?.payload?.trialConvertedToSubscription &&
|
||||
!licenseData?.payload?.workSpaceBlock &&
|
||||
getRemainingDays(licenseData?.payload.trialEnd) < 7
|
||||
) {
|
||||
setShowTrialExpiryBanner(true);
|
||||
}
|
||||
}, [licenseData, isFetching]);
|
||||
|
||||
const handleUpgrade = (): void => {
|
||||
if (role === 'ADMIN') {
|
||||
history.push(ROUTES.BILLING);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout className={isDarkMode ? 'darkMode' : 'lightMode'}>
|
||||
<Helmet>
|
||||
<title>{pageTitle}</title>
|
||||
</Helmet>
|
||||
|
||||
{isToDisplayLayout && <Header />}
|
||||
<Layout>
|
||||
{isToDisplayLayout && !renderFullScreen && <SideNav />}
|
||||
{showTrialExpiryBanner && (
|
||||
<div className="trial-expiry-banner">
|
||||
You are in free trial period. Your free trial will end on{' '}
|
||||
<span>
|
||||
{getFormattedDate(licenseData?.payload?.trialEnd || Date.now())}.
|
||||
</span>
|
||||
{role === 'ADMIN' ? (
|
||||
<span>
|
||||
{' '}
|
||||
Please{' '}
|
||||
<a className="upgrade-link" onClick={handleUpgrade}>
|
||||
upgrade
|
||||
</a>
|
||||
to continue using SigNoz features.
|
||||
</span>
|
||||
) : (
|
||||
'Please contact your administrator for upgrading to a paid plan.'
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Flex className={cx('app-layout', isDarkMode ? 'darkMode' : 'lightMode')}>
|
||||
{isToDisplayLayout && !renderFullScreen && (
|
||||
<SideNav licenseData={licenseData} isFetching={isFetching} />
|
||||
)}
|
||||
<div className="app-content">
|
||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||
<LayoutContent>
|
||||
<ChildrenContainer>
|
||||
@ -214,7 +270,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
</ChildrenContainer>
|
||||
</LayoutContent>
|
||||
</ErrorBoundary>
|
||||
</Layout>
|
||||
</div>
|
||||
</Flex>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
|
@ -13,6 +13,7 @@ export const Layout = styled(LayoutComponent)`
|
||||
|
||||
export const LayoutContent = styled(LayoutComponent.Content)`
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
export const ChildrenContainer = styled.div`
|
||||
|
@ -0,0 +1,37 @@
|
||||
.full-view-header-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 24px 0;
|
||||
|
||||
.brand-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 32px;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
.brand-logo-name {
|
||||
font-family: 'Work Sans', sans-serif;
|
||||
font-size: 24px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.brand-logo {
|
||||
.brand-logo-name {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
28
frontend/src/container/FullViewHeader/FullViewHeader.tsx
Normal file
28
frontend/src/container/FullViewHeader/FullViewHeader.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import './FullViewHeader.styles.scss';
|
||||
|
||||
import history from 'lib/history';
|
||||
|
||||
export default function FullViewHeader({
|
||||
overrideRoute,
|
||||
}: {
|
||||
overrideRoute?: string;
|
||||
}): React.ReactElement {
|
||||
const handleLogoClick = (): void => {
|
||||
history.push(overrideRoute || '/');
|
||||
};
|
||||
return (
|
||||
<div className="full-view-header-container">
|
||||
<div className="brand-logo" onClick={handleLogoClick}>
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
|
||||
<div className="brand-logo-name">SigNoz</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
FullViewHeader.defaultProps = {
|
||||
overrideRoute: '/',
|
||||
};
|
@ -332,7 +332,7 @@ function DashboardsList(): JSX.Element {
|
||||
);
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Card style={{ margin: '16px 0' }}>
|
||||
{GetHeader}
|
||||
|
||||
<TableContainer>
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { blue } from '@ant-design/colors';
|
||||
import { Typography } from 'antd';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const TableLinkText = styled(Typography.Text)`
|
||||
color: ${blue.primary} !important;
|
||||
color: #4e74f8 !important;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { Button, ButtonProps } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import styled, { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||
|
||||
export const LiveButtonStyled = styled(Button)<ButtonProps>`
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 0.9);
|
||||
background-color: #1eb475;
|
||||
|
||||
${({ danger }): FlattenSimpleInterpolation =>
|
||||
!danger
|
||||
? css`
|
||||
&:hover {
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 1) !important;
|
||||
background-color: #1eb475 !important;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 0.7) !important;
|
||||
background-color: #1eb475 !important;
|
||||
}
|
||||
`
|
||||
: css``}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { Col, Row, Space } from 'antd';
|
||||
import { Col, Row, Space, Typography } from 'antd';
|
||||
import ROUTES from 'constants/routes';
|
||||
import NewExplorerCTA from 'container/NewExplorerCTA';
|
||||
import { FileText } from 'lucide-react';
|
||||
import { useLocation } from 'react-use';
|
||||
|
||||
import ShowBreadcrumbs from '../TopNav/Breadcrumbs';
|
||||
import DateTimeSelector from '../TopNav/DateTimeSelection';
|
||||
import { Container } from './styles';
|
||||
import { LocalTopNavProps } from './types';
|
||||
@ -10,13 +12,25 @@ function LocalTopNav({
|
||||
actions,
|
||||
renderPermissions,
|
||||
}: LocalTopNavProps): JSX.Element | null {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const isLiveLogsPage = pathname === ROUTES.LIVE_LOGS;
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{isLiveLogsPage && (
|
||||
<Col span={16}>
|
||||
<ShowBreadcrumbs />
|
||||
</Col>
|
||||
<Space>
|
||||
<FileText color="#fff" size={16} />
|
||||
|
||||
<Col span={8}>
|
||||
<Typography.Title level={4} style={{ marginTop: 0, marginBottom: 0 }}>
|
||||
Live Logs
|
||||
</Typography.Title>
|
||||
</Space>
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Col span={isLiveLogsPage ? 8 : 24}>
|
||||
<Row justify="end">
|
||||
<Space align="start" size={30} direction="horizontal">
|
||||
<NewExplorerCTA />
|
||||
|
@ -3,7 +3,7 @@ import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Row)`
|
||||
&&& {
|
||||
margin-top: 2rem;
|
||||
margin-top: 1rem;
|
||||
min-height: 8vh;
|
||||
}
|
||||
`;
|
||||
|
@ -63,7 +63,7 @@ function LogDetailedView({
|
||||
queryString,
|
||||
);
|
||||
|
||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||
history.replace(`${ROUTES.OLD_LOGS_EXPLORER}?q=${updatedQueryString}`);
|
||||
},
|
||||
[history, queryString],
|
||||
);
|
||||
|
@ -74,6 +74,7 @@ function LogsTopNav(): JSX.Element {
|
||||
icon={<PlayCircleFilled />}
|
||||
onClick={handleGoLive}
|
||||
type="primary"
|
||||
size="small"
|
||||
>
|
||||
Go Live
|
||||
</LiveButtonStyled>
|
||||
|
@ -1,19 +1,18 @@
|
||||
import { Button, ButtonProps } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import styled, { css, FlattenSimpleInterpolation } from 'styled-components';
|
||||
|
||||
export const LiveButtonStyled = styled(Button)<ButtonProps>`
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 0.9);
|
||||
background-color: #1eb475;
|
||||
|
||||
${({ danger }): FlattenSimpleInterpolation =>
|
||||
!danger
|
||||
? css`
|
||||
&:hover {
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 1) !important;
|
||||
background-color: #1eb475 !important;
|
||||
}
|
||||
|
||||
&:active {
|
||||
background-color: rgba(${themeColors.buttonSuccessRgb}, 0.7) !important;
|
||||
background-color: #1eb475 !important;
|
||||
}
|
||||
`
|
||||
: css``}
|
||||
|
5
frontend/src/container/MySettings/MySettings.styles.scss
Normal file
5
frontend/src/container/MySettings/MySettings.styles.scss
Normal file
@ -0,0 +1,5 @@
|
||||
.flexBtn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
import { Button, Space, Typography } from 'antd';
|
||||
import { Button, Card, Space, Typography } from 'antd';
|
||||
import changeMyPassword from 'api/user/changeMyPassword';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { Save } from 'lucide-react';
|
||||
import { isPasswordNotValidMessage, isPasswordValid } from 'pages/SignUp/utils';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
@ -20,9 +21,7 @@ function PasswordContainer(): JSX.Element {
|
||||
false,
|
||||
);
|
||||
|
||||
const defaultPlaceHolder = t('input_password', {
|
||||
ns: 'settings',
|
||||
});
|
||||
const defaultPlaceHolder = '*************';
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
@ -89,8 +88,9 @@ function PasswordContainer(): JSX.Element {
|
||||
currentPassword === updatePassword;
|
||||
|
||||
return (
|
||||
<Space direction="vertical" size="large">
|
||||
<Typography.Title level={3}>
|
||||
<Card>
|
||||
<Space direction="vertical" size="small">
|
||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
||||
{t('change_password', {
|
||||
ns: 'settings',
|
||||
})}
|
||||
@ -144,11 +144,13 @@ function PasswordContainer(): JSX.Element {
|
||||
onClick={onChangePasswordClickHandler}
|
||||
type="primary"
|
||||
>
|
||||
<Save size={12} style={{ marginRight: '8px' }} />{' '}
|
||||
{t('change_password', {
|
||||
ns: 'settings',
|
||||
})}
|
||||
</Button>
|
||||
</Space>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,7 @@
|
||||
.userInfo-label {
|
||||
min-width: 150px;
|
||||
}
|
||||
|
||||
.userInfo-value {
|
||||
min-width: 20rem;
|
||||
}
|
@ -1,6 +1,10 @@
|
||||
import { Button, Space, Typography } from 'antd';
|
||||
import '../MySettings.styles.scss';
|
||||
import './UserInfo.styles.scss';
|
||||
|
||||
import { Button, Card, Flex, Input, Space, Typography } from 'antd';
|
||||
import editUser from 'api/user/editUser';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { PencilIcon, UserSquare } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
@ -12,7 +16,7 @@ import AppReducer from 'types/reducer/app';
|
||||
|
||||
import { NameInput } from '../styles';
|
||||
|
||||
function UpdateName(): JSX.Element {
|
||||
function UserInfo(): JSX.Element {
|
||||
const { user, role, org, userFlags } = useSelector<AppState, AppReducer>(
|
||||
(state) => state.app,
|
||||
);
|
||||
@ -72,9 +76,18 @@ function UpdateName(): JSX.Element {
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Card>
|
||||
<Space direction="vertical" size="middle">
|
||||
<Typography>Name</Typography>
|
||||
<Flex gap={8}>
|
||||
<UserSquare />{' '}
|
||||
<Typography.Title level={4} style={{ marginTop: 0 }}>
|
||||
User Details
|
||||
</Typography.Title>
|
||||
</Flex>
|
||||
|
||||
<Flex gap={16}>
|
||||
<Space>
|
||||
<Typography className="userInfo-label">Name</Typography>
|
||||
<NameInput
|
||||
placeholder="Your Name"
|
||||
onChange={(event): void => {
|
||||
@ -83,17 +96,31 @@ function UpdateName(): JSX.Element {
|
||||
value={changedName}
|
||||
disabled={loading}
|
||||
/>
|
||||
</Space>
|
||||
|
||||
<Button
|
||||
className="flexBtn"
|
||||
loading={loading}
|
||||
disabled={loading}
|
||||
onClick={onClickUpdateHandler}
|
||||
type="primary"
|
||||
>
|
||||
Update Name
|
||||
<PencilIcon size={12} /> Update
|
||||
</Button>
|
||||
</Flex>
|
||||
|
||||
<Space>
|
||||
<Typography className="userInfo-label"> Email </Typography>
|
||||
<Input className="userInfo-value" value={user.email} disabled />
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
<Space>
|
||||
<Typography className="userInfo-label"> Role </Typography>
|
||||
<Input className="userInfo-value" value={role || ''} disabled />
|
||||
</Space>
|
||||
</Space>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
export default UpdateName;
|
||||
export default UserInfo;
|
@ -1,16 +1,28 @@
|
||||
import { Space, Typography } from 'antd';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import './MySettings.styles.scss';
|
||||
|
||||
import { Button, Space } from 'antd';
|
||||
import { Logout } from 'api/utils';
|
||||
import { LogOut } from 'lucide-react';
|
||||
|
||||
import Password from './Password';
|
||||
import UpdateName from './UpdateName';
|
||||
import UserInfo from './UserInfo';
|
||||
|
||||
function MySettings(): JSX.Element {
|
||||
const { t } = useTranslation(['routes']);
|
||||
return (
|
||||
<Space direction="vertical" size="large">
|
||||
<Typography.Title level={2}>{t('my_settings')}</Typography.Title>
|
||||
<UpdateName />
|
||||
<Space
|
||||
direction="vertical"
|
||||
size="large"
|
||||
style={{
|
||||
margin: '16px 0',
|
||||
}}
|
||||
>
|
||||
<UserInfo />
|
||||
|
||||
<Password />
|
||||
|
||||
<Button className="flexBtn" onClick={(): void => Logout()} type="primary">
|
||||
<LogOut size={12} /> Logout
|
||||
</Button>
|
||||
</Space>
|
||||
);
|
||||
}
|
||||
|
@ -7,5 +7,5 @@ export const RIBBON_STYLES = {
|
||||
export const buttonText = {
|
||||
[ROUTES.LOGS_EXPLORER]: 'Switch to Old Logs Explorer',
|
||||
[ROUTES.TRACE]: 'Try new Traces Explorer',
|
||||
[ROUTES.LOGS]: 'Switch to New Logs Explorer',
|
||||
[ROUTES.OLD_LOGS_EXPLORER]: 'Switch to New Logs Explorer',
|
||||
};
|
||||
|
@ -14,16 +14,16 @@ function NewExplorerCTA(): JSX.Element | null {
|
||||
() =>
|
||||
location.pathname === ROUTES.LOGS_EXPLORER ||
|
||||
location.pathname === ROUTES.TRACE ||
|
||||
location.pathname === ROUTES.LOGS,
|
||||
location.pathname === ROUTES.OLD_LOGS_EXPLORER,
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const onClickHandler = useCallback((): void => {
|
||||
if (location.pathname === ROUTES.LOGS_EXPLORER) {
|
||||
history.push(ROUTES.LOGS);
|
||||
history.push(ROUTES.OLD_LOGS_EXPLORER);
|
||||
} else if (location.pathname === ROUTES.TRACE) {
|
||||
history.push(ROUTES.TRACES_EXPLORER);
|
||||
} else if (location.pathname === ROUTES.LOGS) {
|
||||
} else if (location.pathname === ROUTES.OLD_LOGS_EXPLORER) {
|
||||
history.push(ROUTES.LOGS_EXPLORER);
|
||||
}
|
||||
}, [location.pathname]);
|
||||
@ -36,6 +36,7 @@ function NewExplorerCTA(): JSX.Element | null {
|
||||
danger
|
||||
data-testid="newExplorerCTA"
|
||||
type="primary"
|
||||
size="small"
|
||||
>
|
||||
{buttonText[location.pathname]}
|
||||
</Button>
|
||||
|
@ -32,12 +32,12 @@
|
||||
.onboardingHeader {
|
||||
text-align: center;
|
||||
margin-top: 48px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.onboardingHeader h1 {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.modulesContainer {
|
||||
|
@ -6,6 +6,7 @@ import { ArrowRightOutlined } from '@ant-design/icons';
|
||||
import { Button, Card, Typography } from 'antd';
|
||||
import getIngestionData from 'api/settings/getIngestionData';
|
||||
import cx from 'classnames';
|
||||
import FullViewHeader from 'container/FullViewHeader/FullViewHeader';
|
||||
import useAnalytics from 'hooks/analytics/useAnalytics';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useEffect, useState } from 'react';
|
||||
@ -218,11 +219,10 @@ export default function Onboarding(): JSX.Element {
|
||||
<div className={cx('container', isDarkMode ? 'darkMode' : 'lightMode')}>
|
||||
{activeStep === 1 && (
|
||||
<>
|
||||
<FullViewHeader />
|
||||
<div className="onboardingHeader">
|
||||
<h1>Get Started with SigNoz</h1>
|
||||
<div> Select a use-case to get started </div>
|
||||
<h1> Select a use-case to get started</h1>
|
||||
</div>
|
||||
|
||||
<div className="modulesContainer">
|
||||
<div className="moduleContainerRowStyles">
|
||||
{Object.keys(ModulesMap).map((module) => {
|
||||
@ -261,7 +261,6 @@ export default function Onboarding(): JSX.Element {
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="continue-to-next-step">
|
||||
<Button type="primary" icon={<ArrowRightOutlined />} onClick={handleNext}>
|
||||
Get Started
|
||||
|
@ -39,6 +39,36 @@
|
||||
.steps-container {
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
|
||||
.steps-container-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
margin-bottom: 24px;
|
||||
|
||||
.brand-logo {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
.brand-logo-name {
|
||||
font-family: 'Work Sans', sans-serif;
|
||||
font-size: 18px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.selected-step-content {
|
||||
@ -153,3 +183,18 @@
|
||||
.error-container {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.steps-container {
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
|
||||
.steps-container-header {
|
||||
.brand-logo {
|
||||
.brand-logo-name {
|
||||
color: black;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,6 @@
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
/* eslint-disable react/jsx-no-comment-textnodes */
|
||||
/* eslint-disable sonarjs/prefer-single-boolean-return */
|
||||
import './ModuleStepsContainer.styles.scss';
|
||||
|
||||
@ -135,7 +138,7 @@ export default function ModuleStepsContainer({
|
||||
if (selectedModule.id === ModulesMap.APM) {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
} else if (selectedModule.id === ModulesMap.LogsManagement) {
|
||||
history.push(ROUTES.LOGS);
|
||||
history.push(ROUTES.LOGS_EXPLORER);
|
||||
} else if (selectedModule.id === ModulesMap.InfrastructureMonitoring) {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
}
|
||||
@ -197,9 +200,21 @@ export default function ModuleStepsContainer({
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogoClick = (): void => {
|
||||
history.push('/');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="onboarding-module-steps">
|
||||
<div className="steps-container">
|
||||
<div className="steps-container-header">
|
||||
<div className="brand-logo" onClick={handleLogoClick}>
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
|
||||
<div className="brand-logo-name">SigNoz</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Space style={{ marginBottom: '24px' }}>
|
||||
<Button
|
||||
style={{ display: 'flex', alignItems: 'center' }}
|
||||
|
@ -14,11 +14,11 @@ function OptionRenderer({
|
||||
const optionType = getOptionType(label);
|
||||
|
||||
return (
|
||||
<span>
|
||||
<span className="option">
|
||||
{optionType ? (
|
||||
<SelectOptionContainer>
|
||||
<div>{value}</div>
|
||||
<div>
|
||||
<div className="option-value">{value}</div>
|
||||
<div className="option-meta-data-container">
|
||||
<TagContainer>
|
||||
<TagLabel>Type: </TagLabel>
|
||||
<TagValue>{optionType}</TagValue>
|
||||
|
@ -18,17 +18,18 @@ export const StyledCheckOutlined = styled(CheckOutlined)`
|
||||
|
||||
export const SelectOptionContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
`;
|
||||
|
||||
export const TagContainer = styled(Tag)`
|
||||
&&& {
|
||||
border-radius: 0.25rem;
|
||||
padding: 0.063rem 0.5rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.25rem;
|
||||
border-radius: 3px;
|
||||
padding: 0.3rem 0.3rem;
|
||||
font-weight: 400;
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { Typography } from 'antd';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export const Container = styled.div`
|
||||
@ -9,7 +8,7 @@ export const Container = styled.div`
|
||||
export const Name = styled(Typography)`
|
||||
&&& {
|
||||
font-weight: 600;
|
||||
color: ${themeColors.lightBlue};
|
||||
color: #4e74f8;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
@ -8,7 +8,7 @@ export const Container = styled.div`
|
||||
export const Name = styled(Typography)`
|
||||
&&& {
|
||||
font-weight: 600;
|
||||
color: #177ddc;
|
||||
color: #4e74f8;
|
||||
cursor: pointer;
|
||||
}
|
||||
`;
|
||||
|
112
frontend/src/container/SideNav/NavItem/NavItem.styles.scss
Normal file
112
frontend/src/container/SideNav/NavItem/NavItem.styles.scss
Normal file
@ -0,0 +1,112 @@
|
||||
.nav-item {
|
||||
border-radius: 2px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
height: 36px;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&.active {
|
||||
.nav-item-active-marker {
|
||||
background: #3f5ecc;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.nav-item-data {
|
||||
color: white;
|
||||
background: #121317;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.nav-item-data {
|
||||
color: white;
|
||||
background: #121317;
|
||||
// color: #3f5ecc;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item-active-marker {
|
||||
margin: 8px 0;
|
||||
width: 8px;
|
||||
height: 24px;
|
||||
background: transparent;
|
||||
border-radius: 3px;
|
||||
margin-left: -5px;
|
||||
}
|
||||
|
||||
.nav-item-data {
|
||||
flex-grow: 1;
|
||||
|
||||
max-width: calc(100% - 24px);
|
||||
|
||||
display: flex;
|
||||
margin: 0px 8px;
|
||||
padding: 4px 12px;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
align-self: stretch;
|
||||
color: #c0c1c3;
|
||||
|
||||
border-radius: 3px;
|
||||
font-family: Inter;
|
||||
font-size: 13px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 18px;
|
||||
|
||||
background: transparent;
|
||||
border-left: 2px solid transparent;
|
||||
|
||||
transition: 0.2s all linear;
|
||||
|
||||
.nav-item-icon {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.nav-item-label {
|
||||
// width: 220px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.nav-item {
|
||||
&.active {
|
||||
.nav-item-active-marker {
|
||||
background: #3f5ecc;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
.nav-item-data {
|
||||
color: #121317;
|
||||
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
|
||||
&.active {
|
||||
.nav-item-data {
|
||||
color: #121317;
|
||||
background: white;
|
||||
// color: #4e74f8;
|
||||
}
|
||||
}
|
||||
|
||||
.nav-item-data {
|
||||
color: #121317;
|
||||
}
|
||||
}
|
||||
}
|
31
frontend/src/container/SideNav/NavItem/NavItem.tsx
Normal file
31
frontend/src/container/SideNav/NavItem/NavItem.tsx
Normal file
@ -0,0 +1,31 @@
|
||||
import './NavItem.styles.scss';
|
||||
|
||||
import cx from 'classnames';
|
||||
|
||||
import { SidebarItem } from '../sideNav.types';
|
||||
|
||||
export default function NavItem({
|
||||
isCollapsed,
|
||||
item,
|
||||
isActive,
|
||||
onClick,
|
||||
}: {
|
||||
isCollapsed: boolean;
|
||||
item: SidebarItem;
|
||||
isActive: boolean;
|
||||
onClick: () => void;
|
||||
}): JSX.Element {
|
||||
const { label, icon } = item;
|
||||
|
||||
return (
|
||||
// eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions
|
||||
<div className={cx('nav-item', isActive ? 'active' : '')} onClick={onClick}>
|
||||
<div className="nav-item-active-marker" />
|
||||
<div className="nav-item-data">
|
||||
<div className="nav-item-icon">{icon}</div>
|
||||
|
||||
{!isCollapsed && <div className="nav-item-label">{label}</div>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
172
frontend/src/container/SideNav/SideNav.styles.scss
Normal file
172
frontend/src/container/SideNav/SideNav.styles.scss
Normal file
@ -0,0 +1,172 @@
|
||||
@import '@signozhq/design-tokens';
|
||||
|
||||
.sideNav {
|
||||
flex: 0 0 240px;
|
||||
max-width: 240px;
|
||||
min-width: 240px;
|
||||
width: 240px;
|
||||
border-right: 1px solid $bg-slate-400;
|
||||
padding-bottom: 48px;
|
||||
transition: all 0.3s, background 0s, border 0s;
|
||||
position: relative;
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: $padding-4;
|
||||
|
||||
.brand-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: $font-size-xl;
|
||||
}
|
||||
|
||||
.brand-logo-name {
|
||||
font-family: 'Work Sans', sans-serif;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 18px;
|
||||
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.license {
|
||||
&.tag {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
color: rgba(255, 255, 255, 0.85);
|
||||
font-size: 8px;
|
||||
font-weight: $font-weight-medium;
|
||||
letter-spacing: 0.6px;
|
||||
padding: 4px 8px;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
background: $bg-slate-400;
|
||||
border: 1px solid $bg-slate-400;
|
||||
border-radius: 20px;
|
||||
opacity: 1;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.get-started-nav-items {
|
||||
display: flex;
|
||||
margin: 4px 13px 4px 10px;
|
||||
|
||||
.get-started-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
margin-left: 2px;
|
||||
gap: 8px;
|
||||
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
|
||||
border: 1px solid $bg-slate-400;
|
||||
|
||||
border-radius: 2px;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-nav-items {
|
||||
border-top: 1px solid $bg-slate-400;
|
||||
padding: 8px 0;
|
||||
max-width: 100%;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 240px;
|
||||
|
||||
transition: all 0.3s, background 0s, border 0s;
|
||||
|
||||
// position: relative;
|
||||
|
||||
.collapse-expand-handlers {
|
||||
position: absolute;
|
||||
|
||||
top: -9px;
|
||||
right: -9px;
|
||||
cursor: pointer;
|
||||
|
||||
display: none;
|
||||
|
||||
transition: display 0.3s;
|
||||
|
||||
svg {
|
||||
fill: $bg-vanilla-300;
|
||||
color: $bg-slate-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapsed {
|
||||
flex: 0 0 64px;
|
||||
max-width: 64px;
|
||||
min-width: 64px;
|
||||
width: 64px;
|
||||
|
||||
.secondary-nav-items {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.brand {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.get-started-nav-items {
|
||||
.get-started-btn {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.collapse-expand-handlers {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.lightMode {
|
||||
.sideNav {
|
||||
background: $bg-vanilla-300;
|
||||
border-right: 1px solid $bg-vanilla-400;
|
||||
|
||||
.get-started-nav-items {
|
||||
.get-started-btn {
|
||||
border: 1px solid $bg-vanilla-400;
|
||||
}
|
||||
}
|
||||
|
||||
.brand {
|
||||
.brand-logo {
|
||||
.brand-logo-name {
|
||||
color: $bg-slate-400;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.secondary-nav-items {
|
||||
border-top: 1px solid $bg-vanilla-400;
|
||||
|
||||
.collapse-expand-handlers {
|
||||
svg {
|
||||
color: $bg-slate-300;
|
||||
fill: $bg-vanilla-300;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,26 @@
|
||||
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
|
||||
import { MenuProps } from 'antd';
|
||||
/* eslint-disable jsx-a11y/no-static-element-interactions */
|
||||
/* eslint-disable jsx-a11y/click-events-have-key-events */
|
||||
import './SideNav.styles.scss';
|
||||
|
||||
import { Button } from 'antd';
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import cx from 'classnames';
|
||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||
import { FeatureKeys } from 'constants/features';
|
||||
import ROUTES from 'constants/routes';
|
||||
import useLicense, { LICENSE_PLAN_KEY } from 'hooks/useLicense';
|
||||
import { ToggleButton } from 'container/Header/styles';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import useThemeMode, { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { LICENSE_PLAN_KEY, LICENSE_PLAN_STATUS } from 'hooks/useLicense';
|
||||
import history from 'lib/history';
|
||||
import { LifeBuoy } from 'lucide-react';
|
||||
import {
|
||||
AlertTriangle,
|
||||
CheckSquare,
|
||||
ChevronLeftCircle,
|
||||
ChevronRightCircle,
|
||||
RocketIcon,
|
||||
UserCircle,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
@ -17,44 +31,82 @@ import {
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { sideBarCollapse } from 'store/actions/app';
|
||||
import { sideBarCollapse } from 'store/actions';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { License } from 'types/api/licenses/def';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { checkVersionState, isCloudUser, isEECloudUser } from 'utils/app';
|
||||
|
||||
import { routeConfig, styles } from './config';
|
||||
import { routeConfig } from './config';
|
||||
import { getQueryString } from './helper';
|
||||
import defaultMenuItems from './menuItems';
|
||||
import { MenuItem, SecondaryMenuItemKey } from './sideNav.types';
|
||||
import defaultMenuItems, {
|
||||
helpSupportMenuItem,
|
||||
inviteMemberMenuItem,
|
||||
manageLicenseMenuItem,
|
||||
slackSupportMenuItem,
|
||||
trySignozCloudMenuItem,
|
||||
} from './menuItems';
|
||||
import NavItem from './NavItem/NavItem';
|
||||
import { SecondaryMenuItemKey } from './sideNav.types';
|
||||
import { getActiveMenuKeyFromPath } from './sideNav.utils';
|
||||
import Slack from './Slack';
|
||||
import {
|
||||
MenuLabelContainer,
|
||||
RedDot,
|
||||
Sider,
|
||||
StyledPrimaryMenu,
|
||||
StyledSecondaryMenu,
|
||||
StyledText,
|
||||
} from './styles';
|
||||
|
||||
function SideNav(): JSX.Element {
|
||||
function SideNav({
|
||||
licenseData,
|
||||
isFetching,
|
||||
}: {
|
||||
licenseData: any;
|
||||
isFetching: boolean;
|
||||
}): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [menuItems, setMenuItems] = useState(defaultMenuItems);
|
||||
const [collapsed, setCollapsed] = useState<boolean>(
|
||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||
);
|
||||
|
||||
const { pathname, search } = useLocation();
|
||||
const {
|
||||
user,
|
||||
role,
|
||||
featureResponse,
|
||||
currentVersion,
|
||||
latestVersion,
|
||||
isCurrentVersionError,
|
||||
featureResponse,
|
||||
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
const { data, isFetching } = useLicense();
|
||||
const userSettingsMenuItem = {
|
||||
key: ROUTES.MY_SETTINGS,
|
||||
label: user?.name || 'User',
|
||||
icon: <UserCircle size={16} />,
|
||||
};
|
||||
|
||||
let secondaryMenuItems: MenuItem[] = [];
|
||||
const [userManagementMenuItems, setUserManagementMenuItems] = useState([
|
||||
manageLicenseMenuItem,
|
||||
]);
|
||||
|
||||
const onClickSlackHandler = (): void => {
|
||||
window.open('https://signoz.io/slack', '_blank');
|
||||
};
|
||||
|
||||
const onClickVersionHandler = (): void => {
|
||||
history.push(ROUTES.VERSION);
|
||||
};
|
||||
|
||||
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
||||
|
||||
const [inviteMembers] = useComponentPermission(['invite_members'], role);
|
||||
|
||||
useEffect(() => {
|
||||
if (inviteMembers) {
|
||||
const updatedUserManagementMenuItems = [
|
||||
inviteMemberMenuItem,
|
||||
manageLicenseMenuItem,
|
||||
];
|
||||
|
||||
setUserManagementMenuItems(updatedUserManagementMenuItems);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [inviteMembers]);
|
||||
|
||||
useEffect((): void => {
|
||||
const isOnboardingEnabled =
|
||||
@ -78,10 +130,10 @@ function SideNav(): JSX.Element {
|
||||
let items = [...menuItems];
|
||||
|
||||
const isOnBasicPlan =
|
||||
data?.payload?.licenses?.some(
|
||||
(license) =>
|
||||
licenseData?.payload?.licenses?.some(
|
||||
(license: License) =>
|
||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||
) || data?.payload?.licenses === null;
|
||||
) || licenseData?.payload?.licenses === null;
|
||||
|
||||
if (role !== USER_ROLES.ADMIN || isOnBasicPlan) {
|
||||
items = items.filter((item) => item.key !== ROUTES.BILLING);
|
||||
@ -90,9 +142,7 @@ function SideNav(): JSX.Element {
|
||||
setMenuItems(items);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [data?.payload?.licenses, isFetching, role]);
|
||||
|
||||
const { pathname, search } = useLocation();
|
||||
}, [licenseData?.payload?.licenses, isFetching, role]);
|
||||
|
||||
const { t } = useTranslation('');
|
||||
|
||||
@ -104,6 +154,26 @@ function SideNav(): JSX.Element {
|
||||
dispatch(sideBarCollapse(collapsed));
|
||||
}, [collapsed, dispatch]);
|
||||
|
||||
const isLicenseActive =
|
||||
licenseData?.payload?.licenses?.find((e: License) => e.isCurrent)?.status ===
|
||||
LICENSE_PLAN_STATUS.VALID;
|
||||
|
||||
const isEnterprise = licenseData?.payload?.licenses?.some(
|
||||
(license: License) =>
|
||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.ENTERPRISE_PLAN,
|
||||
);
|
||||
|
||||
const onClickSignozCloud = (): void => {
|
||||
window.open(
|
||||
'https://signoz.io/oss-to-cloud/?utm_source=product_navbar&utm_medium=frontend&utm_campaign=oss_users',
|
||||
'_blank',
|
||||
);
|
||||
};
|
||||
|
||||
const onClickGetStarted = (): void => {
|
||||
history.push(`/get-started`);
|
||||
};
|
||||
|
||||
const onClickHandler = useCallback(
|
||||
(key: string) => {
|
||||
const params = new URLSearchParams(search);
|
||||
@ -118,80 +188,175 @@ function SideNav(): JSX.Element {
|
||||
[pathname, search],
|
||||
);
|
||||
|
||||
const onClickMenuHandler: MenuProps['onClick'] = (e) => {
|
||||
onClickHandler(e.key);
|
||||
};
|
||||
|
||||
const onClickSlackHandler = (): void => {
|
||||
window.open('https://signoz.io/slack', '_blank');
|
||||
};
|
||||
|
||||
const onClickVersionHandler = (): void => {
|
||||
history.push(ROUTES.VERSION);
|
||||
};
|
||||
|
||||
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
||||
|
||||
if (isCloudUser() || isEECloudUser()) {
|
||||
secondaryMenuItems = [
|
||||
{
|
||||
key: SecondaryMenuItemKey.Support,
|
||||
label: 'Support',
|
||||
icon: <LifeBuoy />,
|
||||
onClick: onClickMenuHandler,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
secondaryMenuItems = [
|
||||
{
|
||||
key: SecondaryMenuItemKey.Version,
|
||||
icon: !isLatestVersion ? (
|
||||
<WarningOutlined style={{ color: '#E87040' }} />
|
||||
) : (
|
||||
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
|
||||
),
|
||||
label: (
|
||||
<MenuLabelContainer>
|
||||
<StyledText ellipsis>
|
||||
{!isCurrentVersionError ? currentVersion : t('n_a')}
|
||||
</StyledText>
|
||||
{!isLatestVersion && <RedDot />}
|
||||
</MenuLabelContainer>
|
||||
),
|
||||
onClick: onClickVersionHandler,
|
||||
},
|
||||
{
|
||||
key: SecondaryMenuItemKey.Slack,
|
||||
icon: <Slack />,
|
||||
label: <StyledText>Support</StyledText>,
|
||||
onClick: onClickSlackHandler,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
const activeMenuKey = useMemo(() => getActiveMenuKeyFromPath(pathname), [
|
||||
pathname,
|
||||
]);
|
||||
|
||||
const isDarkMode = useIsDarkMode();
|
||||
const { toggleTheme } = useThemeMode();
|
||||
|
||||
const isCloudUserVal = isCloudUser();
|
||||
|
||||
useEffect(() => {
|
||||
if (isCloudUser() || isEECloudUser()) {
|
||||
const updatedUserManagementMenuItems = [
|
||||
helpSupportMenuItem,
|
||||
manageLicenseMenuItem,
|
||||
];
|
||||
|
||||
setUserManagementMenuItems(updatedUserManagementMenuItems);
|
||||
} else if (currentVersion && latestVersion) {
|
||||
const versionMenuItem = {
|
||||
key: SecondaryMenuItemKey.Version,
|
||||
label: !isCurrentVersionError ? currentVersion : t('n_a'),
|
||||
icon: !isLatestVersion ? (
|
||||
<AlertTriangle color="#E87040" size={16} />
|
||||
) : (
|
||||
<CheckSquare color="#D5F2BB" size={16} />
|
||||
),
|
||||
onClick: onClickVersionHandler,
|
||||
};
|
||||
|
||||
const updatedUserManagementMenuItems = [
|
||||
versionMenuItem,
|
||||
slackSupportMenuItem,
|
||||
manageLicenseMenuItem,
|
||||
];
|
||||
|
||||
setUserManagementMenuItems(updatedUserManagementMenuItems);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [currentVersion, latestVersion]);
|
||||
|
||||
const handleUserManagentMenuItemClick = (key: string): void => {
|
||||
switch (key) {
|
||||
case SecondaryMenuItemKey.Slack:
|
||||
onClickSlackHandler();
|
||||
break;
|
||||
case SecondaryMenuItemKey.Version:
|
||||
onClickVersionHandler();
|
||||
break;
|
||||
default:
|
||||
onClickHandler(key);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
||||
<StyledPrimaryMenu
|
||||
theme="dark"
|
||||
defaultSelectedKeys={[ROUTES.APPLICATION]}
|
||||
selectedKeys={activeMenuKey ? [activeMenuKey] : []}
|
||||
mode="vertical"
|
||||
style={styles}
|
||||
items={menuItems}
|
||||
onClick={onClickMenuHandler}
|
||||
<div className={cx('sideNav', collapsed ? 'collapsed' : '')}>
|
||||
<div className="brand">
|
||||
<div
|
||||
className="brand-logo"
|
||||
// eslint-disable-next-line react/no-unknown-property
|
||||
onClick={(): void => {
|
||||
// Current home page
|
||||
onClickHandler(ROUTES.APPLICATION);
|
||||
}}
|
||||
>
|
||||
<img src="/Logos/signoz-brand-logo.svg" alt="SigNoz" />
|
||||
|
||||
{!collapsed && <span className="brand-logo-name"> SigNoz </span>}
|
||||
</div>
|
||||
|
||||
{!collapsed && (
|
||||
<>
|
||||
<div className="license tag">{!isEnterprise ? 'Free' : 'Enterprise'}</div>
|
||||
|
||||
<ToggleButton
|
||||
checked={isDarkMode}
|
||||
onChange={toggleTheme}
|
||||
defaultChecked={isDarkMode}
|
||||
checkedChildren="🌜"
|
||||
unCheckedChildren="🌞"
|
||||
/>
|
||||
<StyledSecondaryMenu
|
||||
theme="dark"
|
||||
selectedKeys={activeMenuKey ? [activeMenuKey] : []}
|
||||
mode="vertical"
|
||||
style={styles}
|
||||
items={secondaryMenuItems}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{isCloudUserVal && (
|
||||
<div className="get-started-nav-items">
|
||||
<Button className="get-started-btn" onClick={onClickGetStarted}>
|
||||
<RocketIcon size={16} />
|
||||
|
||||
{!collapsed && <> Get Started </>}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="primary-nav-items">
|
||||
{menuItems.map((item, index) => (
|
||||
<NavItem
|
||||
isCollapsed={collapsed}
|
||||
key={item.key || index}
|
||||
item={item}
|
||||
isActive={activeMenuKey === item.key}
|
||||
onClick={(): void => {
|
||||
if (item) {
|
||||
onClickHandler(item?.key as string);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</Sider>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="secondary-nav-items">
|
||||
{licenseData && !isLicenseActive && (
|
||||
<NavItem
|
||||
isCollapsed={collapsed}
|
||||
key="trySignozCloud"
|
||||
item={trySignozCloudMenuItem}
|
||||
isActive={false}
|
||||
onClick={onClickSignozCloud}
|
||||
/>
|
||||
)}
|
||||
|
||||
{userManagementMenuItems.map(
|
||||
(item, index): JSX.Element => (
|
||||
<NavItem
|
||||
isCollapsed={collapsed}
|
||||
key={item?.key || index}
|
||||
item={item}
|
||||
isActive={activeMenuKey === item?.key}
|
||||
onClick={(): void => {
|
||||
handleUserManagentMenuItemClick(item?.key as string);
|
||||
}}
|
||||
/>
|
||||
),
|
||||
)}
|
||||
|
||||
{inviteMembers && (
|
||||
<NavItem
|
||||
isCollapsed={collapsed}
|
||||
key={inviteMemberMenuItem.key}
|
||||
item={inviteMemberMenuItem}
|
||||
isActive={activeMenuKey === inviteMemberMenuItem?.key}
|
||||
onClick={(): void => {
|
||||
history.push(`${inviteMemberMenuItem.key}`);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
{user && (
|
||||
<NavItem
|
||||
isCollapsed={collapsed}
|
||||
key={ROUTES.MY_SETTINGS}
|
||||
item={userSettingsMenuItem}
|
||||
isActive={activeMenuKey === userSettingsMenuItem?.key}
|
||||
onClick={(): void => {
|
||||
handleUserManagentMenuItemClick(userSettingsMenuItem?.key as string);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<div className="collapse-expand-handlers" onClick={onCollapse}>
|
||||
{collapsed ? (
|
||||
<ChevronRightCircle size={18} />
|
||||
) : (
|
||||
<ChevronLeftCircle size={18} />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@ export const routeConfig: Record<string, QueryParams[]> = {
|
||||
[ROUTES.LIST_LICENSES]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.LOGIN]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.LOGS]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.LOGS_BASE]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.MY_SETTINGS]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.NOT_FOUND]: [QueryParams.resourceAttributes],
|
||||
[ROUTES.ORG_SETTINGS]: [QueryParams.resourceAttributes],
|
||||
|
@ -1,88 +1,111 @@
|
||||
import {
|
||||
AlertOutlined,
|
||||
AlignLeftOutlined,
|
||||
BarChartOutlined,
|
||||
BugOutlined,
|
||||
DashboardFilled,
|
||||
DeploymentUnitOutlined,
|
||||
FileDoneOutlined,
|
||||
LineChartOutlined,
|
||||
MenuOutlined,
|
||||
RocketOutlined,
|
||||
SearchOutlined,
|
||||
SettingOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { RocketOutlined } from '@ant-design/icons';
|
||||
import ROUTES from 'constants/routes';
|
||||
import {
|
||||
AreaChart,
|
||||
BarChart2,
|
||||
BellDot,
|
||||
BugIcon,
|
||||
Cloudy,
|
||||
DraftingCompass,
|
||||
FileKey2,
|
||||
LayoutGrid,
|
||||
MessageSquare,
|
||||
Receipt,
|
||||
Route,
|
||||
ScrollText,
|
||||
Settings,
|
||||
Slack,
|
||||
UserPlus,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { SidebarMenu } from './sideNav.types';
|
||||
import { SecondaryMenuItemKey, SidebarItem } from './sideNav.types';
|
||||
|
||||
const menuItems: SidebarMenu[] = [
|
||||
{
|
||||
export const getStartedMenuItem = {
|
||||
key: ROUTES.GET_STARTED,
|
||||
label: 'Get Started',
|
||||
icon: <RocketOutlined rotate={45} />,
|
||||
},
|
||||
};
|
||||
|
||||
export const inviteMemberMenuItem = {
|
||||
key: `${ROUTES.ORG_SETTINGS}#invite-team-members`,
|
||||
label: 'Invite Team Member',
|
||||
icon: <UserPlus size={16} />,
|
||||
};
|
||||
|
||||
export const manageLicenseMenuItem = {
|
||||
key: ROUTES.LIST_LICENSES,
|
||||
label: 'Manage Licenses',
|
||||
icon: <FileKey2 size={16} />,
|
||||
};
|
||||
|
||||
export const helpSupportMenuItem = {
|
||||
key: ROUTES.SUPPORT,
|
||||
label: 'Help & Support',
|
||||
icon: <MessageSquare size={16} />,
|
||||
};
|
||||
|
||||
export const slackSupportMenuItem = {
|
||||
key: SecondaryMenuItemKey.Slack,
|
||||
label: 'Slack Support',
|
||||
icon: <Slack size={16} />,
|
||||
};
|
||||
|
||||
export const trySignozCloudMenuItem: SidebarItem = {
|
||||
key: 'trySignozCloud',
|
||||
label: 'Try Signoz Cloud',
|
||||
icon: <Cloudy size={16} />,
|
||||
};
|
||||
|
||||
const menuItems: SidebarItem[] = [
|
||||
{
|
||||
key: ROUTES.APPLICATION,
|
||||
label: 'Services',
|
||||
icon: <BarChartOutlined />,
|
||||
icon: <BarChart2 size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.TRACE,
|
||||
label: 'Traces',
|
||||
icon: <MenuOutlined />,
|
||||
icon: <DraftingCompass size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.LOGS_EXPLORER,
|
||||
key: ROUTES.LOGS,
|
||||
label: 'Logs',
|
||||
icon: <AlignLeftOutlined />,
|
||||
children: [
|
||||
{
|
||||
key: ROUTES.LOGS_EXPLORER,
|
||||
icon: <SearchOutlined />,
|
||||
label: 'Logs Explorer',
|
||||
},
|
||||
{
|
||||
key: ROUTES.LOGS_PIPELINES,
|
||||
icon: <DeploymentUnitOutlined />,
|
||||
label: 'Logs Pipelines',
|
||||
},
|
||||
],
|
||||
icon: <ScrollText size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.ALL_DASHBOARD,
|
||||
label: 'Dashboards',
|
||||
icon: <DashboardFilled />,
|
||||
icon: <LayoutGrid size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.LIST_ALL_ALERT,
|
||||
label: 'Alerts',
|
||||
icon: <AlertOutlined />,
|
||||
icon: <BellDot size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.ALL_ERROR,
|
||||
label: 'Exceptions',
|
||||
icon: <BugOutlined />,
|
||||
icon: <BugIcon size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.SERVICE_MAP,
|
||||
label: 'Service Map',
|
||||
icon: <DeploymentUnitOutlined />,
|
||||
icon: <Route size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.USAGE_EXPLORER,
|
||||
label: 'Usage Explorer',
|
||||
icon: <LineChartOutlined />,
|
||||
icon: <AreaChart size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.BILLING,
|
||||
label: 'Billing',
|
||||
icon: <FileDoneOutlined />,
|
||||
icon: <Receipt size={16} />,
|
||||
},
|
||||
{
|
||||
key: ROUTES.SETTINGS,
|
||||
label: 'Settings',
|
||||
icon: <SettingOutlined />,
|
||||
icon: <Settings size={16} />,
|
||||
},
|
||||
];
|
||||
|
||||
@ -90,7 +113,7 @@ const menuItems: SidebarMenu[] = [
|
||||
export const NEW_ROUTES_MENU_ITEM_KEY_MAP = {
|
||||
[ROUTES.TRACES_EXPLORER]: ROUTES.TRACE,
|
||||
[ROUTES.TRACE_EXPLORER]: ROUTES.TRACE,
|
||||
[ROUTES.LOGS_EXPLORER]: ROUTES.LOGS_EXPLORER,
|
||||
[ROUTES.LOGS_BASE]: ROUTES.LOGS_EXPLORER,
|
||||
};
|
||||
|
||||
export default menuItems;
|
||||
|
@ -8,10 +8,9 @@ export type SidebarMenu = MenuItem & {
|
||||
};
|
||||
|
||||
export interface SidebarItem {
|
||||
onClick: VoidFunction;
|
||||
icon?: ReactNode;
|
||||
text?: ReactNode;
|
||||
key: string;
|
||||
key: string | number;
|
||||
label?: ReactNode;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ const breadcrumbNameMap = {
|
||||
[ROUTES.ALL_DASHBOARD]: 'Dashboard',
|
||||
[ROUTES.LOGS]: 'Logs',
|
||||
[ROUTES.LOGS_EXPLORER]: 'Logs Explorer',
|
||||
[ROUTES.OLD_LOGS_EXPLORER]: 'Old Logs Explorer',
|
||||
[ROUTES.LIVE_LOGS]: 'Live View',
|
||||
[ROUTES.LOGS_PIPELINES]: 'Logs Pipelines',
|
||||
[ROUTES.BILLING]: 'Billing',
|
||||
|
@ -90,6 +90,9 @@ export const routesToSkip = [
|
||||
ROUTES.BILLING,
|
||||
ROUTES.SUPPORT,
|
||||
ROUTES.WORKSPACE_LOCKED,
|
||||
ROUTES.LOGS,
|
||||
ROUTES.MY_SETTINGS,
|
||||
ROUTES.LIST_LICENSES,
|
||||
];
|
||||
|
||||
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
||||
|
@ -4,14 +4,8 @@ import { useMemo } from 'react';
|
||||
import { matchPath, useHistory } from 'react-router-dom';
|
||||
|
||||
import NewExplorerCTA from '../NewExplorerCTA';
|
||||
import ShowBreadcrumbs from './Breadcrumbs';
|
||||
import DateTimeSelector from './DateTimeSelection';
|
||||
import {
|
||||
routesToDisable,
|
||||
routesToHideBreadCrumbs,
|
||||
routesToSkip,
|
||||
} from './DateTimeSelection/config';
|
||||
import { Container } from './styles';
|
||||
import { routesToDisable, routesToSkip } from './DateTimeSelection/config';
|
||||
|
||||
function TopNav(): JSX.Element | null {
|
||||
const { location } = useHistory();
|
||||
@ -24,14 +18,6 @@ function TopNav(): JSX.Element | null {
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const isRouteToHideBreadCrumbs = useMemo(
|
||||
() =>
|
||||
routesToHideBreadCrumbs.some((route) =>
|
||||
matchPath(location.pathname, { path: route, exact: true }),
|
||||
),
|
||||
[location.pathname],
|
||||
);
|
||||
|
||||
const isDisabled = useMemo(
|
||||
() =>
|
||||
routesToDisable.some((route) =>
|
||||
@ -50,15 +36,9 @@ function TopNav(): JSX.Element | null {
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
{!isRouteToHideBreadCrumbs && (
|
||||
<Col span={16}>
|
||||
<ShowBreadcrumbs />
|
||||
</Col>
|
||||
)}
|
||||
|
||||
<Row>
|
||||
{!isRouteToSkip && (
|
||||
<Col span={isRouteToHideBreadCrumbs ? 24 : 8}>
|
||||
<Col span={24} style={{ marginTop: '1rem' }}>
|
||||
<Row justify="end">
|
||||
<Space align="start" size={60} direction="horizontal">
|
||||
<NewExplorerCTA />
|
||||
@ -69,7 +49,7 @@ function TopNav(): JSX.Element | null {
|
||||
</Row>
|
||||
</Col>
|
||||
)}
|
||||
</Container>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,6 @@ import styled from 'styled-components';
|
||||
|
||||
export const Container = styled(Row)`
|
||||
&&& {
|
||||
margin-top: 2rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`;
|
||||
|
@ -36,7 +36,9 @@ export const useActiveLog = (): UseActiveLog => {
|
||||
const { currentQuery, redirectWithQueryBuilderData } = useQueryBuilder();
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const isLogsPage = useMemo(() => pathname === ROUTES.LOGS, [pathname]);
|
||||
const isLogsPage = useMemo(() => pathname === ROUTES.OLD_LOGS_EXPLORER, [
|
||||
pathname,
|
||||
]);
|
||||
|
||||
const [activeLog, setActiveLog] = useState<ILog | null>(null);
|
||||
|
||||
@ -135,7 +137,7 @@ export const useActiveLog = (): UseActiveLog => {
|
||||
queryString,
|
||||
);
|
||||
|
||||
history.replace(`${ROUTES.LOGS}?q=${updatedQueryString}`);
|
||||
history.replace(`${ROUTES.OLD_LOGS_EXPLORER}?q=${updatedQueryString}`);
|
||||
},
|
||||
[history, queryString],
|
||||
);
|
||||
|
@ -76,6 +76,11 @@ export const useThemeConfig = (): ThemeConfig => {
|
||||
borderRadiusXS: 2,
|
||||
fontFamily: 'Inter',
|
||||
fontSize: 13,
|
||||
colorPrimary: '#4E74F8',
|
||||
colorBgBase: isDarkMode ? '#0B0C0E' : '#fff',
|
||||
colorBgContainer: isDarkMode ? '#121317' : '#fff',
|
||||
colorLink: '#4E74F8',
|
||||
colorPrimaryText: '#3F5ECC',
|
||||
},
|
||||
};
|
||||
};
|
||||
|
@ -67,7 +67,7 @@
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&display=swap"
|
||||
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;700&family=Work+Sans:wght@500&display=swap"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
</head>
|
||||
|
@ -8,7 +8,6 @@ import { createRoot } from 'react-dom/client';
|
||||
import { ErrorBoundary } from 'react-error-boundary';
|
||||
import { HelmetProvider } from 'react-helmet-async';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
import { ReactQueryDevtools } from 'react-query/devtools';
|
||||
import { Provider } from 'react-redux';
|
||||
import store from 'store';
|
||||
|
||||
@ -33,7 +32,6 @@ if (container) {
|
||||
<Provider store={store}>
|
||||
<AppRoutes />
|
||||
</Provider>
|
||||
{process.env.NODE_ENV === 'development' && <ReactQueryDevtools />}
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</HelmetProvider>
|
||||
|
@ -30,7 +30,7 @@ import { useSelectedLogView } from './hooks';
|
||||
import PopoverContent from './PopoverContent';
|
||||
import SpaceContainer from './styles';
|
||||
|
||||
function Logs(): JSX.Element {
|
||||
function OldLogsExplorer(): JSX.Element {
|
||||
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||
const { order } = useSelector<AppState, ILogsReducer>((store) => store.logs);
|
||||
const location = useLocation();
|
||||
@ -148,4 +148,4 @@ function Logs(): JSX.Element {
|
||||
);
|
||||
}
|
||||
|
||||
export default Logs;
|
||||
export default OldLogsExplorer;
|
||||
|
28
frontend/src/pages/LogsModulePage/LogsModulePage.tsx
Normal file
28
frontend/src/pages/LogsModulePage/LogsModulePage.tsx
Normal file
@ -0,0 +1,28 @@
|
||||
import RouteTab from 'components/RouteTab';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import LogsExplorer from 'pages/LogsExplorer';
|
||||
import Pipelines from 'pages/Pipelines';
|
||||
import { useLocation } from 'react-use';
|
||||
|
||||
export const logsExplorer = {
|
||||
Component: LogsExplorer,
|
||||
name: 'Explorer',
|
||||
route: ROUTES.LOGS,
|
||||
key: ROUTES.LOGS,
|
||||
};
|
||||
|
||||
export const logsPipelines = {
|
||||
Component: Pipelines,
|
||||
name: 'Pipelines',
|
||||
route: ROUTES.LOGS_PIPELINES,
|
||||
key: ROUTES.LOGS_PIPELINES,
|
||||
};
|
||||
|
||||
export default function LogsModulePage(): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const routes = [logsExplorer, logsPipelines];
|
||||
|
||||
return <RouteTab routes={routes} activeKey={pathname} history={history} />;
|
||||
}
|
3
frontend/src/pages/LogsModulePage/index.tsx
Normal file
3
frontend/src/pages/LogsModulePage/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
import LogsModulePage from './LogsModulePage';
|
||||
|
||||
export default LogsModulePage;
|
@ -81,7 +81,7 @@ function Pipelines(): JSX.Element {
|
||||
|
||||
return (
|
||||
<ErrorBoundary FallbackComponent={ErrorBoundaryFallback}>
|
||||
<Tabs defaultActiveKey="pipelines" items={tabItems} />;
|
||||
<Tabs defaultActiveKey="pipelines" items={tabItems} />
|
||||
</ErrorBoundary>
|
||||
);
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
.workspace-locked-container {
|
||||
text-align: center;
|
||||
padding: 48px;
|
||||
margin: 48px;
|
||||
margin: 24px;
|
||||
}
|
||||
|
||||
.workpace-locked-details {
|
||||
|
@ -6,6 +6,7 @@ import { Button, Card, Skeleton, Typography } from 'antd';
|
||||
import updateCreditCardApi from 'api/billing/checkout';
|
||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||
import ROUTES from 'constants/routes';
|
||||
import FullViewHeader from 'container/FullViewHeader/FullViewHeader';
|
||||
import useLicense from 'hooks/useLicense';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import history from 'lib/history';
|
||||
@ -75,6 +76,9 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
}, [activeLicense?.key, updateCreditCard]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<FullViewHeader overrideRoute={ROUTES.WORKSPACE_LOCKED} />
|
||||
|
||||
<Card className="workspace-locked-container">
|
||||
{isLoadingLicenseData || !licensesData?.payload?.workSpaceBlock ? (
|
||||
<Skeleton />
|
||||
@ -112,5 +116,6 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
</>
|
||||
)}
|
||||
</Card>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -1,3 +1,5 @@
|
||||
@import '@signozhq/design-tokens';
|
||||
|
||||
#root,
|
||||
html,
|
||||
body {
|
||||
|
@ -86,4 +86,6 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
BILLING: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
SUPPORT: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
SOMETHING_WENT_WRONG: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
LOGS_BASE: [],
|
||||
OLD_LOGS_EXPLORER: [],
|
||||
};
|
||||
|
@ -3082,6 +3082,13 @@
|
||||
resolved "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@signozhq/design-tokens@0.0.6":
|
||||
version "0.0.6"
|
||||
resolved "https://registry.yarnpkg.com/@signozhq/design-tokens/-/design-tokens-0.0.6.tgz#42449052dca644c4d52448f9c2c521d39e535720"
|
||||
integrity sha512-i+aG0YCuYL2KVUtRFj3qgAVDU6GbKmTdFXpqCqLUQp8diKMWH5Svzzxj4B14Q6+yE79+wbm1iZ0Nr6nYgkBA8Q==
|
||||
dependencies:
|
||||
style-dictionary "3.8.0"
|
||||
|
||||
"@sinclair/typebox@^0.25.16":
|
||||
version "0.25.24"
|
||||
resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.25.24.tgz"
|
||||
@ -5422,6 +5429,15 @@ canvas-color-tracker@1:
|
||||
dependencies:
|
||||
tinycolor2 "^1.6.0"
|
||||
|
||||
capital-case@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/capital-case/-/capital-case-1.0.4.tgz#9d130292353c9249f6b00fa5852bee38a717e669"
|
||||
integrity sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==
|
||||
dependencies:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
cardboard-vr-display@^1.0.19:
|
||||
version "1.0.19"
|
||||
resolved "https://registry.npmjs.org/cardboard-vr-display/-/cardboard-vr-display-1.0.19.tgz"
|
||||
@ -5461,6 +5477,24 @@ chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
change-case@^4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/change-case/-/change-case-4.1.2.tgz#fedfc5f136045e2398c0410ee441f95704641e12"
|
||||
integrity sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==
|
||||
dependencies:
|
||||
camel-case "^4.1.2"
|
||||
capital-case "^1.0.4"
|
||||
constant-case "^3.0.4"
|
||||
dot-case "^3.0.4"
|
||||
header-case "^2.0.4"
|
||||
no-case "^3.0.4"
|
||||
param-case "^3.0.4"
|
||||
pascal-case "^3.1.2"
|
||||
path-case "^3.0.4"
|
||||
sentence-case "^3.0.4"
|
||||
snake-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
char-regex@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz"
|
||||
@ -5842,6 +5876,15 @@ connect-history-api-fallback@^2.0.0:
|
||||
resolved "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-2.0.0.tgz"
|
||||
integrity sha512-U73+6lQFmfiNPrYbXqr6kZ1i1wiRqXnp2nhMsINseWXO8lDau0LGEffJ8kQi4EjLZympVgRdvqjAgiZ1tgzDDA==
|
||||
|
||||
constant-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/constant-case/-/constant-case-3.0.4.tgz#3b84a9aeaf4cf31ec45e6bf5de91bdfb0589faf1"
|
||||
integrity sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==
|
||||
dependencies:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case "^2.0.2"
|
||||
|
||||
content-disposition@0.5.4:
|
||||
version "0.5.4"
|
||||
resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
|
||||
@ -7993,7 +8036,7 @@ glob-to-regexp@^0.4.1:
|
||||
resolved "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@ -8329,6 +8372,14 @@ he@^1.2.0:
|
||||
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
|
||||
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
|
||||
|
||||
header-case@^2.0.4:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.yarnpkg.com/header-case/-/header-case-2.0.4.tgz#5a42e63b55177349cf405beb8d775acabb92c063"
|
||||
integrity sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==
|
||||
dependencies:
|
||||
capital-case "^1.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
headers-polyfill@3.2.5:
|
||||
version "3.2.5"
|
||||
resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.2.5.tgz#6e67d392c9d113d37448fe45014e0afdd168faed"
|
||||
@ -9871,6 +9922,11 @@ json5@^1.0.2:
|
||||
dependencies:
|
||||
minimist "^1.2.0"
|
||||
|
||||
jsonc-parser@^3.0.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
|
||||
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
|
||||
|
||||
jsonfile@^6.0.1:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz"
|
||||
@ -11789,6 +11845,14 @@ pascal-case@^3.1.2:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
path-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/path-case/-/path-case-3.0.4.tgz#9168645334eb942658375c56f80b4c0cb5f82c6f"
|
||||
integrity sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==
|
||||
dependencies:
|
||||
dot-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
path-exists@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
|
||||
@ -13841,6 +13905,15 @@ send@0.18.0:
|
||||
range-parser "~1.2.1"
|
||||
statuses "2.0.1"
|
||||
|
||||
sentence-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/sentence-case/-/sentence-case-3.0.4.tgz#3645a7b8c117c787fde8702056225bb62a45131f"
|
||||
integrity sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==
|
||||
dependencies:
|
||||
no-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
upper-case-first "^2.0.2"
|
||||
|
||||
serialize-javascript@^5.0.1:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz"
|
||||
@ -14017,6 +14090,14 @@ slice-ansi@^5.0.0:
|
||||
ansi-styles "^6.0.0"
|
||||
is-fullwidth-code-point "^4.0.0"
|
||||
|
||||
snake-case@^3.0.4:
|
||||
version "3.0.4"
|
||||
resolved "https://registry.yarnpkg.com/snake-case/-/snake-case-3.0.4.tgz#4f2bbd568e9935abdfd593f34c691dadb49c452c"
|
||||
integrity sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==
|
||||
dependencies:
|
||||
dot-case "^3.0.4"
|
||||
tslib "^2.0.3"
|
||||
|
||||
sockjs@^0.3.24:
|
||||
version "0.3.24"
|
||||
resolved "https://registry.npmjs.org/sockjs/-/sockjs-0.3.24.tgz"
|
||||
@ -14397,6 +14478,21 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
||||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
style-dictionary@3.8.0:
|
||||
version "3.8.0"
|
||||
resolved "https://registry.yarnpkg.com/style-dictionary/-/style-dictionary-3.8.0.tgz#7cb8d64360c53431f768d44def665f61e971a73e"
|
||||
integrity sha512-wHlB/f5eO3mDcYv6WtOz6gvQC477jBKrwuIXe+PtHskTCBsJdAOvL8hCquczJxDui2TnwpeNE+2msK91JJomZg==
|
||||
dependencies:
|
||||
chalk "^4.0.0"
|
||||
change-case "^4.1.2"
|
||||
commander "^8.3.0"
|
||||
fs-extra "^10.0.0"
|
||||
glob "^7.2.0"
|
||||
json5 "^2.2.2"
|
||||
jsonc-parser "^3.0.0"
|
||||
lodash "^4.17.15"
|
||||
tinycolor2 "^1.4.1"
|
||||
|
||||
style-loader@1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz"
|
||||
@ -14698,7 +14794,7 @@ tiny-warning@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz"
|
||||
integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==
|
||||
|
||||
tinycolor2@1, tinycolor2@1.6.0, tinycolor2@^1.6.0:
|
||||
tinycolor2@1, tinycolor2@1.6.0, tinycolor2@^1.4.1, tinycolor2@^1.6.0:
|
||||
version "1.6.0"
|
||||
resolved "https://registry.yarnpkg.com/tinycolor2/-/tinycolor2-1.6.0.tgz#f98007460169b0263b97072c5ae92484ce02d09e"
|
||||
integrity sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==
|
||||
@ -15156,6 +15252,20 @@ uplot@1.6.26:
|
||||
resolved "https://registry.yarnpkg.com/uplot/-/uplot-1.6.26.tgz#a6012fd141ad4a71741c75af0c71283d0ade45a7"
|
||||
integrity sha512-qN0mveL6UsP40TnHzHAJkUQvpfA3y8zSLXtXKVlJo/sLfj2+vjan/Z3g81MCZjy/hEDUFNtnLftPmETDA4s7Rg==
|
||||
|
||||
upper-case-first@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case-first/-/upper-case-first-2.0.2.tgz#992c3273f882abd19d1e02894cc147117f844324"
|
||||
integrity sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
upper-case@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-2.0.2.tgz#d89810823faab1df1549b7d97a76f8662bae6f7a"
|
||||
integrity sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==
|
||||
dependencies:
|
||||
tslib "^2.0.3"
|
||||
|
||||
uri-js@^4.2.2:
|
||||
version "4.4.1"
|
||||
resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
|
||||
|
Loading…
x
Reference in New Issue
Block a user