diff --git a/apps/dashboard/src/api/telemetry.ts b/apps/dashboard/src/api/telemetry.ts
new file mode 100644
index 00000000000..3c9ff38eb0b
--- /dev/null
+++ b/apps/dashboard/src/api/telemetry.ts
@@ -0,0 +1,8 @@
+import { post } from './api.client';
+
+export const sendTelemetry = async (event: string, data?: Record): Promise => {
+ await post('/telemetry/measure', {
+ event,
+ data,
+ });
+};
diff --git a/apps/dashboard/src/components/primitives/dialog.tsx b/apps/dashboard/src/components/primitives/dialog.tsx
index 89a615b43f2..686fcd90c6b 100644
--- a/apps/dashboard/src/components/primitives/dialog.tsx
+++ b/apps/dashboard/src/components/primitives/dialog.tsx
@@ -58,7 +58,7 @@ const DialogHeader = ({ className, ...props }: React.HTMLAttributes,
React.ComponentPropsWithoutRef
>(({ className, ...props }, ref) => (
-
+
));
DialogDescription.displayName = DialogPrimitive.Description.displayName;
diff --git a/apps/dashboard/src/components/side-navigation/side-navigation.tsx b/apps/dashboard/src/components/side-navigation/side-navigation.tsx
index 76806e7bd00..ad0b3643610 100644
--- a/apps/dashboard/src/components/side-navigation/side-navigation.tsx
+++ b/apps/dashboard/src/components/side-navigation/side-navigation.tsx
@@ -18,6 +18,8 @@ import { OrganizationDropdown } from './organization-dropdown';
import { FreeTrialCard } from './free-trial-card';
import { buildRoute, LEGACY_ROUTES, ROUTES } from '@/utils/routes';
import { SubscribersStayTunedModal } from './subscribers-stay-tuned-modal';
+import { useTelemetry } from '@/hooks';
+import { TelemetryEvent } from '@/utils/telemetry';
const linkVariants = cva(
`flex items-center gap-2 text-sm py-1.5 px-2 rounded-lg focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring cursor-pointer`,
@@ -82,6 +84,7 @@ const NavigationGroup = ({ children, label }: { children: ReactNode; label?: str
export const SideNavigation = () => {
const { currentEnvironment, environments, switchEnvironment } = useEnvironment();
+ const track = useTelemetry();
const environmentNames = useMemo(() => environments?.map((env) => env.name), [environments]);
const onEnvironmentChange = (value: string) => {
const environment = environments?.find((env) => env.name === value);
@@ -100,7 +103,7 @@ export const SideNavigation = () => {
Workflows
-
+ track(TelemetryEvent.SUBSCRIBERS_LINK_CLICKED)}>
Subscribers
diff --git a/apps/dashboard/src/components/side-navigation/subscribers-stay-tuned-modal.tsx b/apps/dashboard/src/components/side-navigation/subscribers-stay-tuned-modal.tsx
index aafa40b2709..c60234ab67c 100644
--- a/apps/dashboard/src/components/side-navigation/subscribers-stay-tuned-modal.tsx
+++ b/apps/dashboard/src/components/side-navigation/subscribers-stay-tuned-modal.tsx
@@ -14,7 +14,7 @@ export const SubscribersStayTunedModal = ({ children }: { children: ReactNode })
-
New subscribers page is coming!
+
New subscribers page is coming!
In the meantime, you can keep using Novu’s powerful APIs to access your subscribers.
diff --git a/apps/dashboard/src/config/index.ts b/apps/dashboard/src/config/index.ts
index a8ce1e6c792..d112dfcb54b 100644
--- a/apps/dashboard/src/config/index.ts
+++ b/apps/dashboard/src/config/index.ts
@@ -15,3 +15,7 @@ if (!CLERK_PUBLISHABLE_KEY) {
}
export const API_HOSTNAME = import.meta.env.VITE_API_HOSTNAME;
+
+export const SEGMENT_KEY = import.meta.env.VITE_SEGMENT_KEY;
+
+export const MIXPANEL_KEY = import.meta.env.VITE_MIXPANEL_KEY;
diff --git a/apps/dashboard/src/hooks/index.ts b/apps/dashboard/src/hooks/index.ts
index 8f26cd8e3ae..e8e4ba07f93 100644
--- a/apps/dashboard/src/hooks/index.ts
+++ b/apps/dashboard/src/hooks/index.ts
@@ -1 +1,2 @@
export * from './use-billing-subscription';
+export * from './use-telemetry';
diff --git a/apps/dashboard/src/hooks/use-telemetry.ts b/apps/dashboard/src/hooks/use-telemetry.ts
new file mode 100644
index 00000000000..d3e23c32755
--- /dev/null
+++ b/apps/dashboard/src/hooks/use-telemetry.ts
@@ -0,0 +1,31 @@
+import { useCallback } from 'react';
+import { useMutation } from '@tanstack/react-query';
+import * as mixpanel from 'mixpanel-browser';
+import { sendTelemetry } from '@/api/telemetry';
+import { MIXPANEL_KEY } from '@/config';
+import { TelemetryEvent } from '@/utils/telemetry';
+
+export const useTelemetry = () => {
+ const { mutate } = useMutation
}>({
+ mutationFn: ({ event, data }) => sendTelemetry(event, data),
+ });
+
+ return useCallback(
+ (event: TelemetryEvent, data?: Record) => {
+ const mixpanelEnabled = !!MIXPANEL_KEY;
+
+ if (mixpanelEnabled) {
+ // @ts-expect-error missing from types
+ const sessionReplayProperties = mixpanel.get_session_recording_properties();
+
+ data = {
+ ...(data || {}),
+ ...sessionReplayProperties,
+ };
+ }
+
+ mutate({ event: `${event} - [DASHBOARD]`, data });
+ },
+ [mutate]
+ );
+};
diff --git a/apps/dashboard/src/utils/segment.ts b/apps/dashboard/src/utils/segment.ts
index 8ba10bcde0c..26a104193f1 100644
--- a/apps/dashboard/src/utils/segment.ts
+++ b/apps/dashboard/src/utils/segment.ts
@@ -1,6 +1,7 @@
import { AnalyticsBrowser } from '@segment/analytics-next';
import type { IUserEntity } from '@novu/shared';
import * as mixpanel from 'mixpanel-browser';
+import { MIXPANEL_KEY, SEGMENT_KEY } from '@/config';
export class SegmentService {
private _segment: AnalyticsBrowser | null = null;
@@ -8,11 +9,11 @@ export class SegmentService {
public _mixpanelEnabled: boolean;
constructor() {
- this._segmentEnabled = !!import.meta.env.VITE_SEGMENT_KEY;
- this._mixpanelEnabled = !!import.meta.env.VITE_MIXPANEL_KEY;
+ this._segmentEnabled = !!SEGMENT_KEY;
+ this._mixpanelEnabled = !!MIXPANEL_KEY;
if (this._mixpanelEnabled) {
- mixpanel.init(import.meta.env.VITE_MIXPANEL_KEY as string, {
+ mixpanel.init(MIXPANEL_KEY as string, {
//@ts-expect-error missing from types
record_sessions_percent: 100,
});
@@ -20,7 +21,7 @@ export class SegmentService {
if (this._segmentEnabled) {
this._segment = AnalyticsBrowser.load({
- writeKey: import.meta.env.VITE_SEGMENT_KEY as string,
+ writeKey: SEGMENT_KEY as string,
});
if (!this._mixpanelEnabled) {
return;
diff --git a/apps/dashboard/src/utils/telemetry.ts b/apps/dashboard/src/utils/telemetry.ts
new file mode 100644
index 00000000000..98ffb208012
--- /dev/null
+++ b/apps/dashboard/src/utils/telemetry.ts
@@ -0,0 +1,3 @@
+export enum TelemetryEvent {
+ SUBSCRIBERS_LINK_CLICKED = 'Subscribers link clicked - [Left navigation]',
+}