mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-17 18:21:32 +08:00
feat: update ui based on license states (#7328)
* feat: update ui based on license states * feat: update license based ui messaging (#7330) * fix: billing test cases
This commit is contained in:
parent
c26277cd42
commit
c81760bdf7
@ -40,6 +40,7 @@
|
||||
"LIST_LICENSES": "SigNoz | List of Licenses",
|
||||
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
||||
"WORKSPACE_SUSPENDED": "SigNoz | Workspace Suspended",
|
||||
"WORKSPACE_ACCESS_RESTRICTED": "SigNoz | Workspace Access Restricted",
|
||||
"SUPPORT": "SigNoz | Support",
|
||||
"DEFAULT": "Open source Observability Platform | SigNoz",
|
||||
"ALERT_HISTORY": "SigNoz | Alert Rule History",
|
||||
|
@ -50,6 +50,7 @@
|
||||
"LIST_LICENSES": "SigNoz | List of Licenses",
|
||||
"WORKSPACE_LOCKED": "SigNoz | Workspace Locked",
|
||||
"WORKSPACE_SUSPENDED": "SigNoz | Workspace Suspended",
|
||||
"WORKSPACE_ACCESS_RESTRICTED": "SigNoz | Workspace Access Restricted",
|
||||
"SUPPORT": "SigNoz | Support",
|
||||
"LOGS_SAVE_VIEWS": "SigNoz | Logs Saved Views",
|
||||
"TRACES_SAVE_VIEWS": "SigNoz | Traces Saved Views",
|
||||
|
@ -11,7 +11,7 @@ import { useAppContext } from 'providers/App/App';
|
||||
import { ReactChild, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useQuery } from 'react-query';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { LicenseState, LicenseStatus } from 'types/api/licensesV3/getActive';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { routePermission } from 'utils/permission';
|
||||
@ -33,10 +33,9 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
user,
|
||||
isLoggedIn: isLoggedInState,
|
||||
isFetchingOrgPreferences,
|
||||
licenses,
|
||||
isFetchingLicenses,
|
||||
activeLicenseV3,
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo,
|
||||
featureFlags,
|
||||
} = useAppContext();
|
||||
|
||||
@ -133,17 +132,53 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingLicenses) {
|
||||
const currentRoute = mapRoutes.get('current');
|
||||
const shouldBlockWorkspace = licenses?.workSpaceBlock;
|
||||
const navigateToWorkSpaceAccessRestricted = (route: any): void => {
|
||||
const { path } = route;
|
||||
|
||||
if (shouldBlockWorkspace && currentRoute) {
|
||||
if (path && path !== ROUTES.WORKSPACE_ACCESS_RESTRICTED) {
|
||||
history.push(ROUTES.WORKSPACE_ACCESS_RESTRICTED);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3) {
|
||||
const currentRoute = mapRoutes.get('current');
|
||||
|
||||
const isTerminated = activeLicenseV3.state === LicenseState.TERMINATED;
|
||||
const isExpired = activeLicenseV3.state === LicenseState.EXPIRED;
|
||||
const isCancelled = activeLicenseV3.state === LicenseState.CANCELLED;
|
||||
|
||||
const isWorkspaceAccessRestricted = isTerminated || isExpired || isCancelled;
|
||||
|
||||
const { platform } = activeLicenseV3;
|
||||
|
||||
if (isWorkspaceAccessRestricted && platform === LicensePlatform.CLOUD) {
|
||||
navigateToWorkSpaceAccessRestricted(currentRoute);
|
||||
}
|
||||
}
|
||||
}, [isFetchingActiveLicenseV3, activeLicenseV3, mapRoutes, pathname]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingActiveLicenseV3) {
|
||||
const currentRoute = mapRoutes.get('current');
|
||||
const shouldBlockWorkspace = trialInfo?.workSpaceBlock;
|
||||
|
||||
if (
|
||||
shouldBlockWorkspace &&
|
||||
currentRoute &&
|
||||
activeLicenseV3?.platform === LicensePlatform.CLOUD
|
||||
) {
|
||||
navigateToWorkSpaceBlocked(currentRoute);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isFetchingLicenses, licenses?.workSpaceBlock, mapRoutes, pathname]);
|
||||
}, [
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo?.workSpaceBlock,
|
||||
activeLicenseV3?.platform,
|
||||
mapRoutes,
|
||||
pathname,
|
||||
]);
|
||||
|
||||
const navigateToWorkSpaceSuspended = (route: any): void => {
|
||||
const { path } = route;
|
||||
@ -157,10 +192,13 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3) {
|
||||
const currentRoute = mapRoutes.get('current');
|
||||
const shouldSuspendWorkspace =
|
||||
activeLicenseV3.status === LicenseStatus.SUSPENDED &&
|
||||
activeLicenseV3.state === LicenseState.DEFAULTED;
|
||||
|
||||
if (shouldSuspendWorkspace && currentRoute) {
|
||||
if (
|
||||
shouldSuspendWorkspace &&
|
||||
currentRoute &&
|
||||
activeLicenseV3.platform === LicensePlatform.CLOUD
|
||||
) {
|
||||
navigateToWorkSpaceSuspended(currentRoute);
|
||||
}
|
||||
}
|
||||
@ -236,15 +274,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
setLocalStorageApi(LOCALSTORAGE.UNAUTHENTICATED_ROUTE_HIT, pathname);
|
||||
history.push(ROUTES.LOGIN);
|
||||
}
|
||||
}, [
|
||||
licenses,
|
||||
isLoggedInState,
|
||||
pathname,
|
||||
user,
|
||||
isOldRoute,
|
||||
currentRoute,
|
||||
location,
|
||||
]);
|
||||
}, [isLoggedInState, pathname, user, isOldRoute, currentRoute, location]);
|
||||
|
||||
// NOTE: disabling this rule as there is no need to have div
|
||||
// eslint-disable-next-line react/jsx-no-useless-fragment
|
||||
|
@ -41,6 +41,10 @@ function App(): JSX.Element {
|
||||
isFetchingUser,
|
||||
isFetchingLicenses,
|
||||
isFetchingFeatureFlags,
|
||||
trialInfo,
|
||||
activeLicenseV3,
|
||||
isFetchingActiveLicenseV3,
|
||||
activeLicenseV3FetchError,
|
||||
userFetchError,
|
||||
licensesFetchError,
|
||||
featureFlagsFetchError,
|
||||
@ -60,7 +64,7 @@ function App(): JSX.Element {
|
||||
const enableAnalytics = useCallback(
|
||||
(user: IUser): void => {
|
||||
// wait for the required data to be loaded before doing init for anything!
|
||||
if (!isFetchingLicenses && licenses && org) {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3 && org) {
|
||||
const orgName =
|
||||
org && Array.isArray(org) && org.length > 0 ? org[0].name : '';
|
||||
|
||||
@ -107,7 +111,7 @@ function App(): JSX.Element {
|
||||
tenant_url: hostname,
|
||||
company_domain: domain,
|
||||
source: 'signoz-ui',
|
||||
isPaidUser: !!licenses?.trialConvertedToSubscription,
|
||||
isPaidUser: !!trialInfo?.trialConvertedToSubscription,
|
||||
});
|
||||
|
||||
posthog?.group('company', domain, {
|
||||
@ -117,7 +121,7 @@ function App(): JSX.Element {
|
||||
tenant_url: hostname,
|
||||
company_domain: domain,
|
||||
source: 'signoz-ui',
|
||||
isPaidUser: !!licenses?.trialConvertedToSubscription,
|
||||
isPaidUser: !!trialInfo?.trialConvertedToSubscription,
|
||||
});
|
||||
|
||||
if (
|
||||
@ -133,7 +137,13 @@ function App(): JSX.Element {
|
||||
}
|
||||
}
|
||||
},
|
||||
[hostname, isFetchingLicenses, licenses, org],
|
||||
[
|
||||
hostname,
|
||||
isFetchingActiveLicenseV3,
|
||||
activeLicenseV3,
|
||||
org,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
],
|
||||
);
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
@ -206,7 +216,9 @@ function App(): JSX.Element {
|
||||
if (
|
||||
!isFetchingFeatureFlags &&
|
||||
(featureFlags || featureFlagsFetchError) &&
|
||||
licenses
|
||||
licenses &&
|
||||
activeLicenseV3 &&
|
||||
trialInfo
|
||||
) {
|
||||
let isChatSupportEnabled = false;
|
||||
let isPremiumSupportEnabled = false;
|
||||
@ -220,7 +232,7 @@ function App(): JSX.Element {
|
||||
?.active || false;
|
||||
}
|
||||
const showAddCreditCardModal =
|
||||
!isPremiumSupportEnabled && !licenses.trialConvertedToSubscription;
|
||||
!isPremiumSupportEnabled && !trialInfo?.trialConvertedToSubscription;
|
||||
|
||||
if (isLoggedInState && isChatSupportEnabled && !showAddCreditCardModal) {
|
||||
window.Intercom('boot', {
|
||||
@ -234,11 +246,13 @@ function App(): JSX.Element {
|
||||
isLoggedInState,
|
||||
user,
|
||||
pathname,
|
||||
licenses?.trialConvertedToSubscription,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
featureFlags,
|
||||
isFetchingFeatureFlags,
|
||||
featureFlagsFetchError,
|
||||
licenses,
|
||||
activeLicenseV3,
|
||||
trialInfo,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -250,7 +264,12 @@ function App(): JSX.Element {
|
||||
// if the user is in logged in state
|
||||
if (isLoggedInState) {
|
||||
// if the setup calls are loading then return a spinner
|
||||
if (isFetchingLicenses || isFetchingUser || isFetchingFeatureFlags) {
|
||||
if (
|
||||
isFetchingLicenses ||
|
||||
isFetchingUser ||
|
||||
isFetchingFeatureFlags ||
|
||||
isFetchingActiveLicenseV3
|
||||
) {
|
||||
return <Spinner tip="Loading..." />;
|
||||
}
|
||||
|
||||
@ -258,7 +277,7 @@ function App(): JSX.Element {
|
||||
// this needs to be on top of data missing error because if there is an error, data will never be loaded and it will
|
||||
// move to indefinitive loading
|
||||
if (
|
||||
(userFetchError || licensesFetchError) &&
|
||||
(userFetchError || licensesFetchError || activeLicenseV3FetchError) &&
|
||||
pathname !== ROUTES.SOMETHING_WENT_WRONG
|
||||
) {
|
||||
history.replace(ROUTES.SOMETHING_WENT_WRONG);
|
||||
@ -268,7 +287,8 @@ function App(): JSX.Element {
|
||||
if (
|
||||
(!licenses || !user.email || !featureFlags) &&
|
||||
!userFetchError &&
|
||||
!licensesFetchError
|
||||
!licensesFetchError &&
|
||||
!activeLicenseV3FetchError
|
||||
) {
|
||||
return <Spinner tip="Loading..." />;
|
||||
}
|
||||
|
@ -229,6 +229,13 @@ export const WorkspaceSuspended = Loadable(
|
||||
),
|
||||
);
|
||||
|
||||
export const WorkspaceAccessRestricted = Loadable(
|
||||
() =>
|
||||
import(
|
||||
/* webpackChunkName: "WorkspaceAccessRestricted" */ 'pages/WorkspaceAccessRestricted'
|
||||
),
|
||||
);
|
||||
|
||||
export const ShortcutsPage = Loadable(
|
||||
() => import(/* webpackChunkName: "ShortcutsPage" */ 'pages/Shortcuts'),
|
||||
);
|
||||
|
@ -55,6 +55,7 @@ import {
|
||||
TracesSaveViews,
|
||||
UnAuthorized,
|
||||
UsageExplorerPage,
|
||||
WorkspaceAccessRestricted,
|
||||
WorkspaceBlocked,
|
||||
WorkspaceSuspended,
|
||||
} from './pageComponents';
|
||||
@ -396,6 +397,13 @@ const routes: AppRoutes[] = [
|
||||
isPrivate: true,
|
||||
key: 'WORKSPACE_SUSPENDED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.WORKSPACE_ACCESS_RESTRICTED,
|
||||
exact: true,
|
||||
component: WorkspaceAccessRestricted,
|
||||
isPrivate: true,
|
||||
key: 'WORKSPACE_ACCESS_RESTRICTED',
|
||||
},
|
||||
{
|
||||
path: ROUTES.SHORTCUTS,
|
||||
exact: true,
|
||||
|
@ -40,7 +40,7 @@ function LaunchChatSupport({
|
||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
||||
const { notifications } = useNotifications();
|
||||
const {
|
||||
licenses,
|
||||
trialInfo,
|
||||
featureFlags,
|
||||
isFetchingFeatureFlags,
|
||||
featureFlagsFetchError,
|
||||
@ -70,7 +70,7 @@ function LaunchChatSupport({
|
||||
if (
|
||||
!isFetchingFeatureFlags &&
|
||||
(featureFlags || featureFlagsFetchError) &&
|
||||
licenses
|
||||
trialInfo
|
||||
) {
|
||||
let isChatSupportEnabled = false;
|
||||
let isPremiumSupportEnabled = false;
|
||||
@ -87,7 +87,7 @@ function LaunchChatSupport({
|
||||
isLoggedIn &&
|
||||
!isPremiumSupportEnabled &&
|
||||
isChatSupportEnabled &&
|
||||
!licenses.trialConvertedToSubscription &&
|
||||
!trialInfo.trialConvertedToSubscription &&
|
||||
isCloudUserVal
|
||||
);
|
||||
}
|
||||
@ -98,7 +98,7 @@ function LaunchChatSupport({
|
||||
isCloudUserVal,
|
||||
isFetchingFeatureFlags,
|
||||
isLoggedIn,
|
||||
licenses,
|
||||
trialInfo,
|
||||
]);
|
||||
|
||||
const handleFacingIssuesClick = (): void => {
|
||||
|
@ -69,6 +69,7 @@ const ROUTES = {
|
||||
METRICS_EXPLORER: '/metrics-explorer/summary',
|
||||
METRICS_EXPLORER_EXPLORER: '/metrics-explorer/explorer',
|
||||
METRICS_EXPLORER_VIEWS: '/metrics-explorer/views',
|
||||
WORKSPACE_ACCESS_RESTRICTED: '/workspace-access-restricted',
|
||||
HOME_PAGE: '/',
|
||||
} as const;
|
||||
|
||||
|
@ -106,7 +106,8 @@
|
||||
}
|
||||
|
||||
.trial-expiry-banner,
|
||||
.slow-api-warning-banner {
|
||||
.slow-api-warning-banner,
|
||||
.workspace-restricted-banner {
|
||||
padding: 8px;
|
||||
background-color: #f25733;
|
||||
color: white;
|
||||
|
@ -53,7 +53,11 @@ import {
|
||||
} from 'types/actions/app';
|
||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
||||
import { CheckoutSuccessPayloadProps } from 'types/api/billing/checkout';
|
||||
import { LicenseEvent } from 'types/api/licensesV3/getActive';
|
||||
import {
|
||||
LicenseEvent,
|
||||
LicensePlatform,
|
||||
LicenseState,
|
||||
} from 'types/api/licensesV3/getActive';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { eventEmitter } from 'utils/getEventEmitter';
|
||||
import {
|
||||
@ -70,8 +74,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
const {
|
||||
isLoggedIn,
|
||||
user,
|
||||
licenses,
|
||||
isFetchingLicenses,
|
||||
trialInfo,
|
||||
activeLicenseV3,
|
||||
isFetchingActiveLicenseV3,
|
||||
featureFlags,
|
||||
@ -253,18 +256,47 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
|
||||
const [showTrialExpiryBanner, setShowTrialExpiryBanner] = useState(false);
|
||||
|
||||
const [showWorkspaceRestricted, setShowWorkspaceRestricted] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isFetchingLicenses &&
|
||||
licenses &&
|
||||
licenses.onTrial &&
|
||||
!licenses.trialConvertedToSubscription &&
|
||||
!licenses.workSpaceBlock &&
|
||||
getRemainingDays(licenses.trialEnd) < 7
|
||||
!isFetchingActiveLicenseV3 &&
|
||||
activeLicenseV3 &&
|
||||
trialInfo?.onTrial &&
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
!trialInfo?.workSpaceBlock &&
|
||||
getRemainingDays(trialInfo?.trialEnd) < 7
|
||||
) {
|
||||
setShowTrialExpiryBanner(true);
|
||||
}
|
||||
}, [isFetchingLicenses, licenses]);
|
||||
}, [isFetchingActiveLicenseV3, activeLicenseV3, trialInfo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3) {
|
||||
const isTerminated = activeLicenseV3.state === LicenseState.TERMINATED;
|
||||
const isExpired = activeLicenseV3.state === LicenseState.EXPIRED;
|
||||
const isCancelled = activeLicenseV3.state === LicenseState.CANCELLED;
|
||||
const isDefaulted = activeLicenseV3.state === LicenseState.DEFAULTED;
|
||||
const isEvaluationExpired =
|
||||
activeLicenseV3.state === LicenseState.EVALUATION_EXPIRED;
|
||||
|
||||
const isWorkspaceAccessRestricted =
|
||||
isTerminated ||
|
||||
isExpired ||
|
||||
isCancelled ||
|
||||
isDefaulted ||
|
||||
isEvaluationExpired;
|
||||
|
||||
const { platform } = activeLicenseV3;
|
||||
|
||||
if (
|
||||
isWorkspaceAccessRestricted &&
|
||||
platform === LicensePlatform.SELF_HOSTED
|
||||
) {
|
||||
setShowWorkspaceRestricted(true);
|
||||
}
|
||||
}
|
||||
}, [isFetchingActiveLicenseV3, activeLicenseV3]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
@ -350,7 +382,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
if (
|
||||
!isFetchingFeatureFlags &&
|
||||
(featureFlags || featureFlagsFetchError) &&
|
||||
licenses
|
||||
activeLicenseV3 &&
|
||||
trialInfo
|
||||
) {
|
||||
let isChatSupportEnabled = false;
|
||||
let isPremiumSupportEnabled = false;
|
||||
@ -367,7 +400,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
isLoggedIn &&
|
||||
!isPremiumSupportEnabled &&
|
||||
isChatSupportEnabled &&
|
||||
!licenses.trialConvertedToSubscription &&
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
isCloudUserVal
|
||||
);
|
||||
}
|
||||
@ -378,7 +411,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
isCloudUserVal,
|
||||
isFetchingFeatureFlags,
|
||||
isLoggedIn,
|
||||
licenses,
|
||||
activeLicenseV3,
|
||||
trialInfo,
|
||||
]);
|
||||
|
||||
// Listen for API warnings
|
||||
@ -418,16 +452,6 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const isTrialUser = useMemo(
|
||||
(): boolean =>
|
||||
(!isFetchingLicenses &&
|
||||
licenses &&
|
||||
licenses.onTrial &&
|
||||
!licenses.trialConvertedToSubscription) ||
|
||||
false,
|
||||
[licenses, isFetchingLicenses],
|
||||
);
|
||||
|
||||
const handleDismissSlowApiWarning = (): void => {
|
||||
setShowSlowApiWarning(false);
|
||||
|
||||
@ -437,8 +461,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (
|
||||
showSlowApiWarning &&
|
||||
isTrialUser &&
|
||||
!licenses?.trialConvertedToSubscription &&
|
||||
trialInfo?.onTrial &&
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
!slowApiWarningShown
|
||||
) {
|
||||
setSlowApiWarningShown(true);
|
||||
@ -478,15 +502,86 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
}, [
|
||||
showSlowApiWarning,
|
||||
notifications,
|
||||
isTrialUser,
|
||||
licenses?.trialConvertedToSubscription,
|
||||
user.role,
|
||||
isLoadingManageBilling,
|
||||
handleFailedPayment,
|
||||
slowApiWarningShown,
|
||||
handleUpgrade,
|
||||
trialInfo?.onTrial,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
]);
|
||||
|
||||
const renderWorkspaceRestrictedBanner = (): JSX.Element => (
|
||||
<div className="workspace-restricted-banner">
|
||||
{activeLicenseV3?.state === LicenseState.TERMINATED && (
|
||||
<>
|
||||
Your SigNoz license is terminated, enterprise features have been disabled.
|
||||
Please contact support at{' '}
|
||||
<a href="mailto:support@signoz.io">support@signoz.io</a> for new license
|
||||
</>
|
||||
)}
|
||||
{activeLicenseV3?.state === LicenseState.EXPIRED && (
|
||||
<>
|
||||
Your SigNoz license has expired. Please contact support at{' '}
|
||||
<a href="mailto:support@signoz.io">support@signoz.io</a> for renewal to
|
||||
avoid termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
{activeLicenseV3?.state === LicenseState.CANCELLED && (
|
||||
<>
|
||||
Your SigNoz license is cancelled. Please contact support at{' '}
|
||||
<a href="mailto:support@signoz.io">support@signoz.io</a> for reactivation
|
||||
to avoid termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeLicenseV3?.state === LicenseState.DEFAULTED && (
|
||||
<>
|
||||
Your SigNoz license is defaulted. Please clear the bill to continue using
|
||||
the enterprise features. Contact support at{' '}
|
||||
<a href="mailto:support@signoz.io">support@signoz.io</a> to avoid
|
||||
termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
|
||||
{activeLicenseV3?.state === LicenseState.EVALUATION_EXPIRED && (
|
||||
<>
|
||||
Your SigNoz trial has ended. Please contact support at{' '}
|
||||
<a href="mailto:support@signoz.io">support@signoz.io</a> for next steps to
|
||||
avoid termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<Layout className={cx(isDarkMode ? 'darkMode' : 'lightMode')}>
|
||||
<Helmet>
|
||||
@ -496,7 +591,7 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
{showTrialExpiryBanner && !showPaymentFailedWarning && (
|
||||
<div className="trial-expiry-banner">
|
||||
You are in free trial period. Your free trial will end on{' '}
|
||||
<span>{getFormattedDate(licenses?.trialEnd || Date.now())}.</span>
|
||||
<span>{getFormattedDate(trialInfo?.trialEnd || Date.now())}.</span>
|
||||
{user.role === USER_ROLES.ADMIN ? (
|
||||
<span>
|
||||
{' '}
|
||||
@ -512,6 +607,8 @@ function AppLayout(props: AppLayoutProps): JSX.Element {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{showWorkspaceRestricted && renderWorkspaceRestrictedBanner()}
|
||||
|
||||
{!showTrialExpiryBanner && showPaymentFailedWarning && (
|
||||
<div className="payment-failed-banner">
|
||||
Your bill payment has failed. Your workspace will get suspended on{' '}
|
||||
|
@ -68,7 +68,7 @@ describe('BillingContainer', () => {
|
||||
test('OnTrail', async () => {
|
||||
act(() => {
|
||||
render(<BillingContainer />, undefined, undefined, {
|
||||
licenses: licensesSuccessResponse.data,
|
||||
trialInfo: licensesSuccessResponse.data,
|
||||
});
|
||||
});
|
||||
|
||||
@ -102,7 +102,7 @@ describe('BillingContainer', () => {
|
||||
test('OnTrail but trialConvertedToSubscription', async () => {
|
||||
act(() => {
|
||||
render(<BillingContainer />, undefined, undefined, {
|
||||
licenses: trialConvertedToSubscriptionResponse.data,
|
||||
trialInfo: trialConvertedToSubscriptionResponse.data,
|
||||
});
|
||||
});
|
||||
|
||||
@ -135,7 +135,7 @@ describe('BillingContainer', () => {
|
||||
|
||||
test('Not on ontrail', async () => {
|
||||
const { findByText } = render(<BillingContainer />, undefined, undefined, {
|
||||
licenses: notOfTrailResponse.data,
|
||||
trialInfo: notOfTrailResponse.data,
|
||||
});
|
||||
|
||||
const billingPeriodText = `Your current billing period is from ${getFormattedDate(
|
||||
|
@ -138,8 +138,10 @@ export default function BillingContainer(): JSX.Element {
|
||||
user,
|
||||
org,
|
||||
licenses,
|
||||
isFetchingLicenses,
|
||||
licensesFetchError,
|
||||
trialInfo,
|
||||
isFetchingActiveLicenseV3,
|
||||
activeLicenseV3,
|
||||
activeLicenseV3FetchError,
|
||||
} = useAppContext();
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
@ -182,7 +184,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
|
||||
setData(formattedUsageData);
|
||||
|
||||
if (!licenses?.onTrial) {
|
||||
if (!trialInfo?.onTrial) {
|
||||
const remainingDays = getRemainingDays(billingPeriodEnd) - 1;
|
||||
|
||||
setHeaderText(
|
||||
@ -196,7 +198,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
|
||||
setApiResponse(data?.payload || {});
|
||||
},
|
||||
[licenses?.onTrial],
|
||||
[trialInfo?.onTrial],
|
||||
);
|
||||
|
||||
const isSubscriptionPastDue =
|
||||
@ -219,24 +221,29 @@ export default function BillingContainer(): JSX.Element {
|
||||
|
||||
setActiveLicense(activeValidLicense);
|
||||
|
||||
if (!isFetchingLicenses && licenses?.onTrial && !licensesFetchError) {
|
||||
const remainingDays = getRemainingDays(licenses?.trialEnd);
|
||||
if (
|
||||
!isFetchingActiveLicenseV3 &&
|
||||
!activeLicenseV3FetchError &&
|
||||
trialInfo?.onTrial
|
||||
) {
|
||||
const remainingDays = getRemainingDays(trialInfo?.trialEnd);
|
||||
|
||||
setIsFreeTrial(true);
|
||||
setBillAmount(0);
|
||||
setDaysRemaining(remainingDays > 0 ? remainingDays : 0);
|
||||
setHeaderText(
|
||||
`You are in free trial period. Your free trial will end on ${getFormattedDate(
|
||||
licenses?.trialEnd,
|
||||
trialInfo?.trialEnd,
|
||||
)}`,
|
||||
);
|
||||
}
|
||||
}, [
|
||||
licenses?.licenses,
|
||||
licenses?.onTrial,
|
||||
licenses?.trialEnd,
|
||||
isFetchingLicenses,
|
||||
licensesFetchError,
|
||||
activeLicenseV3,
|
||||
trialInfo?.onTrial,
|
||||
trialInfo?.trialEnd,
|
||||
isFetchingActiveLicenseV3,
|
||||
activeLicenseV3FetchError,
|
||||
]);
|
||||
|
||||
const columns: ColumnsType<DataType> = [
|
||||
@ -319,7 +326,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
});
|
||||
|
||||
const handleBilling = useCallback(async () => {
|
||||
if (!licenses?.trialConvertedToSubscription) {
|
||||
if (!trialInfo?.trialConvertedToSubscription) {
|
||||
logEvent('Billing : Upgrade Plan', {
|
||||
user: pick(user, ['email', 'userId', 'name']),
|
||||
org,
|
||||
@ -341,7 +348,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [
|
||||
isFreeTrial,
|
||||
licenses?.trialConvertedToSubscription,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
manageCreditCard,
|
||||
updateCreditCard,
|
||||
]);
|
||||
@ -409,9 +416,9 @@ export default function BillingContainer(): JSX.Element {
|
||||
|
||||
const showGracePeriodMessage =
|
||||
!isLoading &&
|
||||
!licenses?.trialConvertedToSubscription &&
|
||||
!licenses?.onTrial &&
|
||||
licenses?.gracePeriodEnd;
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
!trialInfo?.onTrial &&
|
||||
trialInfo?.gracePeriodEnd;
|
||||
|
||||
return (
|
||||
<div className="billing-container">
|
||||
@ -461,14 +468,14 @@ export default function BillingContainer(): JSX.Element {
|
||||
disabled={isLoading}
|
||||
onClick={handleBilling}
|
||||
>
|
||||
{licenses?.trialConvertedToSubscription
|
||||
{trialInfo?.trialConvertedToSubscription
|
||||
? t('manage_billing')
|
||||
: t('upgrade_plan')}
|
||||
</Button>
|
||||
</Flex>
|
||||
</Flex>
|
||||
|
||||
{licenses?.onTrial && licenses?.trialConvertedToSubscription && (
|
||||
{trialInfo?.onTrial && trialInfo?.trialConvertedToSubscription && (
|
||||
<Typography.Text
|
||||
ellipsis
|
||||
style={{ fontWeight: '300', color: '#49aa19', fontSize: 12 }}
|
||||
@ -495,11 +502,11 @@ export default function BillingContainer(): JSX.Element {
|
||||
{!isLoading &&
|
||||
!isFetchingBillingData &&
|
||||
billingData &&
|
||||
licenses?.gracePeriodEnd &&
|
||||
trialInfo?.gracePeriodEnd &&
|
||||
showGracePeriodMessage ? (
|
||||
<Alert
|
||||
message={`Your data is safe with us until ${getFormattedDate(
|
||||
licenses?.gracePeriodEnd || Date.now(),
|
||||
trialInfo?.gracePeriodEnd || Date.now(),
|
||||
)}. Please upgrade plan now to retain your data.`}
|
||||
type="info"
|
||||
showIcon
|
||||
@ -535,7 +542,7 @@ export default function BillingContainer(): JSX.Element {
|
||||
{(isLoading || isFetchingBillingData) && renderTableSkeleton()}
|
||||
</div>
|
||||
|
||||
{!licenses?.trialConvertedToSubscription && (
|
||||
{!trialInfo?.trialConvertedToSubscription && (
|
||||
<div className="upgrade-plan-benefits">
|
||||
<Row
|
||||
justify="space-between"
|
||||
|
@ -184,7 +184,6 @@ export const getMetricsTableData = (data: any): any[] => {
|
||||
const columnsData = (data?.payload.data.result[0] as any).table.columns;
|
||||
const builderQueries = data.params?.compositeQuery?.builderQueries;
|
||||
const columns = columnsData.map((columnData: any) => {
|
||||
console.log({ columnData });
|
||||
if (columnData.isValueColumn) {
|
||||
return {
|
||||
key: columnData.name,
|
||||
|
@ -33,7 +33,7 @@ function ServiceMetricTable({
|
||||
const { notifications } = useNotifications();
|
||||
const { t: getText } = useTranslation(['services']);
|
||||
|
||||
const { licenses, isFetchingLicenses } = useAppContext();
|
||||
const { isFetchingActiveLicenseV3, trialInfo } = useAppContext();
|
||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
||||
|
||||
const queries = useGetQueriesRange(queryRangeRequestData, ENTITY_VERSION_V4, {
|
||||
@ -70,9 +70,9 @@ function ServiceMetricTable({
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isFetchingLicenses &&
|
||||
licenses?.onTrial &&
|
||||
!licenses?.trialConvertedToSubscription &&
|
||||
!isFetchingActiveLicenseV3 &&
|
||||
trialInfo?.onTrial &&
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
isCloudUserVal
|
||||
) {
|
||||
if (services.length > 0) {
|
||||
@ -85,9 +85,9 @@ function ServiceMetricTable({
|
||||
}, [
|
||||
services,
|
||||
isCloudUserVal,
|
||||
isFetchingLicenses,
|
||||
licenses?.onTrial,
|
||||
licenses?.trialConvertedToSubscription,
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo?.onTrial,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
]);
|
||||
|
||||
const paginationConfig = {
|
||||
|
@ -21,15 +21,15 @@ function ServiceTraceTable({
|
||||
const [RPS, setRPS] = useState(0);
|
||||
const { t: getText } = useTranslation(['services']);
|
||||
|
||||
const { licenses, isFetchingLicenses } = useAppContext();
|
||||
const { isFetchingActiveLicenseV3, trialInfo } = useAppContext();
|
||||
const { isCloudUser: isCloudUserVal } = useGetTenantLicense();
|
||||
const tableColumns = useMemo(() => getColumns(search, false), [search]);
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isFetchingLicenses &&
|
||||
licenses?.onTrial &&
|
||||
!licenses?.trialConvertedToSubscription &&
|
||||
!isFetchingActiveLicenseV3 &&
|
||||
trialInfo?.onTrial &&
|
||||
!trialInfo?.trialConvertedToSubscription &&
|
||||
isCloudUserVal
|
||||
) {
|
||||
if (services.length > 0) {
|
||||
@ -42,9 +42,9 @@ function ServiceTraceTable({
|
||||
}, [
|
||||
services,
|
||||
isCloudUserVal,
|
||||
isFetchingLicenses,
|
||||
licenses?.onTrial,
|
||||
licenses?.trialConvertedToSubscription,
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo?.onTrial,
|
||||
trialInfo?.trialConvertedToSubscription,
|
||||
]);
|
||||
|
||||
const paginationConfig = {
|
||||
|
@ -59,7 +59,7 @@ function SideNav(): JSX.Element {
|
||||
AppReducer
|
||||
>((state) => state.app);
|
||||
|
||||
const { user, featureFlags, licenses } = useAppContext();
|
||||
const { user, featureFlags, licenses, trialInfo } = useAppContext();
|
||||
|
||||
const isOnboardingV3Enabled = featureFlags?.find(
|
||||
(flag) => flag.name === FeatureKeys.ONBOARDING_V3,
|
||||
@ -97,7 +97,7 @@ function SideNav(): JSX.Element {
|
||||
const licenseStatus: string =
|
||||
licenses?.licenses?.find((e: License) => e.isCurrent)?.status || '';
|
||||
|
||||
const isWorkspaceBlocked = licenses?.workSpaceBlock || false;
|
||||
const isWorkspaceBlocked = trialInfo?.workSpaceBlock || false;
|
||||
|
||||
const isLicenseActive =
|
||||
licenseStatus?.toLocaleLowerCase() ===
|
||||
|
@ -31,6 +31,7 @@ const breadcrumbNameMap: Record<string, string> = {
|
||||
[ROUTES.SUPPORT]: 'Support',
|
||||
[ROUTES.WORKSPACE_LOCKED]: 'Workspace Locked',
|
||||
[ROUTES.WORKSPACE_SUSPENDED]: 'Workspace Suspended',
|
||||
[ROUTES.WORKSPACE_ACCESS_RESTRICTED]: 'Workspace Access Restricted',
|
||||
[ROUTES.MESSAGING_QUEUES_OVERVIEW]: 'Messaging Queues',
|
||||
};
|
||||
|
||||
|
@ -226,6 +226,7 @@ export const routesToSkip = [
|
||||
ROUTES.METRICS_EXPLORER_VIEWS,
|
||||
ROUTES.CHANNELS_NEW,
|
||||
ROUTES.CHANNELS_EDIT,
|
||||
ROUTES.WORKSPACE_ACCESS_RESTRICTED,
|
||||
];
|
||||
|
||||
export const routesToDisable = [ROUTES.LOGS_EXPLORER, ROUTES.LIVE_LOGS];
|
||||
|
@ -12,13 +12,13 @@ import { getRoutes } from './utils';
|
||||
|
||||
function SettingsPage(): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
const { user, featureFlags, licenses } = useAppContext();
|
||||
const { user, featureFlags, trialInfo } = useAppContext();
|
||||
const {
|
||||
isCloudUser: isCloudAccount,
|
||||
isEECloudUser: isEECloudAccount,
|
||||
} = useGetTenantLicense();
|
||||
|
||||
const isWorkspaceBlocked = licenses?.workSpaceBlock || false;
|
||||
const isWorkspaceBlocked = trialInfo?.workSpaceBlock || false;
|
||||
|
||||
const [isCurrentOrgSettings] = useComponentPermission(
|
||||
['current_org_settings'],
|
||||
|
@ -79,7 +79,7 @@ const supportChannels = [
|
||||
export default function Support(): JSX.Element {
|
||||
const history = useHistory();
|
||||
const { notifications } = useNotifications();
|
||||
const { licenses, featureFlags } = useAppContext();
|
||||
const { trialInfo, featureFlags } = useAppContext();
|
||||
const [isAddCreditCardModalOpen, setIsAddCreditCardModalOpen] = useState(
|
||||
false,
|
||||
);
|
||||
@ -106,7 +106,7 @@ export default function Support(): JSX.Element {
|
||||
?.active || false;
|
||||
|
||||
const showAddCreditCardModal =
|
||||
!isPremiumChatSupportEnabled && !licenses?.trialConvertedToSubscription;
|
||||
!isPremiumChatSupportEnabled && !trialInfo?.trialConvertedToSubscription;
|
||||
|
||||
const handleBillingOnSuccess = (
|
||||
data: ErrorResponse | SuccessResponse<CheckoutSuccessPayloadProps, unknown>,
|
||||
|
@ -0,0 +1,157 @@
|
||||
$light-theme: 'lightMode';
|
||||
$dark-theme: 'darkMode';
|
||||
|
||||
@keyframes gradientFlow {
|
||||
0% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
50% {
|
||||
background-position: 100% 50%;
|
||||
}
|
||||
100% {
|
||||
background-position: 0% 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.workspace-access-restricted {
|
||||
&__modal {
|
||||
.ant-modal-mask {
|
||||
backdrop-filter: blur(2px);
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
margin-top: 148px;
|
||||
|
||||
.ant-tabs {
|
||||
&-nav {
|
||||
&::before {
|
||||
border-color: var(--bg-slate-500);
|
||||
|
||||
.#{$light-theme} & {
|
||||
border-color: var(--bg-vanilla-300);
|
||||
}
|
||||
}
|
||||
}
|
||||
&-nav-wrap {
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__modal {
|
||||
&__header {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.ant-modal-content {
|
||||
border-radius: 4px;
|
||||
border: 1px solid var(--bg-slate-400);
|
||||
background: linear-gradient(
|
||||
139deg,
|
||||
rgba(18, 19, 23, 0.8) 0%,
|
||||
rgba(18, 19, 23, 0.9) 98.68%
|
||||
);
|
||||
box-shadow: 4px 10px 16px 2px rgba(0, 0, 0, 0.2);
|
||||
backdrop-filter: blur(20px);
|
||||
|
||||
.#{$light-theme} & {
|
||||
border: 1px solid var(--bg-vanilla-300);
|
||||
background: var(--bg-vanilla-100);
|
||||
}
|
||||
}
|
||||
|
||||
.ant-modal-header {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.ant-list {
|
||||
&-item {
|
||||
border-color: var(--bg-slate-500);
|
||||
|
||||
.#{$light-theme} & {
|
||||
border-color: var(--bg-vanilla-300);
|
||||
}
|
||||
|
||||
&-meta {
|
||||
align-items: center !important;
|
||||
|
||||
&-title {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
&-avatar {
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
font-weight: 500;
|
||||
color: var(--text-vanilla-100);
|
||||
font-size: 18px;
|
||||
|
||||
.#{$light-theme} & {
|
||||
color: var(--text-ink-200);
|
||||
}
|
||||
}
|
||||
&__cta {
|
||||
margin-top: 54px;
|
||||
}
|
||||
}
|
||||
&__container {
|
||||
padding-top: 36px;
|
||||
}
|
||||
&__details {
|
||||
width: 80%;
|
||||
margin: 0 auto;
|
||||
color: var(--text-vanilla-400, #c0c1c3);
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 24px; /* 150% */
|
||||
|
||||
.#{$light-theme} & {
|
||||
color: var(--text-ink-200);
|
||||
}
|
||||
|
||||
&__highlight {
|
||||
color: var(--text-vanilla-100, #fff);
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 24px;
|
||||
|
||||
.#{$light-theme} & {
|
||||
color: var(--text-ink-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
&__title {
|
||||
background: linear-gradient(
|
||||
99deg,
|
||||
#ead8fd 0%,
|
||||
#7a97fa 33%,
|
||||
#fd5ab2 66%,
|
||||
#ead8fd 100%
|
||||
);
|
||||
background-size: 300% 300%;
|
||||
background-clip: text;
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
animation: gradientFlow 24s ease infinite;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
&__creative {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 54px;
|
||||
|
||||
img {
|
||||
width: -webkit-fill-available;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,129 @@
|
||||
import './WorkspaceAccessRestricted.styles.scss';
|
||||
|
||||
import { Button, Col, Modal, Row, Skeleton, Space, Typography } from 'antd';
|
||||
import ROUTES from 'constants/routes';
|
||||
import history from 'lib/history';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useEffect } from 'react';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
|
||||
function WorkspaceAccessRestricted(): JSX.Element {
|
||||
const { activeLicenseV3, isFetchingActiveLicenseV3 } = useAppContext();
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3) {
|
||||
const isTerminated = activeLicenseV3.state === LicenseState.TERMINATED;
|
||||
const isExpired = activeLicenseV3.state === LicenseState.EXPIRED;
|
||||
const isCancelled = activeLicenseV3.state === LicenseState.CANCELLED;
|
||||
|
||||
const isWorkspaceAccessRestricted = isTerminated || isExpired || isCancelled;
|
||||
|
||||
if (
|
||||
!isWorkspaceAccessRestricted ||
|
||||
activeLicenseV3.platform === LicensePlatform.SELF_HOSTED
|
||||
) {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
}
|
||||
}
|
||||
}, [isFetchingActiveLicenseV3, activeLicenseV3]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Modal
|
||||
rootClassName="workspace-access-restricted__modal"
|
||||
title={
|
||||
<div className="workspace-access-restricted__modal__header">
|
||||
<span className="workspace-access-restricted__modal__title">
|
||||
Your workspace access is restricted
|
||||
</span>
|
||||
</div>
|
||||
}
|
||||
open
|
||||
closable={false}
|
||||
footer={null}
|
||||
width="65%"
|
||||
>
|
||||
<div className="workspace-access-restricted__container">
|
||||
{isFetchingActiveLicenseV3 || !activeLicenseV3 ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<>
|
||||
<Row justify="center" align="middle">
|
||||
<Col>
|
||||
<Space direction="vertical" align="center">
|
||||
<Typography.Title
|
||||
level={4}
|
||||
className="workspace-access-restricted__details"
|
||||
>
|
||||
{activeLicenseV3.state === LicenseState.TERMINATED && (
|
||||
<>
|
||||
Your SigNoz license is terminated, please contact support at{' '}
|
||||
<a href="mailto:cloud-support@signoz.io">
|
||||
cloud-support@signoz.io
|
||||
</a>{' '}
|
||||
for a new deployment
|
||||
</>
|
||||
)}
|
||||
{activeLicenseV3.state === LicenseState.EXPIRED && (
|
||||
<>
|
||||
Your SigNoz license is expired, please contact support at{' '}
|
||||
<a href="mailto:cloud-support@signoz.io">
|
||||
cloud-support@signoz.io
|
||||
</a>{' '}
|
||||
for renewal to avoid termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
.
|
||||
</>
|
||||
)}
|
||||
{activeLicenseV3.state === LicenseState.CANCELLED && (
|
||||
<>
|
||||
Your SigNoz license is cancelled, please contact support at{' '}
|
||||
<a href="mailto:cloud-support@signoz.io">
|
||||
cloud-support@signoz.io
|
||||
</a>{' '}
|
||||
for reactivation to avoid termination of license as per our{' '}
|
||||
<a
|
||||
href="https://signoz.io/terms-of-service"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
terms of service
|
||||
</a>
|
||||
.
|
||||
</>
|
||||
)}
|
||||
</Typography.Title>
|
||||
|
||||
<Button
|
||||
type="default"
|
||||
shape="round"
|
||||
size="middle"
|
||||
href="mailto:cloud-support@signoz.io"
|
||||
role="button"
|
||||
>
|
||||
Contact Us
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="workspace-access-restricted__creative">
|
||||
<img
|
||||
src="/Images/feature-graphic-correlation.svg"
|
||||
alt="correlation-graphic"
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default WorkspaceAccessRestricted;
|
3
frontend/src/pages/WorkspaceAccessRestricted/index.tsx
Normal file
3
frontend/src/pages/WorkspaceAccessRestricted/index.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
import WorkspaceAccessRestricted from './WorkspaceAccessRestricted';
|
||||
|
||||
export default WorkspaceAccessRestricted;
|
@ -26,6 +26,7 @@ import { useAppContext } from 'providers/App/App';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from 'react-query';
|
||||
import { LicensePlatform } from 'types/api/licensesV3/getActive';
|
||||
import { getFormattedDate } from 'utils/timeUtils';
|
||||
|
||||
import CustomerStoryCard from './CustomerStoryCard';
|
||||
@ -38,7 +39,12 @@ import {
|
||||
} from './workspaceLocked.data';
|
||||
|
||||
export default function WorkspaceBlocked(): JSX.Element {
|
||||
const { user, licenses, isFetchingLicenses } = useAppContext();
|
||||
const {
|
||||
user,
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo,
|
||||
activeLicenseV3,
|
||||
} = useAppContext();
|
||||
const isAdmin = user.role === 'ADMIN';
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
@ -64,14 +70,21 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (!isFetchingLicenses) {
|
||||
const shouldBlockWorkspace = licenses?.workSpaceBlock;
|
||||
if (!isFetchingActiveLicenseV3) {
|
||||
const shouldBlockWorkspace = trialInfo?.workSpaceBlock;
|
||||
|
||||
if (!shouldBlockWorkspace) {
|
||||
if (
|
||||
!shouldBlockWorkspace ||
|
||||
activeLicenseV3?.platform === LicensePlatform.SELF_HOSTED
|
||||
) {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
}
|
||||
}
|
||||
}, [isFetchingLicenses, licenses]);
|
||||
}, [
|
||||
isFetchingActiveLicenseV3,
|
||||
trialInfo?.workSpaceBlock,
|
||||
activeLicenseV3?.platform,
|
||||
]);
|
||||
|
||||
const { mutate: updateCreditCard, isLoading } = useMutation(
|
||||
updateCreditCardApi,
|
||||
@ -307,7 +320,7 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
width="65%"
|
||||
>
|
||||
<div className="workspace-locked__container">
|
||||
{isFetchingLicenses || !licenses ? (
|
||||
{isFetchingActiveLicenseV3 || !trialInfo ? (
|
||||
<Skeleton />
|
||||
) : (
|
||||
<>
|
||||
@ -322,7 +335,7 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
<br />
|
||||
{t('yourDataIsSafe')}{' '}
|
||||
<span className="workspace-locked__details__highlight">
|
||||
{getFormattedDate(licenses?.gracePeriodEnd || Date.now())}
|
||||
{getFormattedDate(trialInfo?.gracePeriodEnd || Date.now())}
|
||||
</span>{' '}
|
||||
{t('actNow')}
|
||||
</Typography.Paragraph>
|
||||
|
@ -19,7 +19,7 @@ import { useAppContext } from 'providers/App/App';
|
||||
import { useCallback, useEffect } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useMutation } from 'react-query';
|
||||
import { LicenseState, LicenseStatus } from 'types/api/licensesV3/getActive';
|
||||
import { LicensePlatform, LicenseState } from 'types/api/licensesV3/getActive';
|
||||
import { getFormattedDateWithMinutes } from 'utils/timeUtils';
|
||||
|
||||
function WorkspaceSuspended(): JSX.Element {
|
||||
@ -58,10 +58,12 @@ function WorkspaceSuspended(): JSX.Element {
|
||||
useEffect(() => {
|
||||
if (!isFetchingActiveLicenseV3 && activeLicenseV3) {
|
||||
const shouldSuspendWorkspace =
|
||||
activeLicenseV3.status === LicenseStatus.SUSPENDED &&
|
||||
activeLicenseV3.state === LicenseState.DEFAULTED;
|
||||
|
||||
if (!shouldSuspendWorkspace) {
|
||||
if (
|
||||
!shouldSuspendWorkspace ||
|
||||
activeLicenseV3?.platform === LicensePlatform.SELF_HOSTED
|
||||
) {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import getLocalStorageApi from 'api/browser/localstorage/get';
|
||||
import getAllOrgPreferences from 'api/preferences/getAllOrgPreferences';
|
||||
import { Logout } from 'api/utils';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import dayjs from 'dayjs';
|
||||
import useActiveLicenseV3 from 'hooks/useActiveLicenseV3/useActiveLicenseV3';
|
||||
import useGetFeatureFlag from 'hooks/useGetFeatureFlag';
|
||||
import { useGlobalEventListener } from 'hooks/useGlobalEventListener';
|
||||
@ -19,7 +20,11 @@ import {
|
||||
import { useQuery } from 'react-query';
|
||||
import { FeatureFlagProps as FeatureFlags } from 'types/api/features/getFeaturesFlags';
|
||||
import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||
import {
|
||||
LicenseState,
|
||||
LicenseV3ResModel,
|
||||
TrialInfo,
|
||||
} from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { OrgPreference } from 'types/reducer/app';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
@ -37,6 +42,9 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
activeLicenseV3,
|
||||
setActiveLicenseV3,
|
||||
] = useState<LicenseV3ResModel | null>(null);
|
||||
|
||||
const [trialInfo, setTrialInfo] = useState<TrialInfo | null>(null);
|
||||
|
||||
const [featureFlags, setFeatureFlags] = useState<FeatureFlags[] | null>(null);
|
||||
const [orgPreferences, setOrgPreferences] = useState<OrgPreference[] | null>(
|
||||
null,
|
||||
@ -125,6 +133,29 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
activeLicenseV3Data.payload
|
||||
) {
|
||||
setActiveLicenseV3(activeLicenseV3Data.payload);
|
||||
|
||||
const isOnTrial = dayjs(
|
||||
activeLicenseV3Data.payload.free_until || Date.now(),
|
||||
).isAfter(dayjs());
|
||||
|
||||
const trialInfo: TrialInfo = {
|
||||
trialStart: activeLicenseV3Data.payload.valid_from,
|
||||
trialEnd: dayjs(
|
||||
activeLicenseV3Data.payload.free_until || Date.now(),
|
||||
).unix(),
|
||||
onTrial: isOnTrial,
|
||||
workSpaceBlock:
|
||||
activeLicenseV3Data.payload.state === LicenseState.EVALUATION_EXPIRED,
|
||||
trialConvertedToSubscription:
|
||||
activeLicenseV3Data.payload.state !== LicenseState.ISSUED &&
|
||||
activeLicenseV3Data.payload.state !== LicenseState.EVALUATING &&
|
||||
activeLicenseV3Data.payload.state !== LicenseState.EVALUATION_EXPIRED,
|
||||
gracePeriodEnd: dayjs(
|
||||
activeLicenseV3Data.payload.event_queue.scheduled_at || Date.now(),
|
||||
).unix(),
|
||||
};
|
||||
|
||||
setTrialInfo(trialInfo);
|
||||
}
|
||||
}, [activeLicenseV3Data, isFetchingActiveLicenseV3]);
|
||||
|
||||
@ -216,6 +247,7 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
setIsLoggedIn(false);
|
||||
setUser(getUserDefaults());
|
||||
setActiveLicenseV3(null);
|
||||
setTrialInfo(null);
|
||||
setLicenses(null);
|
||||
setFeatureFlags(null);
|
||||
setOrgPreferences(null);
|
||||
@ -229,6 +261,7 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
licenses,
|
||||
activeLicenseV3,
|
||||
featureFlags,
|
||||
trialInfo,
|
||||
orgPreferences,
|
||||
isLoggedIn,
|
||||
org,
|
||||
@ -248,6 +281,7 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
||||
updateOrg,
|
||||
}),
|
||||
[
|
||||
trialInfo,
|
||||
activeLicenseV3,
|
||||
activeLicenseV3FetchError,
|
||||
featureFlags,
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { FeatureFlagProps as FeatureFlags } from 'types/api/features/getFeaturesFlags';
|
||||
import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||
import { LicenseV3ResModel, TrialInfo } from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { PayloadProps as User } from 'types/api/user/getUser';
|
||||
import { OrgPreference } from 'types/reducer/app';
|
||||
@ -9,6 +9,7 @@ export interface IAppContext {
|
||||
user: IUser;
|
||||
licenses: LicensesResModel | null;
|
||||
activeLicenseV3: LicenseV3ResModel | null;
|
||||
trialInfo: TrialInfo | null;
|
||||
featureFlags: FeatureFlags[] | null;
|
||||
orgPreferences: OrgPreference[] | null;
|
||||
isLoggedIn: boolean;
|
||||
|
@ -116,6 +116,27 @@ export function getAppContextMock(
|
||||
state: LicenseState.ACTIVE,
|
||||
status: LicenseStatus.VALID,
|
||||
platform: LicensePlatform.CLOUD,
|
||||
created_at: '0',
|
||||
plan: {
|
||||
created_at: '0',
|
||||
description: '',
|
||||
is_active: true,
|
||||
name: '',
|
||||
updated_at: '0',
|
||||
},
|
||||
plan_id: '0',
|
||||
free_until: '0',
|
||||
updated_at: '0',
|
||||
valid_from: 0,
|
||||
valid_until: 0,
|
||||
},
|
||||
trialInfo: {
|
||||
trialStart: -1,
|
||||
trialEnd: -1,
|
||||
onTrial: false,
|
||||
workSpaceBlock: false,
|
||||
trialConvertedToSubscription: false,
|
||||
gracePeriodEnd: -1,
|
||||
},
|
||||
isFetchingActiveLicenseV3: false,
|
||||
activeLicenseV3FetchError: null,
|
||||
@ -144,12 +165,6 @@ export function getAppContextMock(
|
||||
isFetchingUser: false,
|
||||
userFetchError: null,
|
||||
licenses: {
|
||||
trialStart: -1,
|
||||
trialEnd: -1,
|
||||
onTrial: false,
|
||||
workSpaceBlock: false,
|
||||
trialConvertedToSubscription: false,
|
||||
gracePeriodEnd: -1,
|
||||
licenses: [
|
||||
{
|
||||
key: 'does-not-matter',
|
||||
|
@ -1,11 +1,5 @@
|
||||
import { License } from './def';
|
||||
|
||||
export type PayloadProps = {
|
||||
trialStart: number;
|
||||
trialEnd: number;
|
||||
onTrial: boolean;
|
||||
workSpaceBlock: boolean;
|
||||
trialConvertedToSubscription: boolean;
|
||||
gracePeriodEnd: number;
|
||||
licenses: License[];
|
||||
};
|
||||
|
@ -11,6 +11,12 @@ export enum LicenseStatus {
|
||||
export enum LicenseState {
|
||||
DEFAULTED = 'DEFAULTED',
|
||||
ACTIVE = 'ACTIVE',
|
||||
EXPIRED = 'EXPIRED',
|
||||
ISSUED = 'ISSUED',
|
||||
EVALUATING = 'EVALUATING',
|
||||
EVALUATION_EXPIRED = 'EVALUATION_EXPIRED',
|
||||
TERMINATED = 'TERMINATED',
|
||||
CANCELLED = 'CANCELLED',
|
||||
}
|
||||
|
||||
export enum LicensePlatform {
|
||||
@ -18,6 +24,12 @@ export enum LicensePlatform {
|
||||
CLOUD = 'CLOUD',
|
||||
}
|
||||
|
||||
// Legacy
|
||||
export const LicensePlanKey = {
|
||||
ENTERPRISE: 'ENTERPRISE',
|
||||
BASIC: 'BASIC',
|
||||
};
|
||||
|
||||
export type LicenseV3EventQueueResModel = {
|
||||
event: LicenseEvent;
|
||||
status: string;
|
||||
@ -31,4 +43,27 @@ export type LicenseV3ResModel = {
|
||||
state: LicenseState;
|
||||
event_queue: LicenseV3EventQueueResModel;
|
||||
platform: LicensePlatform;
|
||||
created_at: string;
|
||||
plan: {
|
||||
created_at: string;
|
||||
description: string;
|
||||
is_active: boolean;
|
||||
name: string;
|
||||
updated_at: string;
|
||||
};
|
||||
plan_id: string;
|
||||
free_until: string;
|
||||
updated_at: string;
|
||||
valid_from: number;
|
||||
valid_until: number;
|
||||
};
|
||||
|
||||
// Duplicate of old licenses API response, need to improve this later
|
||||
export type TrialInfo = {
|
||||
trialStart: number;
|
||||
trialEnd: number;
|
||||
onTrial: boolean;
|
||||
workSpaceBlock: boolean;
|
||||
trialConvertedToSubscription: boolean;
|
||||
gracePeriodEnd: number;
|
||||
};
|
||||
|
@ -115,4 +115,5 @@ export const routePermission: Record<keyof typeof ROUTES, ROLES[]> = {
|
||||
METRICS_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
METRICS_EXPLORER_EXPLORER: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
METRICS_EXPLORER_VIEWS: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
WORKSPACE_ACCESS_RESTRICTED: ['ADMIN', 'EDITOR', 'VIEWER'],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user