mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-10-10 05:36:35 +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 AppActions from 'types/actions';
|
||||
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 PrivateRoute from './Private';
|
||||
@ -32,7 +33,7 @@ function App(): JSX.Element {
|
||||
const themeConfig = useThemeConfig();
|
||||
const { data } = useLicense();
|
||||
const [routes, setRoutes] = useState(defaultRoutes);
|
||||
const { role, isLoggedIn: isLoggedInState, user } = useSelector<
|
||||
const { role, isLoggedIn: isLoggedInState, user, org } = useSelector<
|
||||
AppState,
|
||||
AppReducer
|
||||
>((state) => state.app);
|
||||
@ -41,6 +42,8 @@ function App(): JSX.Element {
|
||||
|
||||
const { hostname, pathname } = window.location;
|
||||
|
||||
const isCloudUserVal = isCloudUser();
|
||||
|
||||
const featureResponse = useGetFeatureFlag((allFlags) => {
|
||||
const isOnboardingEnabled =
|
||||
allFlags.find((flag) => flag.name === FeatureKeys.ONBOARDING)?.active ||
|
||||
@ -58,10 +61,7 @@ function App(): JSX.Element {
|
||||
},
|
||||
});
|
||||
|
||||
if (
|
||||
!isOnboardingEnabled ||
|
||||
!(hostname && hostname.endsWith('signoz.cloud'))
|
||||
) {
|
||||
if (!isOnboardingEnabled || !isCloudUserVal) {
|
||||
const newRoutes = routes.filter(
|
||||
(route) => route?.path !== ROUTES.GET_STARTED,
|
||||
);
|
||||
@ -86,6 +86,35 @@ function App(): JSX.Element {
|
||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||
) || 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(() => {
|
||||
const isIdentifiedUser = getLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER);
|
||||
|
||||
@ -98,12 +127,9 @@ function App(): JSX.Element {
|
||||
) {
|
||||
setLocalStorageApi(LOCALSTORAGE.IS_IDENTIFIED_USER, 'true');
|
||||
|
||||
window.analytics.identify(user?.email, {
|
||||
email: user?.email,
|
||||
name: user?.name,
|
||||
});
|
||||
|
||||
window.clarity('identify', user.email, user.name);
|
||||
if (isCloudUserVal) {
|
||||
enableAnalytics(user);
|
||||
}
|
||||
}
|
||||
|
||||
if (isOnBasicPlan || (isLoggedInState && role && role !== 'ADMIN')) {
|
||||
|
@ -176,24 +176,26 @@ export default function BillingContainer(): JSX.Element {
|
||||
} = data?.payload || {};
|
||||
const formattedUsageData: any[] = [];
|
||||
|
||||
for (let index = 0; index < breakdown.length; index += 1) {
|
||||
const element = breakdown[index];
|
||||
if (breakdown && Array.isArray(breakdown)) {
|
||||
for (let index = 0; index < breakdown.length; index += 1) {
|
||||
const element = breakdown[index];
|
||||
|
||||
element?.tiers.forEach(
|
||||
(
|
||||
tier: { quantity: number; unitPrice: number; tierCost: number },
|
||||
i: number,
|
||||
) => {
|
||||
formattedUsageData.push({
|
||||
key: `${index}${i}`,
|
||||
name: i === 0 ? element?.type : '',
|
||||
unit: element?.unit,
|
||||
dataIngested: tier.quantity,
|
||||
pricePerUnit: tier.unitPrice,
|
||||
cost: `$ ${tier.tierCost}`,
|
||||
});
|
||||
},
|
||||
);
|
||||
element?.tiers.forEach(
|
||||
(
|
||||
tier: { quantity: number; unitPrice: number; tierCost: number },
|
||||
i: number,
|
||||
) => {
|
||||
formattedUsageData.push({
|
||||
key: `${index}${i}`,
|
||||
name: i === 0 ? element?.type : '',
|
||||
unit: element?.unit,
|
||||
dataIngested: tier.quantity,
|
||||
pricePerUnit: tier.unitPrice,
|
||||
cost: `$ ${tier.tierCost}`,
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setData(formattedUsageData);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import './IngestionSettings.styles.scss';
|
||||
|
||||
import { Table, Typography } from 'antd';
|
||||
import { Skeleton, Table, Typography } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import getIngestionData from 'api/settings/getIngestionData';
|
||||
import { useQuery } from 'react-query';
|
||||
@ -12,7 +12,7 @@ import AppReducer from 'types/reducer/app';
|
||||
export default function IngestionSettings(): JSX.Element {
|
||||
const { user } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
const { data: ingestionData } = useQuery({
|
||||
const { data: ingestionData, isFetching } = useQuery({
|
||||
queryFn: getIngestionData,
|
||||
queryKey: ['getIngestionData', user?.userId],
|
||||
});
|
||||
@ -25,11 +25,19 @@ export default function IngestionSettings(): JSX.Element {
|
||||
render: (text): JSX.Element => <Typography.Text> {text} </Typography.Text>,
|
||||
},
|
||||
{
|
||||
title: 'Value',
|
||||
title: '',
|
||||
dataIndex: 'value',
|
||||
key: 'value',
|
||||
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 { AppState } from 'store/reducers';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
import { routeConfig, styles } from './config';
|
||||
import { getQueryString } from './helper';
|
||||
@ -50,8 +51,6 @@ function SideNav(): JSX.Element {
|
||||
license.isCurrent && license.planKey === LICENSE_PLAN_KEY.BASIC_PLAN,
|
||||
) || data?.payload?.licenses === null;
|
||||
|
||||
const { hostname } = window.location;
|
||||
|
||||
const menuItems = useMemo(
|
||||
() =>
|
||||
defaultMenuItems.filter((item) => {
|
||||
@ -64,16 +63,13 @@ function SideNav(): JSX.Element {
|
||||
return item.key !== ROUTES.BILLING;
|
||||
}
|
||||
|
||||
if (
|
||||
!isOnboardingEnabled ||
|
||||
!(hostname && hostname.endsWith('signoz.cloud'))
|
||||
) {
|
||||
if (!isOnboardingEnabled || !isCloudUser()) {
|
||||
return item.key !== ROUTES.GET_STARTED;
|
||||
}
|
||||
|
||||
return true;
|
||||
}),
|
||||
[featureResponse.data, isOnBasicPlan, hostname, role],
|
||||
[featureResponse.data, isOnBasicPlan, role],
|
||||
);
|
||||
|
||||
const { pathname, search } = useLocation();
|
||||
|
@ -19,6 +19,9 @@ export const commonRoutes = (t: TFunction): RouteTabProps['routes'] => [
|
||||
route: ROUTES.ALL_CHANNELS,
|
||||
key: ROUTES.ALL_CHANNELS,
|
||||
},
|
||||
];
|
||||
|
||||
export const ingestionSettings = (t: TFunction): RouteTabProps['routes'] => [
|
||||
{
|
||||
Component: IngestionSettings,
|
||||
name: t('routes:ingestion_settings').toString(),
|
||||
|
@ -1,7 +1,12 @@
|
||||
import { RouteTabProps } from 'components/RouteTab/types';
|
||||
import { TFunction } from 'i18next';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
|
||||
import { commonRoutes, organizationSettings } from './config';
|
||||
import {
|
||||
commonRoutes,
|
||||
ingestionSettings,
|
||||
organizationSettings,
|
||||
} from './config';
|
||||
|
||||
export const getRoutes = (
|
||||
isCurrentOrgSettings: boolean,
|
||||
@ -13,5 +18,9 @@ export const getRoutes = (
|
||||
common = [...common, ...organizationSettings(t)];
|
||||
}
|
||||
|
||||
if (isCloudUser()) {
|
||||
common = [...common, ...ingestionSettings(t)];
|
||||
}
|
||||
|
||||
return common;
|
||||
};
|
||||
|
@ -17,6 +17,7 @@ import { useLocation } from 'react-router-dom';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { PayloadProps } from 'types/api/user/getUser';
|
||||
import { PayloadProps as LoginPrecheckPayloadProps } from 'types/api/user/loginPrecheck';
|
||||
import { isCloudUser } from 'utils/app';
|
||||
import { trackEvent } from 'utils/segmentAnalytics';
|
||||
|
||||
import {
|
||||
@ -233,8 +234,6 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
||||
|
||||
const handleSubmit = (): void => {
|
||||
(async (): Promise<void> => {
|
||||
const { hostname } = window.location;
|
||||
|
||||
try {
|
||||
const values = form.getFieldsValue();
|
||||
setLoading(true);
|
||||
@ -260,11 +259,7 @@ function SignUp({ version }: SignUpProps): JSX.Element {
|
||||
await commonHandler(
|
||||
values,
|
||||
async (): Promise<void> => {
|
||||
if (
|
||||
isOnboardingEnabled &&
|
||||
hostname &&
|
||||
hostname.endsWith('signoz.cloud')
|
||||
) {
|
||||
if (isOnboardingEnabled && isCloudUser()) {
|
||||
history.push(ROUTES.GET_STARTED);
|
||||
} else {
|
||||
history.push(ROUTES.APPLICATION);
|
||||
|
@ -3,3 +3,17 @@ import { SKIP_ONBOARDING } from 'constants/onboarding';
|
||||
|
||||
export const isOnboardingSkipped = (): boolean =>
|
||||
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