From 85ac55d2048e8ad792eb5c3f904287b407234a23 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Wed, 7 Aug 2024 17:42:05 +0400 Subject: [PATCH 1/6] Login/Onboard events Signed-off-by: Kristina Fefelova --- plugins/login-resources/src/analytics.ts | 28 +++++++++++++++++++ plugins/login-resources/src/utils.ts | 21 ++++++++++---- plugins/onboard-resources/src/analytics.ts | 18 ++++++++++++ .../src/components/OnboardForm.svelte | 3 ++ 4 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 plugins/login-resources/src/analytics.ts create mode 100644 plugins/onboard-resources/src/analytics.ts diff --git a/plugins/login-resources/src/analytics.ts b/plugins/login-resources/src/analytics.ts new file mode 100644 index 00000000000..b50886df638 --- /dev/null +++ b/plugins/login-resources/src/analytics.ts @@ -0,0 +1,28 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +export const LoginEvents = { + SignUpEmail: 'signup.viaEmail', + SignUpGoogle: 'signup.viaGoogle', + SignUpGithub: 'signup.viaGitHub', + + LoginPassword: 'login.viaPassword', + LoginOtp: 'login.viaOtp', + LoginGoogle: 'login.viaGoogle', + LoginGithub: 'login.viaGitHub', + + CreateWorkspace: 'onboard.createWorkspace', + SelectWorkspace: 'onboard.selectWorkspace', +} \ No newline at end of file diff --git a/plugins/login-resources/src/utils.ts b/plugins/login-resources/src/utils.ts index 558f8a97707..0f373add6e6 100644 --- a/plugins/login-resources/src/utils.ts +++ b/plugins/login-resources/src/utils.ts @@ -41,6 +41,7 @@ import { workbenchId } from '@hcengineering/workbench' import login from './plugin' import { type Pages } from './index' +import { LoginEvents } from './analytics' /** * Perform a login operation to required workspace with user credentials. @@ -68,14 +69,16 @@ export async function doLogin (email: string, password: string): Promise<[Status const result = await response.json() console.log('login result', result) if (result.error == null) { - Analytics.handleEvent('login') + Analytics.handleEvent(LoginEvents.LoginPassword, { email, ok: true }) Analytics.setUser(email) } else { + Analytics.handleEvent(LoginEvents.LoginPassword, { email, ok: false }) await handleStatusError('Login error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { console.error('login error', err) + Analytics.handleEvent(LoginEvents.LoginPassword, { email, ok: false }) Analytics.handleError(err) return [unknownError(err), undefined] } @@ -108,14 +111,16 @@ export async function signUp ( }) const result = await response.json() if (result.error == null) { - Analytics.handleEvent('signup') + Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: true }) Analytics.setUser(email) } else { + Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: false }) await handleStatusError('Sign up error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { Analytics.handleError(err) + Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: false }) return [unknownError(err), undefined] } } @@ -188,13 +193,15 @@ export async function createWorkspace ( }) const result = await response.json() if (result.error == null) { - Analytics.handleEvent('create workspace') + Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: true}) Analytics.setWorkspace(workspaceName) } else { + Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: false}) await handleStatusError('Create workspace error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { + Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: false}) Analytics.handleError(err) return [unknownError(err), undefined] } @@ -318,13 +325,15 @@ export async function selectWorkspace ( }) const result = await response.json() if (result.error == null) { - Analytics.handleEvent('Select workspace') + Analytics.handleEvent(LoginEvents.SelectWorkspace, { name: workspace, ok: true }) Analytics.setWorkspace(workspace) } else { + Analytics.handleEvent(LoginEvents.SelectWorkspace, { name: workspace, ok: false }) await handleStatusError('Select workspace error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { + Analytics.handleEvent(LoginEvents.SelectWorkspace, { name: workspace, ok: false }) Analytics.handleError(err) return [unknownError(err), undefined] } @@ -1014,14 +1023,16 @@ export async function loginWithOtp (email: string, otp: string): Promise<[Status const result = await response.json() if (result.error == null) { - Analytics.handleEvent('loginWithOtp') + Analytics.handleEvent(LoginEvents.LoginOtp, { email, ok: true }) Analytics.setUser(email) } else { + Analytics.handleEvent(LoginEvents.LoginOtp, { email, ok: false }) await handleStatusError('Login with otp error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { console.error('Login with otp error', err) + Analytics.handleEvent(LoginEvents.LoginOtp, { email, ok: false }) Analytics.handleError(err) return [unknownError(err), undefined] } diff --git a/plugins/onboard-resources/src/analytics.ts b/plugins/onboard-resources/src/analytics.ts new file mode 100644 index 00000000000..639701e0b58 --- /dev/null +++ b/plugins/onboard-resources/src/analytics.ts @@ -0,0 +1,18 @@ +// +// Copyright © 2024 Hardcore Engineering Inc. +// +// Licensed under the Eclipse Public License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. You may +// obtain a copy of the License at https://www.eclipse.org/legal/epl-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// +// See the License for the specific language governing permissions and +// limitations under the License. +// + +export let OnboardEvents = { + StartHuly: 'onboard.startHuly', +} \ No newline at end of file diff --git a/plugins/onboard-resources/src/components/OnboardForm.svelte b/plugins/onboard-resources/src/components/OnboardForm.svelte index 22c211a7486..bb38fe07b4a 100644 --- a/plugins/onboard-resources/src/components/OnboardForm.svelte +++ b/plugins/onboard-resources/src/components/OnboardForm.svelte @@ -17,6 +17,7 @@ import { getAccount, getWorkspaces, navigateToWorkspace } from '@hcengineering/login-resources' import { OK } from '@hcengineering/platform' import { onMount } from 'svelte' + import { Analytics } from '@hcengineering/analytics' import { OnboardSteps } from '../index' import onboard from '../plugin' @@ -25,6 +26,7 @@ import Form from './Form.svelte' import OnboardUserForm from './OnboardUserForm.svelte' import OnboardWorkspaceForm from './OnboardWorkspaceForm.svelte' + import { OnboardEvents } from '../analytics' const steps = Object.values(OnboardSteps) @@ -68,6 +70,7 @@ i18n: onboard.string.StartUsingHuly, func: async () => { if (account !== undefined && isWorkspaceLoginInfo(account)) { + Analytics.handleEvent(OnboardEvents.StartHuly) navigateToWorkspace(account.workspace, account) } } From 243cd6b6e1ab9203dfbe9b4306a96ace94416b99 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Wed, 7 Aug 2024 17:52:07 +0400 Subject: [PATCH 2/6] Settings events Signed-off-by: Kristina Fefelova --- plugins/setting-resources/src/analytics.ts | 5 +++++ plugins/setting-resources/src/components/Settings.svelte | 5 +++++ 2 files changed, 10 insertions(+) create mode 100644 plugins/setting-resources/src/analytics.ts diff --git a/plugins/setting-resources/src/analytics.ts b/plugins/setting-resources/src/analytics.ts new file mode 100644 index 00000000000..cd6a5a86db4 --- /dev/null +++ b/plugins/setting-resources/src/analytics.ts @@ -0,0 +1,5 @@ +export enum SettingsEvents { + SelectWorkspace = 'settings.SelectWorkspace', + InviteToWorkspace = 'settings.InviteToWorkspace', + SignOut = 'settings.SignOut', +} \ No newline at end of file diff --git a/plugins/setting-resources/src/components/Settings.svelte b/plugins/setting-resources/src/components/Settings.svelte index 77bc4123b8d..569a9e7a115 100644 --- a/plugins/setting-resources/src/components/Settings.svelte +++ b/plugins/setting-resources/src/components/Settings.svelte @@ -40,6 +40,8 @@ import { NavFooter } from '@hcengineering/workbench-resources' import { ComponentType, onDestroy } from 'svelte' import { clearSettingsStore, settingsStore, type SettingsStore } from '../store' + import { Analytics } from '@hcengineering/analytics' + import { SettingsEvents } from '../analytics' let category: SettingsCategory | undefined let categoryId: string = '' @@ -99,12 +101,15 @@ setMetadataLocalStorage(login.metadata.LoginEndpoint, null) setMetadataLocalStorage(login.metadata.LoginEmail, null) void closeClient() + Analytics.handleEvent(SettingsEvents.SignOut) navigate({ path: [loginId] }) } function selectWorkspace (): void { + Analytics.handleEvent(SettingsEvents.SelectWorkspace) navigate({ path: [loginId, 'selectWorkspace'] }) } function inviteWorkspace (): void { + Analytics.handleEvent(SettingsEvents.InviteToWorkspace) showPopup(login.component.InviteLink, {}) } From 2eb159ad8931400d66a86b512cc82b100c37a5f0 Mon Sep 17 00:00:00 2001 From: Kristina Fefelova Date: Thu, 8 Aug 2024 17:50:23 +0400 Subject: [PATCH 3/6] Create Issue/Project events Signed-off-by: Kristina Fefelova --- dev/prod/src/analytics/analyticsCollector.ts | 8 +++++--- dev/prod/src/analytics/posthog.ts | 7 +++++-- dev/prod/src/analytics/sentry.ts | 3 +++ models/tracker/src/actions.ts | 13 +++++++++---- packages/analytics/src/index.ts | 15 ++++++++++++--- plugins/login-resources/src/analytics.ts | 4 ++-- plugins/login-resources/src/utils.ts | 12 ++++++------ plugins/onboard-resources/src/analytics.ts | 6 +++--- .../src/components/Settings.svelte | 3 +-- .../src/analytics.ts | 4 ++-- plugins/setting/src/index.ts | 1 + .../src/components/CreateIssue.svelte | 9 ++++++++- .../src/components/NewIssueHeader.svelte | 5 ++++- .../src/components/issues/IssuesView.svelte | 3 ++- .../src/components/projects/CreateProject.svelte | 9 +++++++-- plugins/tracker/src/analytics.ts | 13 +++++++++++++ plugins/tracker/src/index.ts | 1 + plugins/view-resources/src/actions.ts | 2 +- .../src/components/ViewletContentView.svelte | 2 ++ .../src/components/list/List.svelte | 2 ++ .../src/components/list/ListCategories.svelte | 2 ++ .../src/components/list/ListCategory.svelte | 2 ++ .../src/components/list/ListHeader.svelte | 6 ++++++ .../src/components/list/ListView.svelte | 2 ++ plugins/view/src/types.ts | 2 ++ 25 files changed, 103 insertions(+), 33 deletions(-) rename plugins/{setting-resources => setting}/src/analytics.ts (79%) create mode 100644 plugins/tracker/src/analytics.ts diff --git a/dev/prod/src/analytics/analyticsCollector.ts b/dev/prod/src/analytics/analyticsCollector.ts index 029a855545b..54b722f020a 100644 --- a/dev/prod/src/analytics/analyticsCollector.ts +++ b/dev/prod/src/analytics/analyticsCollector.ts @@ -21,7 +21,7 @@ import { AnalyticEvent, AnalyticEventType } from '@hcengineering/analytics-colle import { Config } from '../platform' export class AnalyticsCollectorProvider implements AnalyticProvider { - private readonly collectIntervalMs = 1000 + private readonly collectIntervalMs = 5000 private readonly events: AnalyticEvent[] = [] @@ -85,10 +85,10 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { this.setTag('workspace', ws) } - handleEvent(event: string): void { + handleEvent(event: string, params: Record): void { this.events.push({ event: AnalyticEventType.CustomEvent, - params: { event }, + params: { ...params, event }, timestamp: Date.now() }) } @@ -108,4 +108,6 @@ export class AnalyticsCollectorProvider implements AnalyticProvider { timestamp: Date.now() }) } + + logout(): void {} } \ No newline at end of file diff --git a/dev/prod/src/analytics/posthog.ts b/dev/prod/src/analytics/posthog.ts index e5e12b49d28..dff093d8d7b 100644 --- a/dev/prod/src/analytics/posthog.ts +++ b/dev/prod/src/analytics/posthog.ts @@ -22,8 +22,11 @@ export class PosthogAnalyticProvider implements AnalyticProvider { name: `${ws}` }) } - handleEvent(event: string): void { - posthog.capture(event) + logout(): void { + posthog.reset() + } + handleEvent(event: string, params: Record): void { + posthog.capture(event, params) } handleError(error: Error): void { posthog.capture(error.message) diff --git a/dev/prod/src/analytics/sentry.ts b/dev/prod/src/analytics/sentry.ts index d4b14f9cd0b..c4464ab6087 100644 --- a/dev/prod/src/analytics/sentry.ts +++ b/dev/prod/src/analytics/sentry.ts @@ -30,6 +30,9 @@ export class SentryAnalyticProvider implements AnalyticProvider { setUser(email: string): void { Sentry.setUser({ email }) } + logout(): void { + Sentry.setUser(null) + } setTag(key: string, value: string): void { Sentry.setTag(key, value) } diff --git a/models/tracker/src/actions.ts b/models/tracker/src/actions.ts index f6b859b033d..2ab6b975b2f 100644 --- a/models/tracker/src/actions.ts +++ b/models/tracker/src/actions.ts @@ -20,7 +20,7 @@ import task from '@hcengineering/model-task' import view, { actionTemplates, createAction } from '@hcengineering/model-view' import workbench, { createNavigateAction } from '@hcengineering/model-workbench' import { type IntlString } from '@hcengineering/platform' -import { trackerId } from '@hcengineering/tracker' +import { TrackerEvents, trackerId } from '@hcengineering/tracker' import { type KeyBinding, type ViewAction } from '@hcengineering/view' import tracker from './plugin' @@ -124,6 +124,7 @@ export function createActions (builder: Builder, issuesId: string, componentsId: mode: ['context', 'browser'], group: 'edit' }, + analyticsEvent: TrackerEvents.ArchiveProject, override: [view.action.Archive, view.action.Delete] }, tracker.action.DeleteProject @@ -145,6 +146,7 @@ export function createActions (builder: Builder, issuesId: string, componentsId: mode: ['context', 'browser'], group: 'edit' }, + analyticsEvent: TrackerEvents.DeleteProject, override: [view.action.Archive, view.action.Delete] }, tracker.action.DeleteProjectClean @@ -187,7 +189,8 @@ export function createActions (builder: Builder, issuesId: string, componentsId: group: 'remove' }, visibilityTester: view.function.CanDeleteObject, - override: [view.action.Delete] + override: [view.action.Delete], + analyticsEvent: TrackerEvents.DeleteIssue }, tracker.action.DeleteIssue ) @@ -218,7 +221,8 @@ export function createActions (builder: Builder, issuesId: string, componentsId: application: tracker.app.Tracker, group: 'create' }, - override: [tracker.action.NewIssueGlobal] + override: [tracker.action.NewIssueGlobal], + analyticsEvent: TrackerEvents.CallNewIssueBinding }, tracker.action.NewIssue ) @@ -239,7 +243,8 @@ export function createActions (builder: Builder, issuesId: string, componentsId: context: { mode: [], group: 'create' - } + }, + analyticsEvent: TrackerEvents.ClickCreateIssueGlobal }, tracker.action.NewIssueGlobal ) diff --git a/packages/analytics/src/index.ts b/packages/analytics/src/index.ts index 41e9aa3fcd5..d6066961a84 100644 --- a/packages/analytics/src/index.ts +++ b/packages/analytics/src/index.ts @@ -10,9 +10,10 @@ export interface AnalyticProvider { setUser: (email: string) => void setTag: (key: string, value: string) => void setWorkspace: (ws: string) => void - handleEvent: (event: string) => void + handleEvent: (event: string, params: Record) => void handleError: (error: Error) => void navigate: (path: string) => void + logout: () => void } export const Analytics = { @@ -41,9 +42,11 @@ export const Analytics = { }) }, - handleEvent (event: string): void { + handleEvent (event: string, params: Record = {}): void { + //TODO: remove me + console.log(event, params) providers.forEach((provider) => { - provider.handleEvent(event) + provider.handleEvent(event, params) }) }, @@ -57,6 +60,12 @@ export const Analytics = { providers.forEach((provider) => { provider.navigate(path) }) + }, + + logout (): void { + providers.forEach((provider) => { + provider.logout() + }) } } diff --git a/plugins/login-resources/src/analytics.ts b/plugins/login-resources/src/analytics.ts index b50886df638..7a04d439055 100644 --- a/plugins/login-resources/src/analytics.ts +++ b/plugins/login-resources/src/analytics.ts @@ -24,5 +24,5 @@ export const LoginEvents = { LoginGithub: 'login.viaGitHub', CreateWorkspace: 'onboard.createWorkspace', - SelectWorkspace: 'onboard.selectWorkspace', -} \ No newline at end of file + SelectWorkspace: 'onboard.selectWorkspace' +} diff --git a/plugins/login-resources/src/utils.ts b/plugins/login-resources/src/utils.ts index 0f373add6e6..5943c990cf1 100644 --- a/plugins/login-resources/src/utils.ts +++ b/plugins/login-resources/src/utils.ts @@ -111,16 +111,16 @@ export async function signUp ( }) const result = await response.json() if (result.error == null) { - Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: true }) + Analytics.handleEvent(LoginEvents.SignUpEmail, { email, ok: true }) Analytics.setUser(email) } else { - Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: false }) + Analytics.handleEvent(LoginEvents.SignUpEmail, { email, ok: false }) await handleStatusError('Sign up error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { Analytics.handleError(err) - Analytics.handleEvent(LoginEvents.SignUpEmail, { email: email, ok: false }) + Analytics.handleEvent(LoginEvents.SignUpEmail, { email, ok: false }) return [unknownError(err), undefined] } } @@ -193,15 +193,15 @@ export async function createWorkspace ( }) const result = await response.json() if (result.error == null) { - Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: true}) + Analytics.handleEvent(LoginEvents.CreateWorkspace, { name: workspaceName, ok: true }) Analytics.setWorkspace(workspaceName) } else { - Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: false}) + Analytics.handleEvent(LoginEvents.CreateWorkspace, { name: workspaceName, ok: false }) await handleStatusError('Create workspace error', result.error) } return [result.error ?? OK, result.result] } catch (err: any) { - Analytics.handleEvent(LoginEvents.CreateWorkspace, {name: workspaceName, ok: false}) + Analytics.handleEvent(LoginEvents.CreateWorkspace, { name: workspaceName, ok: false }) Analytics.handleError(err) return [unknownError(err), undefined] } diff --git a/plugins/onboard-resources/src/analytics.ts b/plugins/onboard-resources/src/analytics.ts index 639701e0b58..4cdb01de54e 100644 --- a/plugins/onboard-resources/src/analytics.ts +++ b/plugins/onboard-resources/src/analytics.ts @@ -13,6 +13,6 @@ // limitations under the License. // -export let OnboardEvents = { - StartHuly: 'onboard.startHuly', -} \ No newline at end of file +export const OnboardEvents = { + StartHuly: 'onboard.startHuly' +} diff --git a/plugins/setting-resources/src/components/Settings.svelte b/plugins/setting-resources/src/components/Settings.svelte index 569a9e7a115..59c3b52436d 100644 --- a/plugins/setting-resources/src/components/Settings.svelte +++ b/plugins/setting-resources/src/components/Settings.svelte @@ -18,7 +18,7 @@ import login, { loginId } from '@hcengineering/login' import { setMetadata } from '@hcengineering/platform' import presentation, { closeClient, createQuery } from '@hcengineering/presentation' - import setting, { SettingsCategory } from '@hcengineering/setting' + import setting, { SettingsCategory, SettingsEvents } from '@hcengineering/setting' import { Component, Label, @@ -41,7 +41,6 @@ import { ComponentType, onDestroy } from 'svelte' import { clearSettingsStore, settingsStore, type SettingsStore } from '../store' import { Analytics } from '@hcengineering/analytics' - import { SettingsEvents } from '../analytics' let category: SettingsCategory | undefined let categoryId: string = '' diff --git a/plugins/setting-resources/src/analytics.ts b/plugins/setting/src/analytics.ts similarity index 79% rename from plugins/setting-resources/src/analytics.ts rename to plugins/setting/src/analytics.ts index cd6a5a86db4..99c64856552 100644 --- a/plugins/setting-resources/src/analytics.ts +++ b/plugins/setting/src/analytics.ts @@ -1,5 +1,5 @@ export enum SettingsEvents { SelectWorkspace = 'settings.SelectWorkspace', InviteToWorkspace = 'settings.InviteToWorkspace', - SignOut = 'settings.SignOut', -} \ No newline at end of file + SignOut = 'settings.SignOut' +} diff --git a/plugins/setting/src/index.ts b/plugins/setting/src/index.ts index 77369a42d40..13237257fbb 100644 --- a/plugins/setting/src/index.ts +++ b/plugins/setting/src/index.ts @@ -23,6 +23,7 @@ import { SpaceTypeCreator, SpaceTypeEditor } from './spaceTypeEditor' export * from './spaceTypeEditor' export * from './utils' +export * from './analytics' /** * @public diff --git a/plugins/tracker-resources/src/components/CreateIssue.svelte b/plugins/tracker-resources/src/components/CreateIssue.svelte index df400714996..4dd813bea42 100644 --- a/plugins/tracker-resources/src/components/CreateIssue.svelte +++ b/plugins/tracker-resources/src/components/CreateIssue.svelte @@ -57,7 +57,8 @@ IssueTemplate, Milestone, Project, - ProjectTargetPreference + ProjectTargetPreference, + TrackerEvents } from '@hcengineering/tracker' import { Button, @@ -563,12 +564,18 @@ descriptionBox?.removeDraft(false) isAssigneeTouched = false const d1 = Date.now() + Analytics.handleEvent(TrackerEvents.CreateIssue, { + ok: true, + id: value.identifier, + project: currentProject.identifier + }) console.log('createIssue measure', result, Date.now() - d1) } catch (err: any) { resetObject() draftController.remove() descriptionBox?.removeDraft(false) console.error(err) + Analytics.handleEvent(TrackerEvents.CreateIssue, { ok: false, project: currentProject.identifier }) Analytics.handleError(err) } } diff --git a/plugins/tracker-resources/src/components/NewIssueHeader.svelte b/plugins/tracker-resources/src/components/NewIssueHeader.svelte index 0ab4bad9f4b..31586fa7f67 100644 --- a/plugins/tracker-resources/src/components/NewIssueHeader.svelte +++ b/plugins/tracker-resources/src/components/NewIssueHeader.svelte @@ -17,11 +17,13 @@ import { MultipleDraftController, getClient } from '@hcengineering/presentation' import { ButtonWithDropdown, IconAdd, IconDropdown, SelectPopupValueType, showPopup } from '@hcengineering/ui' import view from '@hcengineering/view' + import { Project, TrackerEvents } from '@hcengineering/tracker' + import { Analytics } from '@hcengineering/analytics' + import { onDestroy } from 'svelte' import tracker from '../plugin' import CreateIssue from './CreateIssue.svelte' import { importTasks } from '..' - import { Project } from '@hcengineering/tracker' export let currentSpace: Ref | undefined @@ -37,6 +39,7 @@ ) async function newIssue (): Promise { closed = false + Analytics.handleEvent(TrackerEvents.ClickNewIssueButton) showPopup(CreateIssue, { space: currentSpace, shouldSaveDraft: true }, 'top', () => { closed = true }) diff --git a/plugins/tracker-resources/src/components/issues/IssuesView.svelte b/plugins/tracker-resources/src/components/issues/IssuesView.svelte index 23556bbb23b..05e9cb1ab1e 100644 --- a/plugins/tracker-resources/src/components/issues/IssuesView.svelte +++ b/plugins/tracker-resources/src/components/issues/IssuesView.svelte @@ -1,7 +1,7 @@ diff --git a/plugins/document-resources/src/components/NewDocumentHeader.svelte b/plugins/document-resources/src/components/NewDocumentHeader.svelte index 83fb5055616..e6e0a041ddc 100644 --- a/plugins/document-resources/src/components/NewDocumentHeader.svelte +++ b/plugins/document-resources/src/components/NewDocumentHeader.svelte @@ -25,6 +25,9 @@ showPopup } from '@hcengineering/ui' import { openDoc } from '@hcengineering/view-resources' + import { Analytics } from '@hcengineering/analytics' + import { DocumentEvents } from '@hcengineering/document' + import document from '../plugin' import { getDocumentIdFromFragment } from '../utils' import CreateDocument from './CreateDocument.svelte' @@ -53,6 +56,7 @@ $: parent = getDocumentIdFromFragment(currentFragment ?? '') async function newDocument (): Promise { + Analytics.handleEvent(DocumentEvents.CreateDocumentButtonClicked) showPopup(CreateDocument, { space: currentSpace, parent }, 'top', async (id) => { if (id !== undefined && id !== null) { const doc = await client.findOne(document.class.Document, { _id: id }) diff --git a/plugins/document-resources/src/components/navigator/TeamspaceSpacePresenter.svelte b/plugins/document-resources/src/components/navigator/TeamspaceSpacePresenter.svelte index 7a5ada63bb4..23dc3f7c5c7 100644 --- a/plugins/document-resources/src/components/navigator/TeamspaceSpacePresenter.svelte +++ b/plugins/document-resources/src/components/navigator/TeamspaceSpacePresenter.svelte @@ -14,7 +14,7 @@ --> diff --git a/plugins/document-resources/src/components/teamspace/CreateTeamspace.svelte b/plugins/document-resources/src/components/teamspace/CreateTeamspace.svelte index 4a5eb98f056..30727b249b7 100644 --- a/plugins/document-resources/src/components/teamspace/CreateTeamspace.svelte +++ b/plugins/document-resources/src/components/teamspace/CreateTeamspace.svelte @@ -27,7 +27,7 @@ getCurrentAccount, WithLookup } from '@hcengineering/core' - import document, { Teamspace } from '@hcengineering/document' + import document, { Teamspace, DocumentEvents } from '@hcengineering/document' import { Asset } from '@hcengineering/platform' import presentation, { Card, getClient, reduceCalls } from '@hcengineering/presentation' import { @@ -45,6 +45,7 @@ import view from '@hcengineering/view' import { IconPicker, SpaceTypeSelector } from '@hcengineering/view-resources' import { createEventDispatcher } from 'svelte' + import { Analytics } from '@hcengineering/analytics' import documentRes from '../../plugin' @@ -207,6 +208,7 @@ rolesAssignment ) + Analytics.handleEvent(DocumentEvents.TeamspaceCreated, { id: teamspaceId }) close(teamspaceId) } diff --git a/plugins/document/src/analytics.ts b/plugins/document/src/analytics.ts new file mode 100644 index 00000000000..ab49fdbc9c3 --- /dev/null +++ b/plugins/document/src/analytics.ts @@ -0,0 +1,8 @@ +export enum DocumentEvents { + CreateDocumentButtonClicked = 'document.CreateButtonClicked', + PlusDocumentButtonClicked = 'document.PlusButtonClicked', + DocumentCreated = 'document.Created', + TeamspaceCreated = 'document.CreatedTeamspace', + DocumentEdited = 'document.Edited', + DocumentOpened = 'document.Opened' +} diff --git a/plugins/document/src/index.ts b/plugins/document/src/index.ts index 0a8565158ab..1c712bf3c87 100644 --- a/plugins/document/src/index.ts +++ b/plugins/document/src/index.ts @@ -16,6 +16,7 @@ import { documentId, documentPlugin } from './plugin' export * from './types' +export * from './analytics' export { documentId } export default documentPlugin diff --git a/plugins/drive-resources/src/components/CreateDrive.svelte b/plugins/drive-resources/src/components/CreateDrive.svelte index 6d0204431f4..19316e610e8 100644 --- a/plugins/drive-resources/src/components/CreateDrive.svelte +++ b/plugins/drive-resources/src/components/CreateDrive.svelte @@ -28,12 +28,13 @@ getCurrentAccount, WithLookup } from '@hcengineering/core' - import { Drive } from '@hcengineering/drive' + import { Drive, DriveEvents } from '@hcengineering/drive' import presentation, { Card, getClient, reduceCalls } from '@hcengineering/presentation' import { EditBox, Label, Toggle } from '@hcengineering/ui' import { SpaceTypeSelector } from '@hcengineering/view-resources' import driveRes from '../plugin' + import { Analytics } from '@hcengineering/analytics' export let drive: Drive | undefined = undefined @@ -168,7 +169,7 @@ // Create space type's mixin with roles assignments await client.createMixin(driveId, driveRes.class.Drive, core.space.Space, spaceType.targetClass, rolesAssignment) - + Analytics.handleEvent(DriveEvents.DriveCreated, { id: driveId }) close(driveId) } diff --git a/plugins/drive-resources/src/components/CreateFolder.svelte b/plugins/drive-resources/src/components/CreateFolder.svelte index afd672707de..2094299cb29 100644 --- a/plugins/drive-resources/src/components/CreateFolder.svelte +++ b/plugins/drive-resources/src/components/CreateFolder.svelte @@ -16,7 +16,7 @@ --> diff --git a/plugins/recruit-resources/src/components/CreateVacancy.svelte b/plugins/recruit-resources/src/components/CreateVacancy.svelte index 71f22734f47..c8cc2c52ad0 100644 --- a/plugins/recruit-resources/src/components/CreateVacancy.svelte +++ b/plugins/recruit-resources/src/components/CreateVacancy.svelte @@ -29,7 +29,7 @@ } from '@hcengineering/core' import { getEmbeddedLabel } from '@hcengineering/platform' import { Card, InlineAttributeBar, MessageBox, createQuery, getClient } from '@hcengineering/presentation' - import { Vacancy as VacancyClass } from '@hcengineering/recruit' + import { RecruitEvents, Vacancy, Vacancy as VacancyClass } from '@hcengineering/recruit' import tags from '@hcengineering/tags' import task, { ProjectType, makeRank } from '@hcengineering/task' import { selectedTypeStore, typeStore } from '@hcengineering/task-resources' @@ -40,15 +40,15 @@ EditBox, FocusHandler, IconAttachment, - Label, - Toggle, createFocusManager, showPopup } from '@hcengineering/ui' import { createEventDispatcher } from 'svelte' import recruit from '../plugin' import Company from './icons/Company.svelte' - import Vacancy from './icons/Vacancy.svelte' + import VacancyIcon from './icons/Vacancy.svelte' + import { Analytics } from '@hcengineering/analytics' + import { getSequenceId } from '../utils' const dispatch = createEventDispatcher() @@ -220,26 +220,33 @@ } const incResult = await client.update(sequence, { $inc: { sequence: 1 } }, true) + const data: Data = { + ...vacancyData, + name, + description: template?.shortDescription ?? '', + fullDescription, + private: false, + archived: false, + number: (incResult as any).object.sequence, + company, + members, + autoJoin: typeType.autoJoin ?? false, + owners: [getCurrentAccount()._id], + type: typeId + } - const id = await client.createDoc( - recruit.class.Vacancy, - core.space.Space, - { - ...vacancyData, - name, - description: template?.shortDescription ?? '', - fullDescription, - private: false, - archived: false, - number: (incResult as any).object.sequence, - company, - members, - autoJoin: typeType.autoJoin ?? false, - owners: [getCurrentAccount()._id], - type: typeId - }, - objectId - ) + const id = await client.createDoc(recruit.class.Vacancy, core.space.Space, data, objectId) + + Analytics.handleEvent(RecruitEvents.VacancyCreated, { + id: getSequenceId({ + ...data, + _id: id, + _class: recruit.class.Vacancy, + space: core.space.Space, + modifiedOn: 0, + modifiedBy: getCurrentAccount()._id + }) + }) if (issueTemplates.length > 0) { for (const issueTemplate of issueTemplates) { @@ -316,7 +323,7 @@ {typeType?.name}
-
{ showPopup(CreateCandidate, { shouldSaveDraft: true }, 'top') + Analytics.handleEvent(RecruitEvents.NewTalentButtonClicked) } diff --git a/plugins/recruit-resources/src/components/review/CreateReview.svelte b/plugins/recruit-resources/src/components/review/CreateReview.svelte index 9f394ba543b..b74bef5342a 100644 --- a/plugins/recruit-resources/src/components/review/CreateReview.svelte +++ b/plugins/recruit-resources/src/components/review/CreateReview.svelte @@ -30,7 +30,7 @@ import { getResource, OK, Resource, Severity, Status } from '@hcengineering/platform' import { Card, getClient } from '@hcengineering/presentation' import { UserBox, UserBoxList } from '@hcengineering/contact-resources' - import type { Applicant, Candidate, Review } from '@hcengineering/recruit' + import { Applicant, Candidate, RecruitEvents, Review } from '@hcengineering/recruit' import task from '@hcengineering/task' import { EmptyMarkup } from '@hcengineering/text' import { StyledTextArea } from '@hcengineering/text-editor-resources' @@ -40,6 +40,8 @@ import { createEventDispatcher } from 'svelte' import recruit from '../../plugin' import IconCompany from '../icons/Company.svelte' + import { Analytics } from '@hcengineering/analytics' + import { getCandidateIdentifier } from '../../utils' // export let space: Ref export let candidate: Ref @@ -117,22 +119,31 @@ ) } - await client.addCollection(recruit.class.Review, doc.space, doc.attachedTo, doc.attachedToClass, 'reviews', { - number: (incResult as any).object.sequence, - date: startDate ?? 0, - dueDate: dueDate ?? 0, - description, - verdict: '', - title, - participants: doc.participants, - company, - application, - location, - access: 'reader', - allDay: false, - eventId: '', - calendar: undefined - }) + const ref = await client.addCollection( + recruit.class.Review, + doc.space, + doc.attachedTo, + doc.attachedToClass, + 'reviews', + { + number: (incResult as any).object.sequence, + date: startDate ?? 0, + dueDate: dueDate ?? 0, + description, + verdict: '', + title, + participants: doc.participants, + company, + application, + location, + access: 'reader', + allDay: false, + eventId: '', + calendar: undefined + } + ) + + Analytics.handleEvent(RecruitEvents.ReviewCreated, { id: ref }) } async function invokeValidate ( diff --git a/plugins/recruit-resources/src/components/review/EditReview.svelte b/plugins/recruit-resources/src/components/review/EditReview.svelte index f80b6259f73..16bec8be716 100644 --- a/plugins/recruit-resources/src/components/review/EditReview.svelte +++ b/plugins/recruit-resources/src/components/review/EditReview.svelte @@ -17,12 +17,13 @@ import contact, { Contact } from '@hcengineering/contact' import { UserBox } from '@hcengineering/contact-resources' import { getClient } from '@hcengineering/presentation' - import type { Review } from '@hcengineering/recruit' + import { RecruitEvents, Review } from '@hcengineering/recruit' import { FullDescriptionBox } from '@hcengineering/text-editor-resources' import { EditBox, Grid } from '@hcengineering/ui' import { ObjectPresenter, openDoc } from '@hcengineering/view-resources' import { createEventDispatcher, onMount } from 'svelte' import recruit from '../../plugin' + import { Analytics } from '@hcengineering/analytics' export let object: Review @@ -52,6 +53,10 @@ } $: updateSelected(object) + + onMount(() => { + Analytics.handleEvent(RecruitEvents.ReviewViewed, { id: object._id }) + }) {#if object !== undefined} diff --git a/plugins/recruit-resources/src/utils.ts b/plugins/recruit-resources/src/utils.ts index 0f2bdd719d6..e22880226c7 100644 --- a/plugins/recruit-resources/src/utils.ts +++ b/plugins/recruit-resources/src/utils.ts @@ -21,7 +21,7 @@ type RecruitDocument = Vacancy | Applicant | Review export async function objectLinkProvider (doc: RecruitDocument): Promise { const location = getCurrentResolvedLocation() const frontUrl = getMetadata(presentation.metadata.FrontUrl) ?? window.location.origin - const url = `${frontUrl}/${workbenchId}/${location.path[1]}/${recruitId}/${await getSequenceId(doc)}` + const url = `${frontUrl}/${workbenchId}/${location.path[1]}/${recruitId}/${getSequenceId(doc)}` return url } @@ -188,7 +188,7 @@ export async function getSequenceLink (doc: RecruitDocument): Promise loc.fragment = undefined loc.query = undefined loc.path[2] = recruitId - loc.path[3] = await getSequenceId(doc) + loc.path[3] = getSequenceId(doc) return loc } @@ -220,6 +220,12 @@ export async function getAppTitle (client: Client, ref: Ref, doc?: Ap return getName(client.getHierarchy(), candidate) } +export function getCandidateIdentifier (ref: Ref): string { + const hierarchy = getClient().getHierarchy() + const clazz = hierarchy.getClass(recruit.mixin.Candidate) + return clazz.shortLabel !== undefined ? `${clazz.shortLabel}-${ref}` : ref +} + export async function getAppIdentifier (client: Client, ref: Ref, doc?: Applicant): Promise { const applicant = doc ?? (await client.findOne(recruit.class.Applicant, { _id: ref })) @@ -235,7 +241,7 @@ export async function getRevTitle (client: Client, ref: Ref, doc?: Revie return object != null ? object.title : '' } -export async function getSequenceId (doc: RecruitDocument): Promise { +export function getSequenceId (doc: RecruitDocument): string { const client = getClient() const hierarchy = client.getHierarchy() if (hierarchy.isDerived(doc._class, recruit.class.Applicant)) { @@ -262,7 +268,7 @@ export async function getVacancyIdentifier (client: Client, ref: Ref, d return '' } - return await getSequenceId(vacancy) + return getSequenceId(vacancy) } export async function getReviewIdentifier (client: Client, ref: Ref, doc?: Review): Promise { @@ -272,5 +278,5 @@ export async function getReviewIdentifier (client: Client, ref: Ref, doc return '' } - return await getSequenceId(review) + return getSequenceId(review) } diff --git a/plugins/recruit/src/analytics.ts b/plugins/recruit/src/analytics.ts new file mode 100644 index 00000000000..36ca4414eb1 --- /dev/null +++ b/plugins/recruit/src/analytics.ts @@ -0,0 +1,11 @@ +export enum RecruitEvents { + NewTalentButtonClicked = 'recruit.talent.NewButtonClicked', + PlusTalentButtonClicked = 'recruit.talent.PlusButtonClicked', + TalentCreated = 'recruit.talent.Created', + ReviewCreated = 'recruit.review.Created', + ReviewViewed = 'recruit.review.Viewed', + VacancyCreated = 'recruit.vacancy.Created', + CompanyCreated = 'recruit.company.Created', + ApplicationCreated = 'recruit.application.Created', + SkillCreated = 'recruit.skill.Created' +} diff --git a/plugins/recruit/src/index.ts b/plugins/recruit/src/index.ts index c9a8cce4fec..ca2f008e068 100644 --- a/plugins/recruit/src/index.ts +++ b/plugins/recruit/src/index.ts @@ -21,6 +21,7 @@ import { AnyComponent, Location, ResolvedLocation } from '@hcengineering/ui' import type { Applicant, ApplicantMatch, Candidate, Opinion, Review, Vacancy, VacancyList } from './types' export * from './types' +export * from './analytics' /** * @public diff --git a/plugins/tags-resources/package.json b/plugins/tags-resources/package.json index 41a10604173..8709c309aed 100644 --- a/plugins/tags-resources/package.json +++ b/plugins/tags-resources/package.json @@ -38,6 +38,7 @@ "svelte-eslint-parser": "^0.33.1" }, "dependencies": { + "@hcengineering/analytics": "^0.6.0", "@hcengineering/platform": "^0.6.11", "svelte": "^4.2.12", "@hcengineering/tags": "^0.6.16", diff --git a/plugins/tags-resources/src/components/CreateTagElement.svelte b/plugins/tags-resources/src/components/CreateTagElement.svelte index 86c0c7363fe..99faee4053b 100644 --- a/plugins/tags-resources/src/components/CreateTagElement.svelte +++ b/plugins/tags-resources/src/components/CreateTagElement.svelte @@ -77,7 +77,7 @@ }) async function createTagElementFnc (): Promise { - const res = await createTagElement(title, targetClass, category, description, color) + const res = await createTagElement(title, targetClass, category, description, color, keyTitle) dispatch('close', res) } diff --git a/plugins/tags-resources/src/components/TagsAttributeEditor.svelte b/plugins/tags-resources/src/components/TagsAttributeEditor.svelte index 00691fe6152..951d314e619 100644 --- a/plugins/tags-resources/src/components/TagsAttributeEditor.svelte +++ b/plugins/tags-resources/src/components/TagsAttributeEditor.svelte @@ -2,9 +2,11 @@ import { AnyAttribute, Class, Doc, Ref } from '@hcengineering/core' import { IntlString } from '@hcengineering/platform' import { createQuery, getClient } from '@hcengineering/presentation' - import type { TagReference } from '@hcengineering/tags' - import tags from '@hcengineering/tags' + import tags, { TagReference, TagsEvents } from '@hcengineering/tags' import { Icon, Label, getEventPopupPositionElement, showPopup } from '@hcengineering/ui' + import { getObjectId } from '@hcengineering/view-resources' + import { Analytics } from '@hcengineering/analytics' + import TagReferencePresenter from './TagReferencePresenter.svelte' import TagsEditorPopup from './TagsEditorPopup.svelte' import TagIcon from './icons/TagIcon.svelte' @@ -18,6 +20,7 @@ let items: TagReference[] = [] const query = createQuery() const client = getClient() + const hierarchy = client.getHierarchy() $: query.query(tags.class.TagReference, { attachedTo: object._id }, (result) => { items = result @@ -31,7 +34,11 @@ }) } async function removeTag (tag: TagReference): Promise { - if (tag !== undefined) await client.remove(tag) + if (tag !== undefined) { + await client.remove(tag) + const id = await getObjectId(object, hierarchy) + Analytics.handleEvent(TagsEvents.TagRemoved, { object: id }) + } } diff --git a/plugins/tags-resources/src/components/TagsEditorPopup.svelte b/plugins/tags-resources/src/components/TagsEditorPopup.svelte index 69ae2c06ce0..9be7ce487d1 100644 --- a/plugins/tags-resources/src/components/TagsEditorPopup.svelte +++ b/plugins/tags-resources/src/components/TagsEditorPopup.svelte @@ -15,8 +15,11 @@ diff --git a/plugins/tracker-resources/src/components/issues/StatusEditor.svelte b/plugins/tracker-resources/src/components/issues/StatusEditor.svelte index d3ae09c78de..83388bd47be 100644 --- a/plugins/tracker-resources/src/components/issues/StatusEditor.svelte +++ b/plugins/tracker-resources/src/components/issues/StatusEditor.svelte @@ -17,7 +17,7 @@ import { getClient } from '@hcengineering/presentation' import { getTaskTypeStates } from '@hcengineering/task' import { taskTypeStore } from '@hcengineering/task-resources' - import { Issue, IssueDraft, IssueStatus, Project } from '@hcengineering/tracker' + import { Issue, IssueDraft, IssueStatus, Project, TrackerEvents } from '@hcengineering/tracker' import { Button, ButtonKind, @@ -29,10 +29,13 @@ showPopup } from '@hcengineering/ui' import { statusStore } from '@hcengineering/view-resources' + import { Analytics } from '@hcengineering/analytics' import { createEventDispatcher } from 'svelte' + import tracker from '../../plugin' import IssueStatusIcon from './IssueStatusIcon.svelte' import StatusPresenter from './StatusPresenter.svelte' + type ValueType = Issue | (AttachedData & { space: Ref }) | IssueDraft export let value: ValueType @@ -67,6 +70,10 @@ if ('_class' in value) { await client.update(value, { status: newStatus }) + Analytics.handleEvent(TrackerEvents.IssueSetStatus, { + issue: value.identifier, + status: newStatus + }) } } diff --git a/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte b/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte index 984f8d48169..bbb1e7aa2ed 100644 --- a/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte +++ b/plugins/tracker-resources/src/components/issues/edit/ControlPanel.svelte @@ -209,7 +209,15 @@ {#if keys.length > 0}
{#each keys as key (typeof key === 'string' ? key : key.key)} - + {/each} {/if} @@ -220,6 +228,7 @@ {#each mixinKeys as key (typeof key === 'string' ? key : key.key)} | string export let _class: Ref> @@ -128,6 +130,7 @@ if (trimmedTitle.length > 0 && trimmedTitle !== issue.title?.trim()) { await client.update(issue, { title: trimmedTitle }) + Analytics.handleEvent(TrackerEvents.IssueTitleUpdated, { issue: issue.identifier ?? issue._id }) } } @@ -284,6 +287,7 @@ {readonly} key={{ key: 'description', attr: descriptionKey }} bind:this={descriptionBox} + identifier={issue?.identifier} placeholder={tracker.string.IssueDescriptionPlaceholder} boundary={content} on:saved={(evt) => { diff --git a/plugins/tracker-resources/src/components/issues/timereport/TimeSpendReportPopup.svelte b/plugins/tracker-resources/src/components/issues/timereport/TimeSpendReportPopup.svelte index 891c8a00cf4..37dd5b19f5c 100644 --- a/plugins/tracker-resources/src/components/issues/timereport/TimeSpendReportPopup.svelte +++ b/plugins/tracker-resources/src/components/issues/timereport/TimeSpendReportPopup.svelte @@ -18,12 +18,13 @@ import type { IntlString } from '@hcengineering/platform' import presentation, { Card, getClient } from '@hcengineering/presentation' import { UserBox } from '@hcengineering/contact-resources' - import { Issue, TimeReportDayType, TimeSpendReport } from '@hcengineering/tracker' + import { Issue, TimeReportDayType, TimeSpendReport, TrackerEvents } from '@hcengineering/tracker' import { Button, DatePresenter, EditBox, Label } from '@hcengineering/ui' import tracker from '../../../plugin' import { getTimeReportDate, getTimeReportDayType } from '../../../utils' import TitlePresenter from '../TitlePresenter.svelte' import TimeReportDayDropdown from './TimeReportDayDropdown.svelte' + import { Analytics } from '@hcengineering/analytics' export let issue: Issue | undefined = undefined export let issueId: Ref | undefined = issue?._id @@ -61,6 +62,7 @@ 'reports', data as AttachedData ) + Analytics.handleEvent(TrackerEvents.IssueTimeSpentAdded, { issue: issue?.identifier ?? issueId }) } } else { const ops: DocumentUpdate = {} @@ -78,6 +80,7 @@ } if (Object.keys(ops).length > 0) { await client.update(value, ops) + Analytics.handleEvent(TrackerEvents.IssueTimeSpentUpdated, { issue: issue?.identifier ?? issueId }) } } } diff --git a/plugins/tracker-resources/src/components/milestones/MilestoneEditor.svelte b/plugins/tracker-resources/src/components/milestones/MilestoneEditor.svelte index 15dfffddaac..5a190d829cc 100644 --- a/plugins/tracker-resources/src/components/milestones/MilestoneEditor.svelte +++ b/plugins/tracker-resources/src/components/milestones/MilestoneEditor.svelte @@ -16,7 +16,8 @@ import { Ref } from '@hcengineering/core' import { IntlString } from '@hcengineering/platform' import { createQuery, getClient } from '@hcengineering/presentation' - import { Issue, IssueTemplate, Milestone, Project } from '@hcengineering/tracker' + import { Issue, IssueTemplate, Milestone, Project, TrackerEvents } from '@hcengineering/tracker' + import { Analytics } from '@hcengineering/analytics' import { ButtonKind, ButtonShape, @@ -58,10 +59,18 @@ await Promise.all( value.map(async (p) => { await client.update(p, { milestone: newMilestoneId }) + Analytics.handleEvent(TrackerEvents.IssueMilestoneAdded, { + issue: p.identifier ?? p._id, + component: newMilestoneId + }) }) ) } else { await client.update(value, { milestone: newMilestoneId }) + Analytics.handleEvent(TrackerEvents.IssueMilestoneAdded, { + issue: (value as Issue).identifier ?? value._id, + component: newMilestoneId + }) } if (isAction) dispatch('close') } diff --git a/plugins/tracker-resources/src/components/projects/CreateProject.svelte b/plugins/tracker-resources/src/components/projects/CreateProject.svelte index cf749c82438..9390e32d26c 100644 --- a/plugins/tracker-resources/src/components/projects/CreateProject.svelte +++ b/plugins/tracker-resources/src/components/projects/CreateProject.svelte @@ -236,7 +236,7 @@ isSaving = true await ops.createDoc(tracker.class.Project, core.space.Space, { ...projectData, type: typeId }, projectId) const succeeded = await ops.commit() - Analytics.handleEvent(TrackerEvents.CreateProject, { + Analytics.handleEvent(TrackerEvents.ProjectCreated, { ok: succeeded.result, id: projectData.identifier }) diff --git a/plugins/tracker-resources/src/components/templates/EstimationEditor.svelte b/plugins/tracker-resources/src/components/templates/EstimationEditor.svelte index 04e64ad6825..e4c3d8db558 100644 --- a/plugins/tracker-resources/src/components/templates/EstimationEditor.svelte +++ b/plugins/tracker-resources/src/components/templates/EstimationEditor.svelte @@ -15,12 +15,13 @@