mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 21:58:59 +08:00
feat: enable billing and org-settings in workspace blocked state (#6761)
* feat: enable billing and org-settings in workspace blocked state * feat: disable sidebar items in workspace blocked state
This commit is contained in:
parent
5c546e8efd
commit
e92d055c30
@ -11,6 +11,7 @@ import { useQuery } from 'react-query';
|
||||
import { matchPath, useLocation } from 'react-router-dom';
|
||||
import { LicenseState, LicenseStatus } from 'types/api/licensesV3/getActive';
|
||||
import { Organization } from 'types/api/user/getOrganization';
|
||||
import { USER_ROLES } from 'types/roles';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
import { routePermission } from 'utils/permission';
|
||||
|
||||
@ -36,6 +37,8 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
activeLicenseV3,
|
||||
isFetchingActiveLicenseV3,
|
||||
} = useAppContext();
|
||||
|
||||
const isAdmin = user.role === USER_ROLES.ADMIN;
|
||||
const mapRoutes = useMemo(
|
||||
() =>
|
||||
new Map(
|
||||
@ -113,7 +116,17 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
const navigateToWorkSpaceBlocked = (route: any): void => {
|
||||
const { path } = route;
|
||||
|
||||
if (path && path !== ROUTES.WORKSPACE_LOCKED) {
|
||||
const isRouteEnabledForWorkspaceBlockedState =
|
||||
isAdmin &&
|
||||
(path === ROUTES.ORG_SETTINGS ||
|
||||
path === ROUTES.BILLING ||
|
||||
path === ROUTES.MY_SETTINGS);
|
||||
|
||||
if (
|
||||
path &&
|
||||
path !== ROUTES.WORKSPACE_LOCKED &&
|
||||
!isRouteEnabledForWorkspaceBlockedState
|
||||
) {
|
||||
history.push(ROUTES.WORKSPACE_LOCKED);
|
||||
}
|
||||
};
|
||||
@ -127,6 +140,7 @@ function PrivateRoute({ children }: PrivateRouteProps): JSX.Element {
|
||||
navigateToWorkSpaceBlocked(currentRoute);
|
||||
}
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [isFetchingLicenses, licenses?.workSpaceBlock, mapRoutes, pathname]);
|
||||
|
||||
const navigateToWorkSpaceSuspended = (route: any): void => {
|
||||
|
@ -15,6 +15,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
.nav-item-data {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
|
@ -11,17 +11,28 @@ export default function NavItem({
|
||||
item,
|
||||
isActive,
|
||||
onClick,
|
||||
isDisabled,
|
||||
}: {
|
||||
item: SidebarItem;
|
||||
isActive: boolean;
|
||||
onClick: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
||||
isDisabled: boolean;
|
||||
}): JSX.Element {
|
||||
const { label, icon, isBeta, isNew } = item;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cx('nav-item', isActive ? 'active' : '')}
|
||||
onClick={(event): void => onClick(event)}
|
||||
className={cx(
|
||||
'nav-item',
|
||||
isActive ? 'active' : '',
|
||||
isDisabled ? 'disabled' : '',
|
||||
)}
|
||||
onClick={(event): void => {
|
||||
if (isDisabled) {
|
||||
return;
|
||||
}
|
||||
onClick(event);
|
||||
}}
|
||||
>
|
||||
<div className="nav-item-active-marker" />
|
||||
<div className={cx('nav-item-data', isBeta ? 'beta-tag' : '')}>
|
||||
|
@ -89,6 +89,8 @@ function SideNav(): JSX.Element {
|
||||
const licenseStatus: string =
|
||||
licenses?.licenses?.find((e: License) => e.isCurrent)?.status || '';
|
||||
|
||||
const isWorkspaceBlocked = licenses?.workSpaceBlock || false;
|
||||
|
||||
const isLicenseActive =
|
||||
licenseStatus?.toLocaleLowerCase() ===
|
||||
LICENSE_PLAN_STATUS.VALID.toLocaleLowerCase();
|
||||
@ -351,7 +353,11 @@ function SideNav(): JSX.Element {
|
||||
<div className="get-started-nav-items">
|
||||
<Button
|
||||
className="get-started-btn"
|
||||
disabled={isWorkspaceBlocked}
|
||||
onClick={(event: MouseEvent): void => {
|
||||
if (isWorkspaceBlocked) {
|
||||
return;
|
||||
}
|
||||
onClickGetStarted(event);
|
||||
}}
|
||||
>
|
||||
@ -369,6 +375,11 @@ function SideNav(): JSX.Element {
|
||||
key={item.key || index}
|
||||
item={item}
|
||||
isActive={activeMenuKey === item.key}
|
||||
isDisabled={
|
||||
isWorkspaceBlocked &&
|
||||
item.key !== ROUTES.BILLING &&
|
||||
item.key !== ROUTES.SETTINGS
|
||||
}
|
||||
onClick={(event): void => {
|
||||
handleMenuItemClick(event, item);
|
||||
}}
|
||||
@ -380,6 +391,7 @@ function SideNav(): JSX.Element {
|
||||
<NavItem
|
||||
key="keyboardShortcuts"
|
||||
item={shortcutMenuItem}
|
||||
isDisabled={isWorkspaceBlocked}
|
||||
isActive={false}
|
||||
onClick={onClickShortcuts}
|
||||
/>
|
||||
@ -389,6 +401,7 @@ function SideNav(): JSX.Element {
|
||||
key="trySignozCloud"
|
||||
item={trySignozCloudMenuItem}
|
||||
isActive={false}
|
||||
isDisabled={isWorkspaceBlocked}
|
||||
onClick={onClickSignozCloud}
|
||||
/>
|
||||
)}
|
||||
@ -399,6 +412,7 @@ function SideNav(): JSX.Element {
|
||||
key={item?.key || index}
|
||||
item={item}
|
||||
isActive={activeMenuKey === item?.key}
|
||||
isDisabled={isWorkspaceBlocked}
|
||||
onClick={(event: MouseEvent): void => {
|
||||
handleUserManagentMenuItemClick(item?.key as string, event);
|
||||
logEvent('Sidebar: Menu clicked', {
|
||||
@ -415,6 +429,7 @@ function SideNav(): JSX.Element {
|
||||
key={inviteMemberMenuItem.key}
|
||||
item={inviteMemberMenuItem}
|
||||
isActive={activeMenuKey === inviteMemberMenuItem?.key}
|
||||
isDisabled={false}
|
||||
onClick={(event: React.MouseEvent): void => {
|
||||
if (isCtrlMetaKey(event)) {
|
||||
openInNewTab(`${inviteMemberMenuItem.key}`);
|
||||
@ -434,6 +449,7 @@ function SideNav(): JSX.Element {
|
||||
key={ROUTES.MY_SETTINGS}
|
||||
item={userSettingsMenuItem}
|
||||
isActive={activeMenuKey === userSettingsMenuItem?.key}
|
||||
isDisabled={false}
|
||||
onClick={(event: MouseEvent): void => {
|
||||
handleUserManagentMenuItemClick(
|
||||
userSettingsMenuItem?.key as string,
|
||||
|
@ -11,7 +11,9 @@ import { getRoutes } from './utils';
|
||||
|
||||
function SettingsPage(): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
const { user, featureFlags } = useAppContext();
|
||||
const { user, featureFlags, licenses } = useAppContext();
|
||||
|
||||
const isWorkspaceBlocked = licenses?.workSpaceBlock || false;
|
||||
|
||||
const [isCurrentOrgSettings] = useComponentPermission(
|
||||
['current_org_settings'],
|
||||
@ -24,8 +26,15 @@ function SettingsPage(): JSX.Element {
|
||||
?.active || false;
|
||||
|
||||
const routes = useMemo(
|
||||
() => getRoutes(user.role, isCurrentOrgSettings, isGatewayEnabled, t),
|
||||
[user.role, isCurrentOrgSettings, isGatewayEnabled, t],
|
||||
() =>
|
||||
getRoutes(
|
||||
user.role,
|
||||
isCurrentOrgSettings,
|
||||
isGatewayEnabled,
|
||||
isWorkspaceBlocked,
|
||||
t,
|
||||
),
|
||||
[user.role, isCurrentOrgSettings, isGatewayEnabled, isWorkspaceBlocked, t],
|
||||
);
|
||||
|
||||
return <RouteTab routes={routes} activeKey={pathname} history={history} />;
|
||||
|
@ -17,6 +17,7 @@ export const getRoutes = (
|
||||
userRole: ROLES | null,
|
||||
isCurrentOrgSettings: boolean,
|
||||
isGatewayEnabled: boolean,
|
||||
isWorkspaceBlocked: boolean,
|
||||
t: TFunction,
|
||||
): RouteTabProps['routes'] => {
|
||||
const settings = [];
|
||||
@ -27,6 +28,12 @@ export const getRoutes = (
|
||||
const isAdmin = userRole === USER_ROLES.ADMIN;
|
||||
const isEditor = userRole === USER_ROLES.EDITOR;
|
||||
|
||||
if (isWorkspaceBlocked && isAdmin) {
|
||||
settings.push(...organizationSettings(t));
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
settings.push(...generalSettings(t));
|
||||
|
||||
if (isCurrentOrgSettings) {
|
||||
|
@ -49,6 +49,14 @@ $dark-theme: 'darkMode';
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
.ant-btn-link {
|
||||
color: var(--text-vanilla-400);
|
||||
|
||||
.#{$light-theme} & {
|
||||
color: var(--text-ink-200);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.ant-modal-content {
|
||||
|
@ -126,6 +126,12 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
});
|
||||
};
|
||||
|
||||
const handleViewBilling = (): void => {
|
||||
logEvent('Workspace Blocked: User Clicked View Billing', {});
|
||||
|
||||
history.push(ROUTES.BILLING);
|
||||
};
|
||||
|
||||
const renderCustomerStories = (
|
||||
filterCondition: (index: number) => boolean,
|
||||
): JSX.Element[] =>
|
||||
@ -276,6 +282,18 @@ export default function WorkspaceBlocked(): JSX.Element {
|
||||
{t('trialPlanExpired')}
|
||||
</span>
|
||||
<span className="workspace-locked__modal__header__actions">
|
||||
{isAdmin && (
|
||||
<Button
|
||||
className="workspace-locked__modal__header__actions__billing"
|
||||
type="link"
|
||||
size="small"
|
||||
role="button"
|
||||
onClick={handleViewBilling}
|
||||
>
|
||||
View Billing
|
||||
</Button>
|
||||
)}
|
||||
|
||||
<Typography.Text className="workspace-locked__modal__title">
|
||||
Got Questions?
|
||||
</Typography.Text>
|
||||
|
Loading…
x
Reference in New Issue
Block a user