mirror of https://github.com/langgenius/dify.git
feat: integrate Amplitude analytics into the application
- Added AmplitudeProvider component to initialize Amplitude analytics. - Integrated Amplitude tracking in layout components and workflow log filter. - Implemented user tracking in AppContextProvider to report user and workspace information to Amplitude. - Added utility functions for tracking events and managing user properties.
This commit is contained in:
parent
9b9588f20d
commit
478aee1c07
|
|
@ -3,6 +3,7 @@ import type { ReactNode } from 'react'
|
|||
import SwrInitializer from '@/app/components/swr-initializer'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import Header from '@/app/components/header'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
|
|
@ -18,6 +19,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
return (
|
||||
<>
|
||||
<GA gaType={GaType.admin} />
|
||||
<AmplitudeProvider />
|
||||
<SwrInitializer>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import Header from './header'
|
|||
import SwrInitor from '@/app/components/swr-initializer'
|
||||
import { AppContextProvider } from '@/context/app-context'
|
||||
import GA, { GaType } from '@/app/components/base/ga'
|
||||
import AmplitudeProvider from '@/app/components/base/amplitude'
|
||||
import HeaderWrapper from '@/app/components/header/header-wrapper'
|
||||
import { EventEmitterContextProvider } from '@/context/event-emitter'
|
||||
import { ProviderContextProvider } from '@/context/provider-context'
|
||||
|
|
@ -13,6 +14,7 @@ const Layout = ({ children }: { children: ReactNode }) => {
|
|||
return (
|
||||
<>
|
||||
<GA gaType={GaType.admin} />
|
||||
<AmplitudeProvider />
|
||||
<SwrInitor>
|
||||
<AppContextProvider>
|
||||
<EventEmitterContextProvider>
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import quarterOfYear from 'dayjs/plugin/quarterOfYear'
|
|||
import type { QueryParam } from './index'
|
||||
import Chip from '@/app/components/base/chip'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { trackEvent } from '../../base/amplitude/utils'
|
||||
dayjs.extend(quarterOfYear)
|
||||
|
||||
const today = dayjs()
|
||||
|
|
@ -37,6 +38,9 @@ const Filter: FC<IFilterProps> = ({ queryParams, setQueryParams }: IFilterProps)
|
|||
value={queryParams.status || 'all'}
|
||||
onSelect={(item) => {
|
||||
setQueryParams({ ...queryParams, status: item.value as string })
|
||||
trackEvent('workflow_log_filter_status_selected', {
|
||||
workflow_log_filter_status: item.value as string,
|
||||
})
|
||||
}}
|
||||
onClear={() => setQueryParams({ ...queryParams, status: 'all' })}
|
||||
items={[{ value: 'all', name: 'All' },
|
||||
|
|
|
|||
|
|
@ -0,0 +1,42 @@
|
|||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import React, { useEffect } from 'react'
|
||||
import * as amplitude from '@amplitude/analytics-browser'
|
||||
|
||||
export type IAmplitudeProps = {
|
||||
apiKey?: string
|
||||
}
|
||||
|
||||
const AmplitudeProvider: FC<IAmplitudeProps> = ({
|
||||
apiKey = '702e89332ab88a7f14e665f417244e9d',
|
||||
}) => {
|
||||
useEffect(() => {
|
||||
// // Only enable in non-CE edition
|
||||
// if (IS_CE_EDITION) {
|
||||
// console.warn('[Amplitude] Amplitude is disabled in CE edition')
|
||||
// return
|
||||
// }
|
||||
|
||||
// Initialize Amplitude
|
||||
amplitude.init(apiKey, {
|
||||
defaultTracking: {
|
||||
sessions: true,
|
||||
pageViews: true,
|
||||
formInteractions: true,
|
||||
fileDownloads: true,
|
||||
},
|
||||
// Enable debug logs in development environment
|
||||
logLevel: process.env.NODE_ENV === 'development' ? amplitude.Types.LogLevel.Debug : amplitude.Types.LogLevel.Warn,
|
||||
})
|
||||
|
||||
// Log initialization success in development
|
||||
if (process.env.NODE_ENV === 'development')
|
||||
console.log('[Amplitude] Initialized successfully, API Key:', apiKey)
|
||||
}, [apiKey])
|
||||
|
||||
// This is a client component that renders nothing
|
||||
return null
|
||||
}
|
||||
|
||||
export default React.memo(AmplitudeProvider)
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
export { default } from './AmplitudeProvider'
|
||||
export { resetUser, setUserId, setUserProperties, trackEvent } from './utils'
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import * as amplitude from '@amplitude/analytics-browser'
|
||||
|
||||
/**
|
||||
* Track custom event
|
||||
* @param eventName Event name
|
||||
* @param eventProperties Event properties (optional)
|
||||
*/
|
||||
export const trackEvent = (eventName: string, eventProperties?: Record<string, any>) => {
|
||||
amplitude.track(eventName, eventProperties)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user ID
|
||||
* @param userId User ID
|
||||
*/
|
||||
export const setUserId = (userId: string) => {
|
||||
amplitude.setUserId(userId)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set user properties
|
||||
* @param properties User properties
|
||||
*/
|
||||
export const setUserProperties = (properties: Record<string, any>) => {
|
||||
const identifyEvent = new amplitude.Identify()
|
||||
Object.entries(properties).forEach(([key, value]) => {
|
||||
identifyEvent.set(key, value)
|
||||
})
|
||||
amplitude.identify(identifyEvent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset user (e.g., when user logs out)
|
||||
*/
|
||||
export const resetUser = () => {
|
||||
amplitude.reset()
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ import { noop } from 'lodash-es'
|
|||
import { setZendeskConversationFields } from '@/app/components/base/zendesk/utils'
|
||||
import { ZENDESK_FIELD_IDS } from '@/config'
|
||||
import { useGlobalPublicStore } from './global-public-context'
|
||||
import { setUserId, setUserProperties } from '@/app/components/base/amplitude'
|
||||
|
||||
export type AppContextValue = {
|
||||
userProfile: UserProfileResponse
|
||||
|
|
@ -159,6 +160,33 @@ export const AppContextProvider: FC<AppContextProviderProps> = ({ children }) =>
|
|||
}, [currentWorkspace?.id])
|
||||
// #endregion Zendesk conversation fields
|
||||
|
||||
// #region Amplitude user tracking
|
||||
useEffect(() => {
|
||||
// Report user info to Amplitude when loaded
|
||||
if (userProfile?.id) {
|
||||
setUserId(userProfile.id)
|
||||
setUserProperties({
|
||||
email: userProfile.email,
|
||||
name: userProfile.name,
|
||||
has_password: userProfile.is_password_set,
|
||||
})
|
||||
}
|
||||
}, [userProfile?.id, userProfile?.email, userProfile?.name, userProfile?.is_password_set])
|
||||
|
||||
useEffect(() => {
|
||||
// Report workspace info to Amplitude when loaded
|
||||
if (currentWorkspace?.id && userProfile?.id) {
|
||||
setUserProperties({
|
||||
workspace_id: currentWorkspace.id,
|
||||
workspace_name: currentWorkspace.name,
|
||||
workspace_plan: currentWorkspace.plan,
|
||||
workspace_status: currentWorkspace.status,
|
||||
workspace_role: currentWorkspace.role,
|
||||
})
|
||||
}
|
||||
}, [currentWorkspace?.id, currentWorkspace?.name, currentWorkspace?.plan, currentWorkspace?.status, currentWorkspace?.role, userProfile?.id])
|
||||
// #endregion Amplitude user tracking
|
||||
|
||||
return (
|
||||
<AppContext.Provider value={{
|
||||
userProfile,
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
"knip": "knip"
|
||||
},
|
||||
"dependencies": {
|
||||
"@amplitude/analytics-browser": "^2.31.3",
|
||||
"@emoji-mart/data": "^1.2.1",
|
||||
"@floating-ui/react": "^0.26.28",
|
||||
"@formatjs/intl-localematcher": "^0.5.10",
|
||||
|
|
|
|||
3376
web/pnpm-lock.yaml
3376
web/pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue