diff --git a/web/app/components/app/create-app-dialog/app-card/__tests__/index.spec.tsx b/web/app/components/app/create-app-dialog/app-card/__tests__/index.spec.tsx
index 898bde5c71..2d76c12b68 100644
--- a/web/app/components/app/create-app-dialog/app-card/__tests__/index.spec.tsx
+++ b/web/app/components/app/create-app-dialog/app-card/__tests__/index.spec.tsx
@@ -3,6 +3,8 @@ import type { App } from '@/models/explore'
import type { AppIconType } from '@/types/app'
import { render, screen, within } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
+import { trackEvent } from '@/app/components/base/amplitude'
+import AppListContext from '@/context/app-list-context'
import { AppModeEnum } from '@/types/app'
import AppCard from '../index'
@@ -10,6 +12,10 @@ vi.mock('@heroicons/react/20/solid', () => ({
PlusIcon: ({ className }: any) =>
+
,
}))
+vi.mock('@/app/components/base/amplitude', () => ({
+ trackEvent: vi.fn(),
+}))
+
const mockApp: App = {
can_trial: true,
app: {
@@ -38,12 +44,30 @@ const mockApp: App = {
}
describe('AppCard', () => {
+ const mockSetShowTryAppPanel = vi.fn()
+ const mockTrackEvent = vi.mocked(trackEvent)
const defaultProps = {
app: mockApp,
canCreate: true,
onCreate: vi.fn(),
}
+ const renderWithProvider = (ui: React.ReactElement) => {
+ return render(
+ // eslint-disable-next-line react/no-context-provider
+
+ {ui}
+ ,
+ )
+ }
+
beforeEach(() => {
vi.clearAllMocks()
})
@@ -217,6 +241,25 @@ describe('AppCard', () => {
// Note: Card click doesn't trigger onCreate, only the button does
expect(mockOnCreate).not.toHaveBeenCalled()
})
+
+ it('should track preview event and open try app panel when detail button is clicked', async () => {
+ renderWithProvider()
+
+ const button = screen.getByRole('button', { name: /explore\.appCard\.try/ })
+ await userEvent.click(button)
+
+ expect(mockTrackEvent).toHaveBeenCalledWith('preview_template', {
+ template_id: mockApp.app_id,
+ template_name: mockApp.app.name,
+ template_mode: mockApp.app.mode,
+ template_category: mockApp.category,
+ page: 'studio',
+ })
+ expect(mockSetShowTryAppPanel).toHaveBeenCalledWith(true, {
+ appId: mockApp.app_id,
+ app: mockApp,
+ })
+ })
})
describe('Keyboard Accessibility', () => {
diff --git a/web/app/components/app/create-app-dialog/app-card/index.tsx b/web/app/components/app/create-app-dialog/app-card/index.tsx
index 9241461608..fef7199ca2 100644
--- a/web/app/components/app/create-app-dialog/app-card/index.tsx
+++ b/web/app/components/app/create-app-dialog/app-card/index.tsx
@@ -6,6 +6,7 @@ import { RiInformation2Line } from '@remixicon/react'
import { useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useContextSelector } from 'use-context-selector'
+import { trackEvent } from '@/app/components/base/amplitude'
import AppIcon from '@/app/components/base/app-icon'
import { Button } from '@/app/components/base/ui/button'
import AppListContext from '@/context/app-list-context'
@@ -28,11 +29,16 @@ const AppCard = ({
const { systemFeatures } = useGlobalPublicStore()
const isTrialApp = app.can_trial && systemFeatures.enable_trial_app
const setShowTryAppPanel = useContextSelector(AppListContext, ctx => ctx.setShowTryAppPanel)
- const showTryAPPPanel = useCallback((appId: string) => {
- return () => {
- setShowTryAppPanel?.(true, { appId, app })
- }
- }, [setShowTryAppPanel, app.category])
+ const handleShowTryAppPanel = useCallback(() => {
+ trackEvent('preview_template', {
+ template_id: app.app_id,
+ template_name: appBasicInfo.name,
+ template_mode: appBasicInfo.mode,
+ template_category: app.category,
+ page: 'studio',
+ })
+ setShowTryAppPanel?.(true, { appId: app.app_id, app })
+ }, [setShowTryAppPanel, app, appBasicInfo])
return (
@@ -71,7 +77,7 @@ const AppCard = ({
{t('newApp.useTemplate', { ns: 'app' })}
)}
-