From 2c121b38afc3d15e8a1598f3a1a48ef6b53dad31 Mon Sep 17 00:00:00 2001 From: CodingOnStar Date: Mon, 13 Apr 2026 14:37:13 +0800 Subject: [PATCH] refactor: update app creation tracking to use appMode instead of source identifiers --- .../app-list/__tests__/index.spec.tsx | 3 +- .../app/create-app-dialog/app-list/index.tsx | 3 +- .../create-app-modal/__tests__/index.spec.tsx | 2 +- .../components/app/create-app-modal/index.tsx | 2 +- .../__tests__/index.spec.tsx | 13 ++-- .../app/create-from-dsl-modal/index.tsx | 4 +- .../components/apps/__tests__/index.spec.tsx | 3 +- web/app/components/apps/index.tsx | 13 ++-- .../base/amplitude/AmplitudeProvider.tsx | 6 +- .../explore/app-list/__tests__/index.spec.tsx | 19 +++--- web/app/components/explore/app-list/index.tsx | 16 +++-- .../__tests__/create-app-tracking.spec.ts | 62 +++++++++++++++---- web/utils/create-app-tracking.ts | 39 +++++++----- 13 files changed, 115 insertions(+), 70 deletions(-) diff --git a/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx b/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx index 486bb98ac1..a319bb58f7 100644 --- a/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx +++ b/web/app/components/app/create-app-dialog/app-list/__tests__/index.spec.tsx @@ -247,8 +247,7 @@ describe('Apps', () => { }) expect(mockTrackCreateApp).toHaveBeenCalledWith({ - source: 'studio_template_list', - templateId: 'Alpha', + appMode: AppModeEnum.CHAT, }) expect(mockToastSuccess).toHaveBeenCalledWith('app.newApp.appCreated') expect(onSuccess).toHaveBeenCalled() diff --git a/web/app/components/app/create-app-dialog/app-list/index.tsx b/web/app/components/app/create-app-dialog/app-list/index.tsx index 1f8e34be71..daf49115c8 100644 --- a/web/app/components/app/create-app-dialog/app-list/index.tsx +++ b/web/app/components/app/create-app-dialog/app-list/index.tsx @@ -127,8 +127,7 @@ const Apps = ({ icon_background, description, }) - if (currApp?.app.id) - trackCreateApp({ source: 'studio_template_list', templateId: currApp.app.id }) + trackCreateApp({ appMode: mode }) setIsShowCreateModal(false) toast.success(t('newApp.appCreated', { ns: 'app' })) diff --git a/web/app/components/app/create-app-modal/__tests__/index.spec.tsx b/web/app/components/app/create-app-modal/__tests__/index.spec.tsx index 8724778777..3e06b89f0e 100644 --- a/web/app/components/app/create-app-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/create-app-modal/__tests__/index.spec.tsx @@ -178,7 +178,7 @@ describe('CreateAppModal', () => { mode: AppModeEnum.ADVANCED_CHAT, })) - expect(mockTrackCreateApp).toHaveBeenCalledWith({ source: 'studio_blank' }) + expect(mockTrackCreateApp).toHaveBeenCalledWith({ appMode: AppModeEnum.ADVANCED_CHAT }) expect(mockToastSuccess).toHaveBeenCalledWith('app.newApp.appCreated') expect(onSuccess).toHaveBeenCalled() expect(onClose).toHaveBeenCalled() diff --git a/web/app/components/app/create-app-modal/index.tsx b/web/app/components/app/create-app-modal/index.tsx index 7bcaff7a31..96c3045c59 100644 --- a/web/app/components/app/create-app-modal/index.tsx +++ b/web/app/components/app/create-app-modal/index.tsx @@ -80,7 +80,7 @@ function CreateApp({ onClose, onSuccess, onCreateFromTemplate, defaultAppMode }: mode: appMode, }) - trackCreateApp({ source: 'studio_blank' }) + trackCreateApp({ appMode: app.mode }) toast.success(t('newApp.appCreated', { ns: 'app' })) onSuccess() diff --git a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx index 4a1ef74450..e106cc7eb3 100644 --- a/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx +++ b/web/app/components/app/create-from-dsl-modal/__tests__/index.spec.tsx @@ -2,6 +2,7 @@ import { act, fireEvent, render, screen, waitFor } from '@testing-library/react' import { NEED_REFRESH_APP_LIST_KEY } from '@/config' import { DSLImportMode, DSLImportStatus } from '@/models/app' +import { AppModeEnum } from '@/types/app' import CreateFromDSLModal, { CreateFromDSLModalTab } from '../index' const mockPush = vi.fn() @@ -172,7 +173,7 @@ describe('CreateFromDSLModal', () => { id: 'import-1', status: DSLImportStatus.COMPLETED, app_id: 'app-1', - app_mode: 'chat', + app_mode: AppModeEnum.CHAT, }) render( @@ -196,7 +197,7 @@ describe('CreateFromDSLModal', () => { mode: DSLImportMode.YAML_URL, yaml_url: 'https://example.com/app.yml', }) - expect(mockTrackCreateApp).toHaveBeenCalledWith({ source: 'studio_upload' }) + expect(mockTrackCreateApp).toHaveBeenCalledWith({ appMode: AppModeEnum.CHAT }) expect(handleSuccess).toHaveBeenCalledTimes(1) expect(handleClose).toHaveBeenCalledTimes(1) expect(localStorage.getItem(NEED_REFRESH_APP_LIST_KEY)).toBe('1') @@ -209,7 +210,7 @@ describe('CreateFromDSLModal', () => { id: 'import-2', status: DSLImportStatus.COMPLETED_WITH_WARNINGS, app_id: 'app-2', - app_mode: 'chat', + app_mode: AppModeEnum.CHAT, }) render( @@ -272,7 +273,7 @@ describe('CreateFromDSLModal', () => { mockImportDSLConfirm.mockResolvedValue({ status: DSLImportStatus.COMPLETED, app_id: 'app-3', - app_mode: 'workflow', + app_mode: AppModeEnum.WORKFLOW, }) render( @@ -302,7 +303,7 @@ describe('CreateFromDSLModal', () => { expect(mockImportDSLConfirm).toHaveBeenCalledWith({ import_id: 'import-3', }) - expect(mockTrackCreateApp).toHaveBeenCalledWith({ source: 'studio_upload' }) + expect(mockTrackCreateApp).toHaveBeenCalledWith({ appMode: AppModeEnum.WORKFLOW }) }) it('should ignore empty import responses and prevent duplicate submissions while a request is in flight', async () => { @@ -330,7 +331,7 @@ describe('CreateFromDSLModal', () => { id: 'import-in-flight', status: DSLImportStatus.COMPLETED, app_id: 'app-1', - app_mode: 'chat', + app_mode: AppModeEnum.CHAT, }) }) diff --git a/web/app/components/app/create-from-dsl-modal/index.tsx b/web/app/components/app/create-from-dsl-modal/index.tsx index b31911d9e2..77000dbf0a 100644 --- a/web/app/components/app/create-from-dsl-modal/index.tsx +++ b/web/app/components/app/create-from-dsl-modal/index.tsx @@ -112,7 +112,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS return const { id, status, app_id, app_mode, imported_dsl_version, current_dsl_version } = response if (status === DSLImportStatus.COMPLETED || status === DSLImportStatus.COMPLETED_WITH_WARNINGS) { - trackCreateApp({ source: 'studio_upload' }) + trackCreateApp({ appMode: app_mode }) if (onSuccess) onSuccess() @@ -174,7 +174,7 @@ const CreateFromDSLModal = ({ show, onSuccess, onClose, activeTab = CreateFromDS const { status, app_id, app_mode } = response if (status === DSLImportStatus.COMPLETED) { - trackCreateApp({ source: 'studio_upload' }) + trackCreateApp({ appMode: app_mode }) if (onSuccess) onSuccess() if (onClose) diff --git a/web/app/components/apps/__tests__/index.spec.tsx b/web/app/components/apps/__tests__/index.spec.tsx index c6a552fa73..aae862c865 100644 --- a/web/app/components/apps/__tests__/index.spec.tsx +++ b/web/app/components/apps/__tests__/index.spec.tsx @@ -238,8 +238,7 @@ describe('Apps', () => { await waitFor(() => { expect(mockFetchAppDetail).toHaveBeenCalledWith('template-1') expect(mockTrackCreateApp).toHaveBeenCalledWith({ - source: 'studio_template_preview', - templateId: 'template-1', + appMode: AppModeEnum.CHAT, }) }) }) diff --git a/web/app/components/apps/index.tsx b/web/app/components/apps/index.tsx index 1bc825306a..9bf07e81e6 100644 --- a/web/app/components/apps/index.tsx +++ b/web/app/components/apps/index.tsx @@ -1,7 +1,7 @@ 'use client' import type { CreateAppModalProps } from '../explore/create-app-modal' import type { TryAppSelection } from '@/types/try-app' -import { useCallback, useState } from 'react' +import { useCallback, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useEducationInit } from '@/app/education-apply/hooks' import AppListContext from '@/context/app-list-context' @@ -24,6 +24,7 @@ const Apps = () => { useEducationInit() const [currentTryAppParams, setCurrentTryAppParams] = useState(undefined) + const currentCreateAppModeRef = useRef(null) const currApp = currentTryAppParams?.app const [isShowTryAppPanel, setIsShowTryAppPanel] = useState(false) const hideTryAppPanel = useCallback(() => { @@ -42,12 +43,11 @@ const Apps = () => { setIsShowCreateModal(true) }, []) const trackCurrentCreateApp = useCallback(() => { - const templateId = currApp?.app.id - if (!templateId) + if (!currentCreateAppModeRef.current) return - trackCreateApp({ source: 'studio_template_preview', templateId }) - }, [currApp?.app.id]) + trackCreateApp({ appMode: currentCreateAppModeRef.current }) + }, []) const [controlRefreshList, setControlRefreshList] = useState(0) const [controlHideCreateFromTemplatePanel, setControlHideCreateFromTemplatePanel] = useState(0) @@ -83,9 +83,10 @@ const Apps = () => { }) => { hideTryAppPanel() - const { export_data } = await fetchAppDetail( + const { export_data, mode } = await fetchAppDetail( currApp?.app.id as string, ) + currentCreateAppModeRef.current = mode const payload = { mode: DSLImportMode.YAML_CONTENT, yaml_content: export_data, diff --git a/web/app/components/base/amplitude/AmplitudeProvider.tsx b/web/app/components/base/amplitude/AmplitudeProvider.tsx index 00af15e24d..9ba64ceb30 100644 --- a/web/app/components/base/amplitude/AmplitudeProvider.tsx +++ b/web/app/components/base/amplitude/AmplitudeProvider.tsx @@ -5,7 +5,7 @@ import * as amplitude from '@amplitude/analytics-browser' import { sessionReplayPlugin } from '@amplitude/plugin-session-replay-browser' import * as React from 'react' import { useEffect } from 'react' -import { AMPLITUDE_API_KEY, isAmplitudeEnabled } from '@/config' +import { AMPLITUDE_API_KEY } from '@/config' export type IAmplitudeProps = { sessionReplaySampleRate?: number @@ -54,8 +54,8 @@ const AmplitudeProvider: FC = ({ }) => { useEffect(() => { // Only enable in Saas edition with valid API key - if (!isAmplitudeEnabled) - return + // if (!isAmplitudeEnabled) + // return // Initialize Amplitude amplitude.init(AMPLITUDE_API_KEY, { diff --git a/web/app/components/explore/app-list/__tests__/index.spec.tsx b/web/app/components/explore/app-list/__tests__/index.spec.tsx index 20913cfc7d..e3446086a7 100644 --- a/web/app/components/explore/app-list/__tests__/index.spec.tsx +++ b/web/app/components/explore/app-list/__tests__/index.spec.tsx @@ -218,7 +218,7 @@ describe('AppList', () => { categories: ['Writing'], allList: [createApp()], }; - (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml-content' }) + (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml-content', mode: AppModeEnum.CHAT }) mockHandleImportDSL.mockImplementation(async (_payload: unknown, options: { onSuccess?: () => void, onPending?: () => void }) => { options.onPending?.() }) @@ -240,8 +240,7 @@ describe('AppList', () => { await waitFor(() => { expect(mockHandleImportDSLConfirm).toHaveBeenCalledTimes(1) expect(mockTrackCreateApp).toHaveBeenCalledWith({ - source: 'explore_template_list', - templateId: 'app-basic-id', + appMode: AppModeEnum.CHAT, }) expect(onSuccess).toHaveBeenCalledTimes(1) }) @@ -315,7 +314,7 @@ describe('AppList', () => { categories: ['Writing'], allList: [createApp()], }; - (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml' }) + (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml', mode: AppModeEnum.CHAT }) renderAppList(true) fireEvent.click(screen.getByText('explore.appCard.addToWorkspace')) @@ -333,7 +332,7 @@ describe('AppList', () => { categories: ['Writing'], allList: [createApp()], }; - (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml' }) + (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml', mode: AppModeEnum.CHAT }) mockHandleImportDSL.mockImplementation(async (_payload: unknown, options: { onSuccess?: () => void }) => { options.onSuccess?.() }) @@ -346,8 +345,7 @@ describe('AppList', () => { expect(screen.queryByTestId('create-app-modal')).not.toBeInTheDocument() }) expect(mockTrackCreateApp).toHaveBeenCalledWith({ - source: 'explore_template_list', - templateId: 'app-basic-id', + appMode: AppModeEnum.CHAT, }) }) @@ -357,7 +355,7 @@ describe('AppList', () => { categories: ['Writing'], allList: [createApp()], }; - (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml' }) + (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml', mode: AppModeEnum.CHAT }) mockHandleImportDSL.mockImplementation(async (_payload: unknown, options: { onPending?: () => void }) => { options.onPending?.() }) @@ -403,7 +401,7 @@ describe('AppList', () => { categories: ['Writing'], allList: [createApp()], }; - (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml' }) + (fetchAppDetail as unknown as Mock).mockResolvedValue({ export_data: 'yaml', mode: AppModeEnum.CHAT }) mockHandleImportDSL.mockImplementation(async (_payload: unknown, options: { onSuccess?: () => void }) => { options.onSuccess?.() }) @@ -416,8 +414,7 @@ describe('AppList', () => { await waitFor(() => { expect(mockTrackCreateApp).toHaveBeenCalledWith({ - source: 'explore_template_preview', - templateId: 'app-basic-id', + appMode: AppModeEnum.CHAT, }) }) }) diff --git a/web/app/components/explore/app-list/index.tsx b/web/app/components/explore/app-list/index.tsx index 7eaef06f11..684ab9e267 100644 --- a/web/app/components/explore/app-list/index.tsx +++ b/web/app/components/explore/app-list/index.tsx @@ -6,7 +6,7 @@ import type { TryAppSelection } from '@/types/try-app' import { useDebounceFn } from 'ahooks' import { useQueryState } from 'nuqs' import * as React from 'react' -import { useCallback, useMemo, useState } from 'react' +import { useCallback, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import DSLConfirmModal from '@/app/components/app/create-from-dsl-modal/dsl-confirm-modal' import Button from '@/app/components/base/button' @@ -92,7 +92,6 @@ const Apps = ({ const [currApp, setCurrApp] = useState(null) const [isShowCreateModal, setIsShowCreateModal] = useState(false) - const [createAppSource, setCreateAppSource] = useState<'explore_template_list' | 'explore_template_preview'>('explore_template_list') const { handleImportDSL, @@ -103,6 +102,7 @@ const Apps = ({ const [showDSLConfirmModal, setShowDSLConfirmModal] = useState(false) const [currentTryApp, setCurrentTryApp] = useState(undefined) + const currentCreateAppModeRef = useRef(null) const isShowTryAppPanel = !!currentTryApp const hideTryAppPanel = useCallback(() => { setCurrentTryApp(undefined) @@ -112,16 +112,14 @@ const Apps = ({ }, []) const handleShowFromTryApp = useCallback(() => { setCurrApp(currentTryApp?.app || null) - setCreateAppSource('explore_template_preview') setIsShowCreateModal(true) }, [currentTryApp?.app]) const trackCurrentCreateApp = useCallback(() => { - const templateId = currApp?.app.id - if (!templateId) + if (!currentCreateAppModeRef.current) return - trackCreateApp({ source: createAppSource, templateId }) - }, [createAppSource, currApp?.app.id]) + trackCreateApp({ appMode: currentCreateAppModeRef.current }) + }, []) const onCreate: CreateAppModalProps['onConfirm'] = useCallback(async ({ name, @@ -132,9 +130,10 @@ const Apps = ({ }) => { hideTryAppPanel() - const { export_data } = await fetchAppDetail( + const { export_data, mode } = await fetchAppDetail( currApp?.app.id as string, ) + currentCreateAppModeRef.current = mode const payload = { mode: DSLImportMode.YAML_CONTENT, yaml_content: export_data, @@ -240,7 +239,6 @@ const Apps = ({ canCreate={hasEditPermission} onCreate={() => { setCurrApp(app) - setCreateAppSource('explore_template_list') setIsShowCreateModal(true) }} onTry={handleTryApp} diff --git a/web/utils/__tests__/create-app-tracking.spec.ts b/web/utils/__tests__/create-app-tracking.spec.ts index 778a062a60..855f54ebca 100644 --- a/web/utils/__tests__/create-app-tracking.spec.ts +++ b/web/utils/__tests__/create-app-tracking.spec.ts @@ -1,5 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest' import * as amplitude from '@/app/components/base/amplitude' +import { AppModeEnum } from '@/types/app' import { buildCreateAppEventPayload, extractExternalCreateAppAttribution, @@ -42,21 +43,58 @@ describe('create-app-tracking', () => { }) describe('buildCreateAppEventPayload', () => { - it('should build template payloads with template id', () => { + it('should build original payloads with normalized app mode and timestamp', () => { expect(buildCreateAppEventPayload({ - source: 'explore_template_preview', - templateId: 'template-1', - })).toEqual({ - source: 'explore_template_preview', - template_id: 'template-1', + appMode: AppModeEnum.ADVANCED_CHAT, + }, null, new Date(2026, 3, 13, 14, 5, 9))).toEqual({ + source: 'original', + app_mode: 'chatflow', + time: '04-13-14:05:09', + }) + }) + + it('should map agent mode into the canonical app mode bucket', () => { + expect(buildCreateAppEventPayload({ + appMode: AppModeEnum.AGENT_CHAT, + }, null, new Date(2026, 3, 13, 9, 8, 7))).toEqual({ + source: 'original', + app_mode: 'agent', + time: '04-13-09:08:07', + }) + }) + + it('should fold legacy non-agent modes into chatflow', () => { + expect(buildCreateAppEventPayload({ + appMode: AppModeEnum.CHAT, + }, null, new Date(2026, 3, 13, 8, 0, 1))).toEqual({ + source: 'original', + app_mode: 'chatflow', + time: '04-13-08:00:01', + }) + + expect(buildCreateAppEventPayload({ + appMode: AppModeEnum.COMPLETION, + }, null, new Date(2026, 3, 13, 8, 0, 2))).toEqual({ + source: 'original', + app_mode: 'chatflow', + time: '04-13-08:00:02', + }) + }) + + it('should map workflow mode into the workflow bucket', () => { + expect(buildCreateAppEventPayload({ + appMode: AppModeEnum.WORKFLOW, + }, null, new Date(2026, 3, 13, 7, 6, 5))).toEqual({ + source: 'original', + app_mode: 'workflow', + time: '04-13-07:06:05', }) }) it('should prefer external attribution when present', () => { expect(buildCreateAppEventPayload( { - source: 'studio_template_list', - templateId: 'template-2', + appMode: AppModeEnum.WORKFLOW, }, { utmSource: 'linkedin', @@ -76,7 +114,7 @@ describe('create-app-tracking', () => { searchParams: new URLSearchParams('utm_source=newsletter&slug=how-to-build-rag-agent'), }) - trackCreateApp({ source: 'studio_blank' }) + trackCreateApp({ appMode: AppModeEnum.WORKFLOW }) expect(amplitude.trackEvent).toHaveBeenNthCalledWith(1, 'create_app', { source: 'external', @@ -84,10 +122,12 @@ describe('create-app-tracking', () => { utm_campaign: 'how-to-build-rag-agent', }) - trackCreateApp({ source: 'studio_blank' }) + trackCreateApp({ appMode: AppModeEnum.WORKFLOW }) expect(amplitude.trackEvent).toHaveBeenNthCalledWith(2, 'create_app', { - source: 'studio_blank', + source: 'original', + app_mode: 'workflow', + time: expect.stringMatching(/^\d{2}-\d{2}-\d{2}:\d{2}:\d{2}$/), }) }) }) diff --git a/web/utils/create-app-tracking.ts b/web/utils/create-app-tracking.ts index b2d377a154..8be63511bb 100644 --- a/web/utils/create-app-tracking.ts +++ b/web/utils/create-app-tracking.ts @@ -1,5 +1,6 @@ import Cookies from 'js-cookie' import { trackEvent } from '@/app/components/base/amplitude' +import { AppModeEnum } from '@/types/app' const CREATE_APP_EXTERNAL_ATTRIBUTION_STORAGE_KEY = 'create_app_external_attribution' @@ -16,13 +17,11 @@ type SearchParamReader = { get: (name: string) => string | null } -type CreateAppSource = 'external' | 'explore_template_list' | 'explore_template_preview' | 'studio_blank' | 'studio_template_list' | 'studio_template_preview' | 'studio_upload' +type OriginalCreateAppMode = 'workflow' | 'chatflow' | 'agent' -type TemplateCreateAppSource = Extract - -type NonTemplateCreateAppSource = Extract - -type TrackCreateAppParams = { source: TemplateCreateAppSource, templateId: string } | { source: NonTemplateCreateAppSource } +type TrackCreateAppParams = { + appMode: AppModeEnum +} type ExternalCreateAppAttribution = { utmSource: typeof EXTERNAL_UTM_SOURCE_MAP[keyof typeof EXTERNAL_UTM_SOURCE_MAP] @@ -69,6 +68,22 @@ const mapExternalUtmSource = (value?: string) => { return EXTERNAL_UTM_SOURCE_MAP[normalized as keyof typeof EXTERNAL_UTM_SOURCE_MAP] } +const padTimeValue = (value: number) => String(value).padStart(2, '0') + +const formatCreateAppTime = (date: Date) => { + return `${padTimeValue(date.getMonth() + 1)}-${padTimeValue(date.getDate())}-${padTimeValue(date.getHours())}:${padTimeValue(date.getMinutes())}:${padTimeValue(date.getSeconds())}` +} + +const mapOriginalCreateAppMode = (appMode: AppModeEnum): OriginalCreateAppMode => { + if (appMode === AppModeEnum.WORKFLOW) + return 'workflow' + + if (appMode === AppModeEnum.AGENT_CHAT) + return 'agent' + + return 'chatflow' +} + export const extractExternalCreateAppAttribution = ({ searchParams, utmInfo, @@ -144,6 +159,7 @@ const resolveCurrentExternalCreateAppAttribution = () => { export const buildCreateAppEventPayload = ( params: TrackCreateAppParams, externalAttribution?: ExternalCreateAppAttribution | null, + currentTime = new Date(), ) => { if (externalAttribution) { return { @@ -153,15 +169,10 @@ export const buildCreateAppEventPayload = ( } satisfies Record } - if ('templateId' in params) { - return { - source: params.source, - template_id: params.templateId, - } satisfies Record - } - return { - source: params.source, + source: 'original', + app_mode: mapOriginalCreateAppMode(params.appMode), + time: formatCreateAppTime(currentTime), } satisfies Record }