mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 19:26:01 +08:00
fix: update version check login and other minor UI fixes (#3759)
* fix: update version check login and other minor UI fixes * fix: update text in billing page * fix: remove useEffect and replace with onSuccess and fix remaining days bug
This commit is contained in:
parent
53dee57e17
commit
6e20fbb174
@ -17,6 +17,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { License } from 'types/api/licenses/def';
|
import { License } from 'types/api/licenses/def';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import { getFormattedDate } from 'utils/timeUtils';
|
||||||
|
|
||||||
interface DataType {
|
interface DataType {
|
||||||
key: string;
|
key: string;
|
||||||
@ -107,19 +108,6 @@ export const getRemainingDays = (billingEndDate: number): number => {
|
|||||||
return Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
|
return Math.ceil(timeDifference / (1000 * 60 * 60 * 24));
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getFormattedDate = (date?: number): string => {
|
|
||||||
if (!date) {
|
|
||||||
return new Date().toLocaleDateString();
|
|
||||||
}
|
|
||||||
const trialEndDate = new Date(date * 1000);
|
|
||||||
|
|
||||||
const options = { day: 'numeric', month: 'short', year: 'numeric' };
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
||||||
// @ts-ignore
|
|
||||||
return trialEndDate.toLocaleDateString(undefined, options);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function BillingContainer(): JSX.Element {
|
export default function BillingContainer(): JSX.Element {
|
||||||
const daysRemainingStr = 'days remaining in your billing period.';
|
const daysRemainingStr = 'days remaining in your billing period.';
|
||||||
const [headerText, setHeaderText] = useState('');
|
const [headerText, setHeaderText] = useState('');
|
||||||
@ -138,35 +126,6 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
|
|
||||||
const handleError = useAxiosError();
|
const handleError = useAxiosError();
|
||||||
|
|
||||||
const { isLoading, data: usageData } = useQuery(
|
|
||||||
[REACT_QUERY_KEY.GET_BILLING_USAGE, user?.userId],
|
|
||||||
{
|
|
||||||
queryFn: () => getUsage(activeLicense?.key || ''),
|
|
||||||
onError: handleError,
|
|
||||||
enabled: activeLicense !== null,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const activeValidLicense =
|
|
||||||
licensesData?.payload?.licenses?.find(
|
|
||||||
(license) => license.isCurrent === true,
|
|
||||||
) || null;
|
|
||||||
|
|
||||||
setActiveLicense(activeValidLicense);
|
|
||||||
|
|
||||||
if (!isFetching && licensesData?.payload?.onTrial && !licenseError) {
|
|
||||||
setIsFreeTrial(true);
|
|
||||||
setBillAmount(0);
|
|
||||||
setDaysRemaining(getRemainingDays(licensesData?.payload?.trialEnd));
|
|
||||||
setHeaderText(
|
|
||||||
`You are in free trial period. Your free trial will end on ${getFormattedDate(
|
|
||||||
licensesData?.payload?.trialEnd,
|
|
||||||
)}`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [isFetching, licensesData?.payload, licenseError]);
|
|
||||||
|
|
||||||
const processUsageData = useCallback(
|
const processUsageData = useCallback(
|
||||||
(data: any): void => {
|
(data: any): void => {
|
||||||
const {
|
const {
|
||||||
@ -202,23 +161,51 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
setTotalBillAmount(total);
|
setTotalBillAmount(total);
|
||||||
|
|
||||||
if (!licensesData?.payload?.onTrial) {
|
if (!licensesData?.payload?.onTrial) {
|
||||||
|
const remainingDays = getRemainingDays(billingPeriodEnd) - 1;
|
||||||
|
|
||||||
setHeaderText(
|
setHeaderText(
|
||||||
`Your current billing period is from ${getFormattedDate(
|
`Your current billing period is from ${getFormattedDate(
|
||||||
billingPeriodStart,
|
billingPeriodStart,
|
||||||
)} to ${getFormattedDate(billingPeriodEnd)}`,
|
)} to ${getFormattedDate(billingPeriodEnd)}`,
|
||||||
);
|
);
|
||||||
setDaysRemaining(getRemainingDays(billingPeriodEnd) - 1);
|
setDaysRemaining(remainingDays > 0 ? remainingDays : 0);
|
||||||
setBillAmount(billTotal);
|
setBillAmount(billTotal);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[licensesData?.payload?.onTrial],
|
[licensesData?.payload?.onTrial],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { isLoading } = useQuery(
|
||||||
|
[REACT_QUERY_KEY.GET_BILLING_USAGE, user?.userId],
|
||||||
|
{
|
||||||
|
queryFn: () => getUsage(activeLicense?.key || ''),
|
||||||
|
onError: handleError,
|
||||||
|
enabled: activeLicense !== null,
|
||||||
|
onSuccess: processUsageData,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isLoading && usageData) {
|
const activeValidLicense =
|
||||||
processUsageData(usageData);
|
licensesData?.payload?.licenses?.find(
|
||||||
|
(license) => license.isCurrent === true,
|
||||||
|
) || null;
|
||||||
|
|
||||||
|
setActiveLicense(activeValidLicense);
|
||||||
|
|
||||||
|
if (!isFetching && licensesData?.payload?.onTrial && !licenseError) {
|
||||||
|
const remainingDays = getRemainingDays(licensesData?.payload?.trialEnd);
|
||||||
|
|
||||||
|
setIsFreeTrial(true);
|
||||||
|
setBillAmount(0);
|
||||||
|
setDaysRemaining(remainingDays > 0 ? remainingDays : 0);
|
||||||
|
setHeaderText(
|
||||||
|
`You are in free trial period. Your free trial will end on ${getFormattedDate(
|
||||||
|
licensesData?.payload?.trialEnd,
|
||||||
|
)}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}, [isLoading, processUsageData, usageData]);
|
}, [isFetching, licensesData?.payload, licenseError]);
|
||||||
|
|
||||||
const columns: ColumnsType<DataType> = [
|
const columns: ColumnsType<DataType> = [
|
||||||
{
|
{
|
||||||
@ -402,7 +389,7 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Typography.Text className="plan-benefit">
|
<Typography.Text className="plan-benefit">
|
||||||
<CheckCircleOutlined />
|
<CheckCircleOutlined />
|
||||||
You will be charged only when trial period ends
|
Your billing will start only after the trial period
|
||||||
</Typography.Text>
|
</Typography.Text>
|
||||||
<Typography.Text className="plan-benefit">
|
<Typography.Text className="plan-benefit">
|
||||||
<CheckCircleOutlined />
|
<CheckCircleOutlined />
|
||||||
|
@ -8,10 +8,7 @@ import {
|
|||||||
import { Button, Divider, MenuProps, Space, Typography } from 'antd';
|
import { Button, Divider, MenuProps, Space, Typography } from 'antd';
|
||||||
import { Logout } from 'api/utils';
|
import { Logout } from 'api/utils';
|
||||||
import ROUTES from 'constants/routes';
|
import ROUTES from 'constants/routes';
|
||||||
import {
|
import { getRemainingDays } from 'container/BillingContainer/BillingContainer';
|
||||||
getFormattedDate,
|
|
||||||
getRemainingDays,
|
|
||||||
} from 'container/BillingContainer/BillingContainer';
|
|
||||||
import Config from 'container/ConfigDropdown';
|
import Config from 'container/ConfigDropdown';
|
||||||
import { useIsDarkMode, useThemeMode } from 'hooks/useDarkMode';
|
import { useIsDarkMode, useThemeMode } from 'hooks/useDarkMode';
|
||||||
import useLicense, { LICENSE_PLAN_STATUS } from 'hooks/useLicense';
|
import useLicense, { LICENSE_PLAN_STATUS } from 'hooks/useLicense';
|
||||||
@ -29,6 +26,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink } from 'react-router-dom';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import { getFormattedDate } from 'utils/timeUtils';
|
||||||
|
|
||||||
import CurrentOrganization from './CurrentOrganization';
|
import CurrentOrganization from './CurrentOrganization';
|
||||||
import ManageLicense from './ManageLicense';
|
import ManageLicense from './ManageLicense';
|
||||||
@ -139,9 +137,12 @@ function HeaderContainer(): JSX.Element {
|
|||||||
{showTrialExpiryBanner && (
|
{showTrialExpiryBanner && (
|
||||||
<div className="trial-expiry-banner">
|
<div className="trial-expiry-banner">
|
||||||
You are in free trial period. Your free trial will end on{' '}
|
You are in free trial period. Your free trial will end on{' '}
|
||||||
<span> {getFormattedDate(licenseData?.payload?.trialEnd)}. </span>
|
<span>
|
||||||
|
{getFormattedDate(licenseData?.payload?.trialEnd || Date.now())}.
|
||||||
|
</span>
|
||||||
{role === 'ADMIN' ? (
|
{role === 'ADMIN' ? (
|
||||||
<span>
|
<span>
|
||||||
|
{' '}
|
||||||
Please{' '}
|
Please{' '}
|
||||||
<Button className="upgrade-link" type="link" onClick={handleUpgrade}>
|
<Button className="upgrade-link" type="link" onClick={handleUpgrade}>
|
||||||
upgrade
|
upgrade
|
||||||
|
@ -13,7 +13,7 @@ import { useLocation } from 'react-router-dom';
|
|||||||
import { sideBarCollapse } from 'store/actions/app';
|
import { sideBarCollapse } from 'store/actions/app';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
import { isCloudUser } from 'utils/app';
|
import { checkVersionState, isCloudUser } from 'utils/app';
|
||||||
|
|
||||||
import { routeConfig, styles } from './config';
|
import { routeConfig, styles } from './config';
|
||||||
import { getQueryString } from './helper';
|
import { getQueryString } from './helper';
|
||||||
@ -110,22 +110,12 @@ function SideNav(): JSX.Element {
|
|||||||
history.push(ROUTES.VERSION);
|
history.push(ROUTES.VERSION);
|
||||||
};
|
};
|
||||||
|
|
||||||
const checkVersionState = (): boolean => {
|
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
||||||
const versionCore = currentVersion?.split('-')[0];
|
|
||||||
|
|
||||||
if (versionCore) {
|
|
||||||
return versionCore !== latestVersion;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const isNotCurrentVersion = checkVersionState();
|
|
||||||
|
|
||||||
const secondaryMenuItems: MenuItem[] = [
|
const secondaryMenuItems: MenuItem[] = [
|
||||||
{
|
{
|
||||||
key: SecondaryMenuItemKey.Version,
|
key: SecondaryMenuItemKey.Version,
|
||||||
icon: isNotCurrentVersion ? (
|
icon: !isLatestVersion ? (
|
||||||
<WarningOutlined style={{ color: '#E87040' }} />
|
<WarningOutlined style={{ color: '#E87040' }} />
|
||||||
) : (
|
) : (
|
||||||
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
|
<CheckCircleTwoTone twoToneColor={['#D5F2BB', '#1f1f1f']} />
|
||||||
@ -135,7 +125,7 @@ function SideNav(): JSX.Element {
|
|||||||
<StyledText ellipsis>
|
<StyledText ellipsis>
|
||||||
{!isCurrentVersionError ? currentVersion : t('n_a')}
|
{!isCurrentVersionError ? currentVersion : t('n_a')}
|
||||||
</StyledText>
|
</StyledText>
|
||||||
{isNotCurrentVersion && <RedDot />}
|
{!isLatestVersion && <RedDot />}
|
||||||
</MenuLabelContainer>
|
</MenuLabelContainer>
|
||||||
),
|
),
|
||||||
onClick: onClickVersionHandler,
|
onClick: onClickVersionHandler,
|
||||||
|
@ -5,6 +5,7 @@ import { useTranslation } from 'react-i18next';
|
|||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import { checkVersionState } from 'utils/app';
|
||||||
|
|
||||||
import { githubReleaseURL } from './constant';
|
import { githubReleaseURL } from './constant';
|
||||||
import { InputComponent } from './styles';
|
import { InputComponent } from './styles';
|
||||||
@ -20,7 +21,8 @@ function Version(): JSX.Element {
|
|||||||
isLatestVersionError,
|
isLatestVersionError,
|
||||||
} = useSelector<AppState, AppReducer>((state) => state.app);
|
} = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const isLatestVersion = currentVersion === latestVersion;
|
const isLatestVersion = checkVersionState(currentVersion, latestVersion);
|
||||||
|
|
||||||
const isError = isCurrentVersionError || isLatestVersionError;
|
const isError = isCurrentVersionError || isLatestVersionError;
|
||||||
|
|
||||||
const latestVersionUrl = useMemo(
|
const latestVersionUrl = useMemo(
|
||||||
|
@ -5,7 +5,6 @@ import { CreditCardOutlined, LockOutlined } from '@ant-design/icons';
|
|||||||
import { Button, Card, Typography } from 'antd';
|
import { Button, Card, Typography } from 'antd';
|
||||||
import updateCreditCardApi from 'api/billing/checkout';
|
import updateCreditCardApi from 'api/billing/checkout';
|
||||||
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
import { SOMETHING_WENT_WRONG } from 'constants/api';
|
||||||
import { getFormattedDate } from 'container/BillingContainer/BillingContainer';
|
|
||||||
import useLicense from 'hooks/useLicense';
|
import useLicense from 'hooks/useLicense';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { useCallback, useEffect, useState } from 'react';
|
import { useCallback, useEffect, useState } from 'react';
|
||||||
@ -14,6 +13,7 @@ import { useSelector } from 'react-redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import { License } from 'types/api/licenses/def';
|
import { License } from 'types/api/licenses/def';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer from 'types/reducer/app';
|
||||||
|
import { getFormattedDate } from 'utils/timeUtils';
|
||||||
|
|
||||||
export default function WorkspaceBlocked(): JSX.Element {
|
export default function WorkspaceBlocked(): JSX.Element {
|
||||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
@ -68,8 +68,8 @@ export default function WorkspaceBlocked(): JSX.Element {
|
|||||||
<Typography.Paragraph className="workpace-locked-details">
|
<Typography.Paragraph className="workpace-locked-details">
|
||||||
You have been locked out of your workspace because your trial ended without
|
You have been locked out of your workspace because your trial ended without
|
||||||
an upgrade to a paid plan. Your data will continue to be ingested till{' '}
|
an upgrade to a paid plan. Your data will continue to be ingested till{' '}
|
||||||
{getFormattedDate(licensesData?.payload?.gracePeriodEnd)} , at which point
|
{getFormattedDate(licensesData?.payload?.gracePeriodEnd || Date.now())} , at
|
||||||
we will drop all the ingested data and terminate the account.
|
which point we will drop all the ingested data and terminate the account.
|
||||||
{!isAdmin && 'Please contact your administrator for further help'}
|
{!isAdmin && 'Please contact your administrator for further help'}
|
||||||
</Typography.Paragraph>
|
</Typography.Paragraph>
|
||||||
|
|
||||||
|
@ -17,3 +17,11 @@ export const isCloudUser = (): boolean => {
|
|||||||
|
|
||||||
return hostname?.endsWith('signoz.cloud');
|
return hostname?.endsWith('signoz.cloud');
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const checkVersionState = (
|
||||||
|
currentVersion: string,
|
||||||
|
latestVersion: string,
|
||||||
|
): boolean => {
|
||||||
|
const versionCore = currentVersion?.split('-')[0];
|
||||||
|
return versionCore === latestVersion;
|
||||||
|
};
|
||||||
|
@ -1,4 +1,17 @@
|
|||||||
|
import dayjs from 'dayjs';
|
||||||
|
import customParseFormat from 'dayjs/plugin/customParseFormat';
|
||||||
|
|
||||||
|
dayjs.extend(customParseFormat);
|
||||||
|
|
||||||
export function toUTCEpoch(time: number): number {
|
export function toUTCEpoch(time: number): number {
|
||||||
const x = new Date();
|
const x = new Date();
|
||||||
return time + x.getTimezoneOffset() * 60 * 1000;
|
return time + x.getTimezoneOffset() * 60 * 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getFormattedDate = (epochTimestamp: number): string => {
|
||||||
|
// Convert epoch timestamp to a date
|
||||||
|
const date = dayjs.unix(epochTimestamp);
|
||||||
|
|
||||||
|
// Format the date as "18 Nov 2013"
|
||||||
|
return date.format('DD MMM YYYY');
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user