mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-18 02:35:57 +08:00
feat: version page is added (#924)
* feat👔 : getLatestVersion api is added * chore: VERSION page is added * feat: ✨ version page is added * chore: all string is grabbed from locale * chore: warning is removed * chore: translation json is added * chore: feedback about version is added * chore: made two different functions * unused import is removed * feat: version changes are updated * chore: if current version is present then it is displayed
This commit is contained in:
parent
5744193f50
commit
2a348e916c
@ -1,5 +1,14 @@
|
|||||||
{
|
{
|
||||||
"monitor_signup": "Monitor your applications. Find what is causing issues.",
|
"monitor_signup": "Monitor your applications. Find what is causing issues.",
|
||||||
|
"version": "Version",
|
||||||
|
"latest_version": "Latest version",
|
||||||
|
"current_version": "Current version",
|
||||||
|
"release_notes": "Release Notes",
|
||||||
|
"read_how_to_upgrade": "Read instructions on how to upgrade",
|
||||||
|
"latest_version_signoz": "You are running the latest version of SigNoz.",
|
||||||
|
"stale_version": "You are on an older version and may be loosing on the latest features we have shipped. We recommend to upgrade to the latest version",
|
||||||
|
"oops_something_went_wrong_version": "Oops.. facing issues with fetching updated version information",
|
||||||
|
"n_a": "N/A",
|
||||||
"routes": {
|
"routes": {
|
||||||
"general": "General",
|
"general": "General",
|
||||||
"alert_channels": "Alert Channels"
|
"alert_channels": "Alert Channels"
|
||||||
|
@ -85,3 +85,7 @@ export const EditAlertChannelsAlerts = Loadable(
|
|||||||
export const AllAlertChannels = Loadable(
|
export const AllAlertChannels = Loadable(
|
||||||
() => import(/* webpackChunkName: "All Channels" */ 'pages/AllAlertChannels'),
|
() => import(/* webpackChunkName: "All Channels" */ 'pages/AllAlertChannels'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const StatusPage = Loadable(
|
||||||
|
() => import(/* webpackChunkName: "All Status" */ 'pages/Status'),
|
||||||
|
);
|
||||||
|
@ -17,6 +17,7 @@ import {
|
|||||||
ServicesTablePage,
|
ServicesTablePage,
|
||||||
SettingsPage,
|
SettingsPage,
|
||||||
SignupPage,
|
SignupPage,
|
||||||
|
StatusPage,
|
||||||
TraceDetail,
|
TraceDetail,
|
||||||
TraceFilter,
|
TraceFilter,
|
||||||
UsageExplorerPage,
|
UsageExplorerPage,
|
||||||
@ -113,6 +114,11 @@ const routes: AppRoutes[] = [
|
|||||||
exact: true,
|
exact: true,
|
||||||
component: AllAlertChannels,
|
component: AllAlertChannels,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ROUTES.VERSION,
|
||||||
|
exact: true,
|
||||||
|
component: StatusPage,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
interface AppRoutes {
|
interface AppRoutes {
|
||||||
|
25
frontend/src/api/user/getLatestVersion.ts
Normal file
25
frontend/src/api/user/getLatestVersion.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
||||||
|
import axios, { AxiosError } from 'axios';
|
||||||
|
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||||
|
import { PayloadProps } from 'types/api/user/getLatestVersion';
|
||||||
|
|
||||||
|
const getLatestVersion = async (): Promise<
|
||||||
|
SuccessResponse<PayloadProps> | ErrorResponse
|
||||||
|
> => {
|
||||||
|
try {
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://api.github.com/repos/signoz/signoz/releases/latest`,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
statusCode: 200,
|
||||||
|
error: null,
|
||||||
|
message: response.data.status,
|
||||||
|
payload: response.data,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return ErrorResponseHandler(error as AxiosError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getLatestVersion;
|
@ -17,6 +17,7 @@ const ROUTES = {
|
|||||||
ALL_CHANNELS: '/settings/channels',
|
ALL_CHANNELS: '/settings/channels',
|
||||||
CHANNELS_NEW: '/setting/channels/new',
|
CHANNELS_NEW: '/setting/channels/new',
|
||||||
CHANNELS_EDIT: '/setting/channels/edit/:id',
|
CHANNELS_EDIT: '/setting/channels/edit/:id',
|
||||||
|
VERSION: '/status',
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ROUTES;
|
export default ROUTES;
|
||||||
|
@ -1,11 +1,24 @@
|
|||||||
|
import { notification } from 'antd';
|
||||||
|
import getLatestVersion from 'api/user/getLatestVersion';
|
||||||
|
import getVersion from 'api/user/getVersion';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import TopNav from 'container/Header';
|
import TopNav from 'container/Header';
|
||||||
import SideNav from 'container/SideNav';
|
import SideNav from 'container/SideNav';
|
||||||
|
import useFetch from 'hooks/useFetch';
|
||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import React, { ReactNode, useEffect, useState } from 'react';
|
import React, { ReactNode, useEffect, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useDispatch, useSelector } from 'react-redux';
|
||||||
import { useLocation } from 'react-router-dom';
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { Dispatch } from 'redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppActions from 'types/actions';
|
||||||
|
import {
|
||||||
|
UPDATE_CURRENT_ERROR,
|
||||||
|
UPDATE_CURRENT_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
|
} from 'types/actions/app';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
import { Content, Layout } from './styles';
|
import { Content, Layout } from './styles';
|
||||||
@ -13,11 +26,24 @@ import { Content, Layout } from './styles';
|
|||||||
function AppLayout(props: AppLayoutProps): JSX.Element {
|
function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||||
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { isLoggedIn } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname);
|
const [isSignUpPage, setIsSignUpPage] = useState(ROUTES.SIGN_UP === pathname);
|
||||||
|
|
||||||
|
const { payload: versionPayload, loading, error: getVersionError } = useFetch(
|
||||||
|
getVersion,
|
||||||
|
);
|
||||||
|
|
||||||
|
const {
|
||||||
|
payload: latestVersionPayload,
|
||||||
|
loading: latestLoading,
|
||||||
|
error: latestError,
|
||||||
|
} = useFetch(getLatestVersion);
|
||||||
|
|
||||||
const { children } = props;
|
const { children } = props;
|
||||||
|
|
||||||
|
const dispatch = useDispatch<Dispatch<AppActions>>();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoggedIn) {
|
if (!isLoggedIn) {
|
||||||
setIsSignUpPage(true);
|
setIsSignUpPage(true);
|
||||||
@ -27,11 +53,71 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
|||||||
}
|
}
|
||||||
}, [isLoggedIn, isSignUpPage]);
|
}, [isLoggedIn, isSignUpPage]);
|
||||||
|
|
||||||
|
const latestCurrentCounter = useRef(0);
|
||||||
|
const latestVersionCounter = useRef(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isLoggedIn && pathname === ROUTES.SIGN_UP) {
|
if (isLoggedIn && pathname === ROUTES.SIGN_UP) {
|
||||||
history.push(ROUTES.APPLICATION);
|
history.push(ROUTES.APPLICATION);
|
||||||
}
|
}
|
||||||
}, [isLoggedIn, pathname]);
|
|
||||||
|
if (!latestLoading && latestError && latestCurrentCounter.current === 0) {
|
||||||
|
latestCurrentCounter.current = 1;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_LATEST_VERSION_ERROR,
|
||||||
|
payload: {
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.error({
|
||||||
|
message: t('oops_something_went_wrong_version'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loading && getVersionError && latestVersionCounter.current === 0) {
|
||||||
|
latestVersionCounter.current = 1;
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_CURRENT_ERROR,
|
||||||
|
payload: {
|
||||||
|
isError: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
notification.error({
|
||||||
|
message: t('oops_something_went_wrong_version'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!latestLoading && versionPayload) {
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_CURRENT_VERSION,
|
||||||
|
payload: {
|
||||||
|
currentVersion: versionPayload.version,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loading && latestVersionPayload) {
|
||||||
|
dispatch({
|
||||||
|
type: UPDATE_LATEST_VERSION,
|
||||||
|
payload: {
|
||||||
|
latestVersion: latestVersionPayload.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
dispatch,
|
||||||
|
loading,
|
||||||
|
latestLoading,
|
||||||
|
versionPayload,
|
||||||
|
latestVersionPayload,
|
||||||
|
isLoggedIn,
|
||||||
|
pathname,
|
||||||
|
getVersionError,
|
||||||
|
latestError,
|
||||||
|
t,
|
||||||
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
|
@ -11,6 +11,7 @@ const breadcrumbNameMap = {
|
|||||||
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
[ROUTES.INSTRUMENTATION]: 'Add instrumentation',
|
||||||
[ROUTES.SETTINGS]: 'Settings',
|
[ROUTES.SETTINGS]: 'Settings',
|
||||||
[ROUTES.DASHBOARD]: 'Dashboard',
|
[ROUTES.DASHBOARD]: 'Dashboard',
|
||||||
|
[ROUTES.VERSION]: 'Status',
|
||||||
};
|
};
|
||||||
|
|
||||||
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
function ShowBreadcrumbs(props: RouteComponentProps): JSX.Element {
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons';
|
||||||
import { Menu, Typography } from 'antd';
|
import { Menu, Typography } from 'antd';
|
||||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
import { IS_SIDEBAR_COLLAPSED } from 'constants/app';
|
||||||
@ -5,6 +6,7 @@ import ROUTES from 'constants/routes';
|
|||||||
import history from 'lib/history';
|
import history from 'lib/history';
|
||||||
import setTheme, { AppMode } from 'lib/theme/setTheme';
|
import setTheme, { AppMode } from 'lib/theme/setTheme';
|
||||||
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
import React, { useCallback, useLayoutEffect, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { connect, useDispatch, useSelector } from 'react-redux';
|
import { connect, useDispatch, useSelector } from 'react-redux';
|
||||||
import { NavLink, useLocation } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
@ -19,11 +21,13 @@ import menus from './menuItems';
|
|||||||
import Slack from './Slack';
|
import Slack from './Slack';
|
||||||
import {
|
import {
|
||||||
Logo,
|
Logo,
|
||||||
|
RedDot,
|
||||||
Sider,
|
Sider,
|
||||||
SlackButton,
|
SlackButton,
|
||||||
SlackMenuItemContainer,
|
SlackMenuItemContainer,
|
||||||
ThemeSwitcherWrapper,
|
ThemeSwitcherWrapper,
|
||||||
ToggleButton,
|
ToggleButton,
|
||||||
|
VersionContainer,
|
||||||
} from './styles';
|
} from './styles';
|
||||||
|
|
||||||
function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
||||||
@ -31,9 +35,15 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
const [collapsed, setCollapsed] = useState<boolean>(
|
const [collapsed, setCollapsed] = useState<boolean>(
|
||||||
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
);
|
);
|
||||||
const { isDarkMode } = useSelector<AppState, AppReducer>((state) => state.app);
|
const {
|
||||||
|
isDarkMode,
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
isCurrentVersionError,
|
||||||
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
|
const { t } = useTranslation('');
|
||||||
|
|
||||||
const toggleTheme = useCallback(() => {
|
const toggleTheme = useCallback(() => {
|
||||||
const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode';
|
const preMode: AppMode = isDarkMode ? 'lightMode' : 'darkMode';
|
||||||
@ -77,6 +87,38 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
window.open('https://signoz.io/slack', '_blank');
|
window.open('https://signoz.io/slack', '_blank');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onClickVersionHandler = (): void => {
|
||||||
|
history.push(ROUTES.VERSION);
|
||||||
|
};
|
||||||
|
|
||||||
|
const isNotCurrentVersion = currentVersion !== latestVersion;
|
||||||
|
|
||||||
|
const sidebar = [
|
||||||
|
{
|
||||||
|
onClick: onClickSlackHandler,
|
||||||
|
icon: <Slack />,
|
||||||
|
text: <SlackButton>Support</SlackButton>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
onClick: onClickVersionHandler,
|
||||||
|
icon: isNotCurrentVersion ? (
|
||||||
|
<WarningOutlined style={{ color: '#E87040' }} />
|
||||||
|
) : (
|
||||||
|
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
|
||||||
|
),
|
||||||
|
text: (
|
||||||
|
<VersionContainer>
|
||||||
|
{!isCurrentVersionError ? (
|
||||||
|
<SlackButton>{currentVersion}</SlackButton>
|
||||||
|
) : (
|
||||||
|
<SlackButton>{t('n_a')}</SlackButton>
|
||||||
|
)}
|
||||||
|
{isNotCurrentVersion && <RedDot />}
|
||||||
|
</VersionContainer>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
<Sider collapsible collapsed={collapsed} onCollapse={onCollapse} width={200}>
|
||||||
<ThemeSwitcherWrapper>
|
<ThemeSwitcherWrapper>
|
||||||
@ -87,7 +129,7 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
/>
|
/>
|
||||||
</ThemeSwitcherWrapper>
|
</ThemeSwitcherWrapper>
|
||||||
<NavLink to={ROUTES.APPLICATION}>
|
<NavLink to={ROUTES.APPLICATION}>
|
||||||
<Logo src="/signoz.svg" alt="SigNoz" collapsed={collapsed} />
|
<Logo index={0} src="/signoz.svg" alt="SigNoz" collapsed={collapsed} />
|
||||||
</NavLink>
|
</NavLink>
|
||||||
|
|
||||||
<Menu
|
<Menu
|
||||||
@ -105,11 +147,21 @@ function SideNav({ toggleDarkMode }: Props): JSX.Element {
|
|||||||
<Typography>{name}</Typography>
|
<Typography>{name}</Typography>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
))}
|
))}
|
||||||
<SlackMenuItemContainer collapsed={collapsed}>
|
{sidebar.map((props, index) => (
|
||||||
<Menu.Item onClick={onClickSlackHandler} icon={<Slack />}>
|
<SlackMenuItemContainer
|
||||||
<SlackButton>Support</SlackButton>
|
index={index + 1}
|
||||||
</Menu.Item>
|
key={`${index + 1}`}
|
||||||
</SlackMenuItemContainer>
|
collapsed={collapsed}
|
||||||
|
>
|
||||||
|
<Menu.Item
|
||||||
|
eventKey={index.toString()}
|
||||||
|
onClick={props.onClick}
|
||||||
|
icon={props.icon}
|
||||||
|
>
|
||||||
|
{props.text}
|
||||||
|
</Menu.Item>
|
||||||
|
</SlackMenuItemContainer>
|
||||||
|
))}
|
||||||
</Menu>
|
</Menu>
|
||||||
</Sider>
|
</Sider>
|
||||||
);
|
);
|
||||||
|
@ -19,6 +19,7 @@ export const Logo = styled.img<LogoProps>`
|
|||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
collapsed: boolean;
|
collapsed: boolean;
|
||||||
|
index: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Sider = styled(SiderComponent)`
|
export const Sider = styled(SiderComponent)`
|
||||||
@ -50,9 +51,10 @@ export const SlackButton = styled(Typography)`
|
|||||||
|
|
||||||
export const SlackMenuItemContainer = styled.div<LogoProps>`
|
export const SlackMenuItemContainer = styled.div<LogoProps>`
|
||||||
position: fixed;
|
position: fixed;
|
||||||
bottom: 48px;
|
bottom: ${({ index }): string => `${index * 48 + (index + 16)}px`};
|
||||||
background: #262626;
|
background: #262626;
|
||||||
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
|
width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')};
|
||||||
|
transition: inherit;
|
||||||
|
|
||||||
&&& {
|
&&& {
|
||||||
li {
|
li {
|
||||||
@ -60,11 +62,14 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
collapsed &&
|
collapsed &&
|
||||||
css`
|
css`
|
||||||
padding-left: 24px;
|
padding-left: 24px;
|
||||||
|
padding-top: 6px;
|
||||||
`}
|
`}
|
||||||
}
|
}
|
||||||
|
|
||||||
svg {
|
svg {
|
||||||
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
|
margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')};
|
||||||
|
width: 28px;
|
||||||
|
height: 28px;
|
||||||
|
|
||||||
${({ collapsed }): StyledCSS =>
|
${({ collapsed }): StyledCSS =>
|
||||||
collapsed &&
|
collapsed &&
|
||||||
@ -73,5 +78,24 @@ export const SlackMenuItemContainer = styled.div<LogoProps>`
|
|||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
`}
|
`}
|
||||||
}
|
}
|
||||||
|
.ant-menu-title-content {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const RedDot = styled.div`
|
||||||
|
width: 12px;
|
||||||
|
height: 12px;
|
||||||
|
background: #d32029;
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
margin-left: 1rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const VersionContainer = styled.div`
|
||||||
|
&&& {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
110
frontend/src/container/Version/index.tsx
Normal file
110
frontend/src/container/Version/index.tsx
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
import { WarningFilled } from '@ant-design/icons';
|
||||||
|
import { Button, Card, Form, Space, Typography } from 'antd';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { useSelector } from 'react-redux';
|
||||||
|
import { AppState } from 'store/reducers';
|
||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
|
import { InputComponent } from './styles';
|
||||||
|
|
||||||
|
const { Title } = Typography;
|
||||||
|
|
||||||
|
function Version(): JSX.Element {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const onClickUpgradeHandler = useCallback((link: string) => {
|
||||||
|
window.open(link, '_blank');
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const {
|
||||||
|
currentVersion,
|
||||||
|
latestVersion,
|
||||||
|
isCurrentVersionError,
|
||||||
|
isLatestVersionError,
|
||||||
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
|
const isLatestVersion = currentVersion === latestVersion;
|
||||||
|
const isError = isCurrentVersionError || isLatestVersionError;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<Title ellipsis level={4}>
|
||||||
|
{t('version')}
|
||||||
|
</Title>
|
||||||
|
|
||||||
|
<Form
|
||||||
|
wrapperCol={{
|
||||||
|
span: 14,
|
||||||
|
}}
|
||||||
|
labelCol={{
|
||||||
|
span: 3,
|
||||||
|
}}
|
||||||
|
layout="horizontal"
|
||||||
|
form={form}
|
||||||
|
labelAlign="left"
|
||||||
|
>
|
||||||
|
<Form.Item label={t('current_version')}>
|
||||||
|
<InputComponent
|
||||||
|
readOnly
|
||||||
|
value={isCurrentVersionError ? t('n_a').toString() : currentVersion}
|
||||||
|
placeholder={t('current_version')}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
|
||||||
|
<Form.Item label={t('latest_version')}>
|
||||||
|
<InputComponent
|
||||||
|
readOnly
|
||||||
|
value={isLatestVersionError ? t('n_a').toString() : latestVersion}
|
||||||
|
placeholder={t('latest_version')}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={(): void =>
|
||||||
|
onClickUpgradeHandler('https://github.com/SigNoz/signoz/releases')
|
||||||
|
}
|
||||||
|
type="link"
|
||||||
|
>
|
||||||
|
{t('release_notes')}
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
|
||||||
|
{!isError && isLatestVersion && (
|
||||||
|
<div>
|
||||||
|
<Space align="start">
|
||||||
|
<span>✅</span>
|
||||||
|
<Typography.Paragraph italic>
|
||||||
|
{t('latest_version_signoz')}
|
||||||
|
</Typography.Paragraph>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isError && !isLatestVersion && (
|
||||||
|
<div>
|
||||||
|
<Space align="start">
|
||||||
|
<span>
|
||||||
|
<WarningFilled style={{ color: '#E87040' }} />
|
||||||
|
</span>
|
||||||
|
<Typography.Paragraph italic>{t('stale_version')}</Typography.Paragraph>
|
||||||
|
</Space>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isError && !isLatestVersion && (
|
||||||
|
<Button
|
||||||
|
onClick={(): void =>
|
||||||
|
onClickUpgradeHandler(
|
||||||
|
'https://signoz.io/docs/operate/docker-standalone/#upgrade',
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{t('read_how_to_upgrade')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Version;
|
8
frontend/src/container/Version/styles.ts
Normal file
8
frontend/src/container/Version/styles.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Input } from 'antd';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
export const InputComponent = styled(Input)`
|
||||||
|
&&& {
|
||||||
|
max-width: 183px;
|
||||||
|
}
|
||||||
|
`;
|
8
frontend/src/pages/Status/index.tsx
Normal file
8
frontend/src/pages/Status/index.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import Version from 'container/Version';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
function Status(): JSX.Element {
|
||||||
|
return <Version />;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Status;
|
@ -7,6 +7,10 @@ import {
|
|||||||
LOGGED_IN,
|
LOGGED_IN,
|
||||||
SIDEBAR_COLLAPSE,
|
SIDEBAR_COLLAPSE,
|
||||||
SWITCH_DARK_MODE,
|
SWITCH_DARK_MODE,
|
||||||
|
UPDATE_CURRENT_ERROR,
|
||||||
|
UPDATE_CURRENT_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION,
|
||||||
|
UPDATE_LATEST_VERSION_ERROR,
|
||||||
} from 'types/actions/app';
|
} from 'types/actions/app';
|
||||||
import InitialValueTypes from 'types/reducer/app';
|
import InitialValueTypes from 'types/reducer/app';
|
||||||
|
|
||||||
@ -14,6 +18,10 @@ const InitialValue: InitialValueTypes = {
|
|||||||
isDarkMode: getTheme() === 'darkMode',
|
isDarkMode: getTheme() === 'darkMode',
|
||||||
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
isLoggedIn: getLocalStorageKey(IS_LOGGED_IN) === 'yes',
|
||||||
isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
isSideBarCollapsed: getLocalStorageKey(IS_SIDEBAR_COLLAPSED) === 'true',
|
||||||
|
currentVersion: '',
|
||||||
|
latestVersion: '',
|
||||||
|
isCurrentVersionError: false,
|
||||||
|
isLatestVersionError: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const appReducer = (
|
const appReducer = (
|
||||||
@ -42,6 +50,28 @@ const appReducer = (
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case UPDATE_CURRENT_VERSION: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
currentVersion: action.payload.currentVersion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_LATEST_VERSION: {
|
||||||
|
return { ...state, latestVersion: action.payload.latestVersion };
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_CURRENT_ERROR: {
|
||||||
|
return { ...state, isCurrentVersionError: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
case UPDATE_LATEST_VERSION_ERROR: {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
isLatestVersionError: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
|
import AppReducer from 'types/reducer/app';
|
||||||
|
|
||||||
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
export const SWITCH_DARK_MODE = 'SWITCH_DARK_MODE';
|
||||||
export const LOGGED_IN = 'LOGGED_IN';
|
export const LOGGED_IN = 'LOGGED_IN';
|
||||||
export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE';
|
export const SIDEBAR_COLLAPSE = 'SIDEBAR_COLLAPSE';
|
||||||
|
|
||||||
|
export const UPDATE_CURRENT_VERSION = 'UPDATE_CURRENT_VERSION';
|
||||||
|
export const UPDATE_LATEST_VERSION = 'UPDATE_LATEST_VERSION';
|
||||||
|
|
||||||
|
export const UPDATE_CURRENT_ERROR = 'UPDATE_CURRENT_ERROR';
|
||||||
|
export const UPDATE_LATEST_VERSION_ERROR = 'UPDATE_LATEST_VERSION_ERROR';
|
||||||
|
|
||||||
export interface SwitchDarkMode {
|
export interface SwitchDarkMode {
|
||||||
type: typeof SWITCH_DARK_MODE;
|
type: typeof SWITCH_DARK_MODE;
|
||||||
}
|
}
|
||||||
@ -15,4 +23,31 @@ export interface SideBarCollapse {
|
|||||||
payload: boolean;
|
payload: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type AppAction = SwitchDarkMode | LoggedInUser | SideBarCollapse;
|
export interface UpdateAppVersion {
|
||||||
|
type: typeof UPDATE_CURRENT_VERSION;
|
||||||
|
payload: {
|
||||||
|
currentVersion: AppReducer['currentVersion'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateLatestVersion {
|
||||||
|
type: typeof UPDATE_LATEST_VERSION;
|
||||||
|
payload: {
|
||||||
|
latestVersion: AppReducer['latestVersion'];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface UpdateVersionError {
|
||||||
|
type: typeof UPDATE_CURRENT_ERROR | typeof UPDATE_LATEST_VERSION_ERROR;
|
||||||
|
payload: {
|
||||||
|
isError: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type AppAction =
|
||||||
|
| SwitchDarkMode
|
||||||
|
| LoggedInUser
|
||||||
|
| SideBarCollapse
|
||||||
|
| UpdateAppVersion
|
||||||
|
| UpdateLatestVersion
|
||||||
|
| UpdateVersionError;
|
||||||
|
18
frontend/src/types/api/user/getLatestVersion.ts
Normal file
18
frontend/src/types/api/user/getLatestVersion.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export interface PayloadProps {
|
||||||
|
body: string;
|
||||||
|
created_at: string;
|
||||||
|
draft: boolean;
|
||||||
|
html_url: string;
|
||||||
|
id: number;
|
||||||
|
mentions_count: number;
|
||||||
|
name: string;
|
||||||
|
node_id: number;
|
||||||
|
prerelease: boolean;
|
||||||
|
published_at: string;
|
||||||
|
tag_name: number;
|
||||||
|
tarball_url: string;
|
||||||
|
target_commitish: string;
|
||||||
|
upload_url: string;
|
||||||
|
url: string;
|
||||||
|
zipball_url: string;
|
||||||
|
}
|
@ -2,4 +2,8 @@ export default interface AppReducer {
|
|||||||
isDarkMode: boolean;
|
isDarkMode: boolean;
|
||||||
isLoggedIn: boolean;
|
isLoggedIn: boolean;
|
||||||
isSideBarCollapsed: boolean;
|
isSideBarCollapsed: boolean;
|
||||||
|
currentVersion: string;
|
||||||
|
latestVersion: string;
|
||||||
|
isCurrentVersionError: boolean;
|
||||||
|
isLatestVersionError: boolean;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user