mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-11 00:01:31 +08:00
feat: add analytics group call (#3757)
* feat: add analytics group call * feat: add safety check for billing breakdown variable
This commit is contained in:
parent
377dbd8aec
commit
f7fe64a8df
@ -22,7 +22,8 @@ import { Dispatch } from 'redux';
|
|||||||
import { AppState } from 'store/reducers';
|
import { AppState } from 'store/reducers';
|
||||||
import AppActions from 'types/actions';
|
import AppActions from 'types/actions';
|
||||||
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
|
import { UPDATE_FEATURE_FLAG_RESPONSE } from 'types/actions/app';
|
||||||
import AppReducer from 'types/reducer/app';
|
import AppReducer, { User } from 'types/reducer/app';
|
||||||
|
import { extractDomain, isCloudUser } from 'utils/app';
|
||||||
import { trackPageView } from 'utils/segmentAnalytics';
|
import { trackPageView } from 'utils/segmentAnalytics';
|
||||||
|
|
||||||
import PrivateRoute from './Private';
|
import PrivateRoute from './Private';
|
||||||
@ -32,7 +33,7 @@ function App(): JSX.Element {
|
|||||||
const themeConfig = useThemeConfig();
|
const themeConfig = useThemeConfig();
|
||||||
const { data } = useLicense();
|
const { data } = useLicense();
|
||||||
const [routes, setRoutes] = useState(defaultRoutes);
|
const [routes, setRoutes] = useState(defaultRoutes);
|
||||||
const { role, isLoggedIn: isLoggedInState, user } = useSelector<
|
const { role, isLoggedIn: isLoggedInState, user, org } = useSelector<
|
||||||
AppState,
|
AppState,
|
||||||
AppReducer
|
AppReducer
|
||||||
>((state) => state.app);
|
>((state) => state.app);
|
||||||
@ -41,6 +42,8 @@ function App(): JSX.Element {
|
|||||||
|
|
||||||
const { hostname, pathname } = window.location;
|
const { hostname, pathname } = window.location;
|
||||||
|
|
||||||
|
const isCloudUserVal = isCloudUser();
|
||||||
|
|
||||||
const featureResponse = useGetFeatureFlag((allFlags) => {
|
const featureResponse = useGetFeatureFlag((allFlags) => {
|
||||||
const isOnboardingEnabled =
|
const isOnboardingEnabled =
|
||||||
allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active ||
|
allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active ||
|
||||||
@ -58,10 +61,7 @@ function App(): JSX.Element {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (
|
if (!isOnboardingEnabled || !isCloudUserVal) {
|
||||||
!isOnboardingEnabled ||
|
|
||||||
!(hostname && hostname.endsWith('signoz.cloud'))
|
|
||||||
) {
|
|
||||||
const newRoutes = routes.filter(
|
const newRoutes = routes.filter(
|
||||||
(route) => route?.path !== ROUTES.GET_STARTED,
|
(route) => route?.path !== ROUTES.GET_STARTED,
|
||||||
);
|
);
|
||||||
@ -86,6 +86,35 @@ function App(): JSX.Element {
|
|||||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||||
) || data?.payload?.licenses === null;
|
) || data?.payload?.licenses === null;
|
||||||
|
|
||||||
|
const enableAnalytics = (user: User): void => {
|
||||||
|
const orgName =
|
||||||
|
org && Array.isArray(org) && org.length > 0 ? org[0].name : '';
|
||||||
|
|
||||||
|
const identifyPayload = {
|
||||||
|
email: user?.email,
|
||||||
|
name: user?.name,
|
||||||
|
company_name: orgName,
|
||||||
|
role,
|
||||||
|
};
|
||||||
|
const domain = extractDomain(user?.email);
|
||||||
|
|
||||||
|
const hostNameParts = hostname.split('.');
|
||||||
|
|
||||||
|
const groupTraits = {
|
||||||
|
name: orgName,
|
||||||
|
tenant_id: hostNameParts[0],
|
||||||
|
data_region: hostNameParts[1],
|
||||||
|
tenant_url: hostname,
|
||||||
|
company_domain: domain,
|
||||||
|
};
|
||||||
|
|
||||||
|
window.analytics.identify(user?.email, identifyPayload);
|
||||||
|
|
||||||
|
window.analytics.group(domain, groupTraits);
|
||||||
|
|
||||||
|
window.clarity('identify', user.email, user.name);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER);
|
const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER);
|
||||||
|
|
||||||
@ -98,12 +127,9 @@ function App(): JSX.Element {
|
|||||||
) {
|
) {
|
||||||
setLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER, 'true');
|
setLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER, 'true');
|
||||||
|
|
||||||
window.analytics.identify(user?.email, {
|
if (isCloudUserVal) {
|
||||||
email: user?.email,
|
enableAnalytics(user);
|
||||||
name: user?.name,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
window.clarity('identify', user.email, user.name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isOnBasicPlan || (isLoggedInState && role && role !== 'ADMIN')) {
|
if (isOnBasicPlan || (isLoggedInState && role && role !== 'ADMIN')) {
|
||||||
|
@ -176,6 +176,7 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
} = data?.payload || {};
|
} = data?.payload || {};
|
||||||
const formattedUsageData: any[] = [];
|
const formattedUsageData: any[] = [];
|
||||||
|
|
||||||
|
if (breakdown && Array.isArray(breakdown)) {
|
||||||
for (let index = 0; index < breakdown.length; index += 1) {
|
for (let index = 0; index < breakdown.length; index += 1) {
|
||||||
const element = breakdown[index];
|
const element = breakdown[index];
|
||||||
|
|
||||||
@ -195,6 +196,7 @@ export default function BillingContainer(): JSX.Element {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setData(formattedUsageData);
|
setData(formattedUsageData);
|
||||||
setTotalBillAmount(total);
|
setTotalBillAmount(total);
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import './IngestionSettings.styles.scss';
|
import './IngestionSettings.styles.scss';
|
||||||
|
|
||||||
import { Table, Typography } from 'antd';
|
import { Skeleton, Table, Typography } from 'antd';
|
||||||
import type { ColumnsType } from 'antd/es/table';
|
import type { ColumnsType } from 'antd/es/table';
|
||||||
import getIngestionData from 'api/settings/getIngestionData';
|
import getIngestionData from 'api/settings/getIngestionData';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
@ -12,7 +12,7 @@ import AppReducer from 'types/reducer/app';
|
|||||||
export default function IngestionSettings(): JSX.Element {
|
export default function IngestionSettings(): JSX.Element {
|
||||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||||
|
|
||||||
const { data: ingestionData } = useQuery({
|
const { data: ingestionData, isFetching } = useQuery({
|
||||||
queryFn: getIngestionData,
|
queryFn: getIngestionData,
|
||||||
queryKey: ['getIngestionData', user?.userId],
|
queryKey: ['getIngestionData', user?.userId],
|
||||||
});
|
});
|
||||||
@ -25,11 +25,19 @@ export default function IngestionSettings(): JSX.Element {
|
|||||||
render: (text): JSX.Element => <Typography.Text> {text} </Typography.Text>,
|
render: (text): JSX.Element => <Typography.Text> {text} </Typography.Text>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'Value',
|
title: '',
|
||||||
dataIndex: 'value',
|
dataIndex: 'value',
|
||||||
key: 'value',
|
key: 'value',
|
||||||
render: (text): JSX.Element => (
|
render: (text): JSX.Element => (
|
||||||
<Typography.Text copyable>{text}</Typography.Text>
|
<div>
|
||||||
|
{isFetching ? (
|
||||||
|
<Skeleton.Input active style={{ height: 20 }} />
|
||||||
|
) : (
|
||||||
|
<Typography.Text copyable={!isFetching && text !== ''}>
|
||||||
|
{text}
|
||||||
|
</Typography.Text>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
@ -13,6 +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 { routeConfig, styles } from './config';
|
import { routeConfig, styles } from './config';
|
||||||
import { getQueryString } from './helper';
|
import { getQueryString } from './helper';
|
||||||
@ -50,8 +51,6 @@ function SideNav(): JSX.Element {
|
|||||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||||
) || data?.payload?.licenses === null;
|
) || data?.payload?.licenses === null;
|
||||||
|
|
||||||
const { hostname } = window.location;
|
|
||||||
|
|
||||||
const menuItems = useMemo(
|
const menuItems = useMemo(
|
||||||
() =>
|
() =>
|
||||||
defaultMenuItems.filter((item) => {
|
defaultMenuItems.filter((item) => {
|
||||||
@ -64,16 +63,13 @@ function SideNav(): JSX.Element {
|
|||||||
return item.key !== ROUTES.BILLING;
|
return item.key !== ROUTES.BILLING;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (!isOnboardingEnabled || !isCloudUser()) {
|
||||||
!isOnboardingEnabled ||
|
|
||||||
!(hostname && hostname.endsWith('signoz.cloud'))
|
|
||||||
) {
|
|
||||||
return item.key !== ROUTES.GET_STARTED;
|
return item.key !== ROUTES.GET_STARTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}),
|
}),
|
||||||
[featureResponse.data, isOnBasicPlan, hostname, role],
|
[featureResponse.data, isOnBasicPlan, role],
|
||||||
);
|
);
|
||||||
|
|
||||||
const { pathname, search } = useLocation();
|
const { pathname, search } = useLocation();
|
||||||
|
@ -19,6 +19,9 @@ export const commonRoutes = (t: TFunction): RouteTabProps['routes'] => [
|
|||||||
route: ROUTES.ALL_CHANNELS,
|
route: ROUTES.ALL_CHANNELS,
|
||||||
key: ROUTES.ALL_CHANNELS,
|
key: ROUTES.ALL_CHANNELS,
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const ingestionSettings = (t: TFunction): RouteTabProps['routes'] => [
|
||||||
{
|
{
|
||||||
Component: IngestionSettings,
|
Component: IngestionSettings,
|
||||||
name: t('routes:ingestion_settings').toString(),
|
name: t('routes:ingestion_settings').toString(),
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
import { RouteTabProps } from 'components/RouteTab/types';
|
import { RouteTabProps } from 'components/RouteTab/types';
|
||||||
import { TFunction } from 'i18next';
|
import { TFunction } from 'i18next';
|
||||||
|
import { isCloudUser } from 'utils/app';
|
||||||
|
|
||||||
import { commonRoutes, organizationSettings } from './config';
|
import {
|
||||||
|
commonRoutes,
|
||||||
|
ingestionSettings,
|
||||||
|
organizationSettings,
|
||||||
|
} from './config';
|
||||||
|
|
||||||
export const getRoutes = (
|
export const getRoutes = (
|
||||||
isCurrentOrgSettings: boolean,
|
isCurrentOrgSettings: boolean,
|
||||||
@ -13,5 +18,9 @@ export const getRoutes = (
|
|||||||
common = [...common, ...organizationSettings(t)];
|
common = [...common, ...organizationSettings(t)];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isCloudUser()) {
|
||||||
|
common = [...common, ...ingestionSettings(t)];
|
||||||
|
}
|
||||||
|
|
||||||
return common;
|
return common;
|
||||||
};
|
};
|
||||||
|
@ -17,6 +17,7 @@ import { useLocation } from 'react-router-dom';
|
|||||||
import { SuccessResponse } from 'types/api';
|
import { SuccessResponse } from 'types/api';
|
||||||
import { PayloadProps } from 'types/api/user/getUser';
|
import { PayloadProps } from 'types/api/user/getUser';
|
||||||
import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck';
|
import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck';
|
||||||
|
import { isCloudUser } from 'utils/app';
|
||||||
import { trackEvent } from 'utils/segmentAnalytics';
|
import { trackEvent } from 'utils/segmentAnalytics';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -233,8 +234,6 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
|
|
||||||
const handleSubmit = (): void => {
|
const handleSubmit = (): void => {
|
||||||
(async (): Promise<void> => {
|
(async (): Promise<void> => {
|
||||||
const { hostname } = window.location;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const values = form.getFieldsValue();
|
const values = form.getFieldsValue();
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@ -260,11 +259,7 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
|||||||
await commonHandler(
|
await commonHandler(
|
||||||
values,
|
values,
|
||||||
async (): Promise<void> => {
|
async (): Promise<void> => {
|
||||||
if (
|
if (isOnboardingEnabled && isCloudUser()) {
|
||||||
isOnboardingEnabled &&
|
|
||||||
hostname &&
|
|
||||||
hostname.endsWith('signoz.cloud')
|
|
||||||
) {
|
|
||||||
history.push(ROUTES.GET_STARTED);
|
history.push(ROUTES.GET_STARTED);
|
||||||
} else {
|
} else {
|
||||||
history.push(ROUTES.APPLICATION);
|
history.push(ROUTES.APPLICATION);
|
||||||
|
@ -3,3 +3,17 @@ import { SKIP_ONBOARDING } from 'constants/onboarding';
|
|||||||
|
|
||||||
export const isOnboardingSkipped = (): boolean =>
|
export const isOnboardingSkipped = (): boolean =>
|
||||||
getLocalStorage(SKIP_ONBOARDING) === 'true';
|
getLocalStorage(SKIP_ONBOARDING) === 'true';
|
||||||
|
|
||||||
|
export function extractDomain(email: string): string {
|
||||||
|
const emailParts = email.split('@');
|
||||||
|
if (emailParts.length !== 2) {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
return emailParts[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const isCloudUser = (): boolean => {
|
||||||
|
const { hostname } = window.location;
|
||||||
|
|
||||||
|
return hostname?.endsWith('signoz.cloud');
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user