Skip to content

Commit ef86b5b

Browse files
feat: add analytics (#1188)
Co-authored-by: Dhairya Majmudar <2022kuec2045@iiitkota.ac.in> Co-authored-by: Ashish Padhy <ashishpadhy1729@gmail.com>
1 parent 38996f4 commit ef86b5b

File tree

14 files changed

+279
-37
lines changed

14 files changed

+279
-37
lines changed

.changeset/shiny-snails-cover.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@asyncapi/studio": major
3+
---
4+
5+
feat: google tag manager analytics

apps/studio/src/app/layout.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ import 'tippy.js/animations/shift-away.css';
66
import '@asyncapi/react-component/styles/default.min.css';
77
import 'reactflow/dist/style.css';
88
import './globals.css'
9+
import { GoogleTagManager, GoogleTagManagerNoScript } from '@/components/common/GoogleTagManager';
10+
11+
const GTM_ID = process.env.NEXT_PUBLIC_ANALYTICS_ID ?? '';
912

1013
export default function RootLayout({
1114
children,
@@ -14,8 +17,9 @@ export default function RootLayout({
1417
}) {
1518
return (
1619
<html lang="en">
20+
<head>{GTM_ID && <GoogleTagManager gtmId={GTM_ID} />}</head>
1721
<body>
18-
<Toolbar />
22+
{GTM_ID && <GoogleTagManagerNoScript gtmId={GTM_ID} />} <Toolbar />
1923
<main className="flex flex-col w-full h-[calc(100vh-4rem)]">
2024
{children}
2125
</main>

apps/studio/src/components/Editor/ConvertDropdown.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { ConvertModal } from '../Modals';
77
import { Dropdown, Tooltip } from '../common';
88
import { useServices } from '@/services';
99
import { useDocumentsState, useFilesState } from '../../state';
10+
import { trackEvent } from '@/helpers/analytics';
1011

1112
export const ConvertDropdown: React.FC = () => {
1213
const { editorSvc } = useServices();
@@ -34,6 +35,7 @@ export const ConvertDropdown: React.FC = () => {
3435
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
3536
title={`Convert to ${language === 'yaml' ? 'JSON' : 'YAML'}`}
3637
onClick={() => {
38+
trackEvent('Convert', 'click', language === 'yaml' ? 'JSON' : 'YAML');
3739
toast.promise(
3840
language === 'yaml'
3941
? editorSvc.convertToJSON()
@@ -67,7 +69,10 @@ export const ConvertDropdown: React.FC = () => {
6769
type="button"
6870
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
6971
title="Convert AsyncAPI document"
70-
onClick={() => show(ConvertModal)}
72+
onClick={() => {
73+
trackEvent('Convert', 'click', 'Open convert document modal');
74+
show(ConvertModal);
75+
}}
7176
>
7277
Convert document
7378
</button>

apps/studio/src/components/Editor/GenerateDropdown.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { GeneratorModal } from '../Modals';
77
import { Dropdown, Tooltip } from '../common';
88
import { useDocumentsState } from '../../state';
99
import { useServices } from '@/services';
10+
import { trackEvent} from '@/helpers/analytics';
1011

1112
export const GenerateDropdown: React.FC = () => {
1213
const isInvalidDocument = !useDocumentsState(state =>
@@ -33,7 +34,10 @@ export const GenerateDropdown: React.FC = () => {
3334
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
3435
title="Generate code/docs"
3536
disabled={isInvalidDocument}
36-
onClick={() => show(GeneratorModal)}
37+
onClick={() => {
38+
trackEvent('Generate', 'click', 'Generate code/docs')
39+
show(GeneratorModal)
40+
}}
3741
>
3842
Generate code/docs
3943
</button>
@@ -44,6 +48,7 @@ export const GenerateDropdown: React.FC = () => {
4448
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
4549
title='Share as Base64'
4650
onClick={() => {
51+
trackEvent('Generate', 'click', 'Share as Base64');
4752
toast.promise(
4853
(async function () {
4954
const base64 = await editorSvc.exportAsBase64();

apps/studio/src/components/Editor/ImportDropdown.tsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import toast from 'react-hot-toast';
33
import { show } from '@ebay/nice-modal-react';
44
import { FaFileImport } from 'react-icons/fa';
5+
import { trackEvent} from '@/helpers/analytics';
56

67
import {
78
ImportURLModal,
@@ -33,7 +34,10 @@ export const ImportDropdown: React.FC = () => {
3334
type="button"
3435
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
3536
title="Import from URL"
36-
onClick={() => show(ImportURLModal)}
37+
onClick={() => {
38+
trackEvent('Import', 'click', 'Import AsyncAPI document from URL')
39+
show(ImportURLModal)
40+
}}
3741
>
3842
Import from URL
3943
</button>
@@ -49,12 +53,13 @@ export const ImportDropdown: React.FC = () => {
4953
accept='.yaml, .yml, .json'
5054
style={{ position: 'fixed', top: '-100em' }}
5155
onChange={event => {
56+
trackEvent('Import', 'file_upload', 'Import File');
5257
toast.promise(editorSvc.importFile(event.target.files), {
5358
loading: 'Importing...',
5459
success: (
5560
<div>
5661
<span className="block text-bold">
57-
Document succesfully imported!
62+
Document successfully imported!
5863
</span>
5964
</div>
6065
),
@@ -77,7 +82,10 @@ export const ImportDropdown: React.FC = () => {
7782
type="button"
7883
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
7984
title="Import from Base64"
80-
onClick={() => show(ImportBase64Modal)}
85+
onClick={() => {
86+
trackEvent('Import', 'click', 'Import AsyncAPI document from Base64')
87+
show(ImportBase64Modal)
88+
}}
8189
>
8290
Import from Base64
8391
</button>
@@ -88,7 +96,10 @@ export const ImportDropdown: React.FC = () => {
8896
type="button"
8997
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150"
9098
title="Import from UUID"
91-
onClick={() => show(ImportUUIDModal)}
99+
onClick={() => {
100+
trackEvent('Import', 'click', 'Import AsyncAPI document from UUID');
101+
show(ImportUUIDModal);
102+
}}
92103
>
93104
Import from UUID
94105
</button>

apps/studio/src/components/Editor/SaveDropdown.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { FaSave } from 'react-icons/fa';
55
import { Dropdown, Tooltip } from '../common';
66
import { useServices } from '@/services';
77
import { useDocumentsState, useFilesState } from '../../state';
8+
import { trackEvent } from '@/helpers/analytics';
89

910
export const SaveDropdown: React.FC = () => {
1011
const { editorSvc } = useServices();
@@ -32,6 +33,11 @@ export const SaveDropdown: React.FC = () => {
3233
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
3334
title={`Save as ${language === 'yaml' ? 'YAML' : 'JSON'}`}
3435
onClick={() => {
36+
trackEvent(
37+
'Save',
38+
'click',
39+
`Save AsyncAPI document as ${language}`,
40+
);
3541
toast.promise(
3642
language === 'yaml'
3743
? editorSvc.saveAsYaml()
@@ -66,6 +72,13 @@ export const SaveDropdown: React.FC = () => {
6672
className="px-4 py-1 w-full text-left text-sm rounded-md focus:outline-none transition ease-in-out duration-150 disabled:cursor-not-allowed"
6773
title={`Convert and save as ${language === 'yaml' ? 'JSON' : 'YAML'}`}
6874
onClick={() => {
75+
trackEvent(
76+
'Save',
77+
'click',
78+
`Convert and save AsyncAPI document as ${
79+
language === 'yaml' ? 'JSON' : 'YAML'
80+
}`,
81+
);
6982
toast.promise(
7083
language === 'yaml'
7184
? editorSvc.saveAsJSON()

apps/studio/src/components/Editor/ShareButton.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import { FaShareAlt } from 'react-icons/fa';
33
import { useServices } from '../../services';
44
import { toast } from 'react-hot-toast';
55
import { Tooltip } from '../common';
6+
import { trackEvent } from '@/helpers/analytics';
67

78
interface ShareButtonProps {}
89

910
export const ShareButton: React.FunctionComponent<ShareButtonProps> = () => {
1011
const { editorSvc } = useServices();
1112

1213
const handleShare = () => {
14+
trackEvent('Share Button', 'click', 'Share AsyncAPI document');
1315
toast.promise(
1416
(async function () {
1517
const url = await editorSvc.exportAsURL();

apps/studio/src/components/SplitPane/Pane.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
/* eslint-disable */
22
// @ts-nocheck
33

4+
import { trackEvent } from "@/helpers/analytics";
5+
46
function Pane(props) {
57
const { children, className, split, style: styleProps, size, eleRef } = props;
68

@@ -23,6 +25,7 @@ function Pane(props) {
2325
}
2426

2527
style = Object.assign({}, style, styleProps || {});
28+
trackEvent('SplitPane', 'render', `Pane rendered with size: ${size || 'default'}`);
2629

2730
return (
2831
<div ref={eleRef} className={classes.join(' ')} style={style}>

apps/studio/src/components/Template/TemplateSidebar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import React from 'react';
22
import { VscRefresh } from 'react-icons/vsc';
33

44
import { useSettingsState, otherState } from '../../state';
5+
import { trackEvent } from '@/helpers/analytics';
56

67
interface TemplateSidebarProps {}
78

@@ -17,7 +18,10 @@ export const TemplateSidebar: React.FunctionComponent<TemplateSidebarProps> = ()
1718
<div />
1819
) : (
1920
<div className="ml-2 text-gray-500 text-xs flex" style={{ height: '30px', lineHeight: '30px' }}>
20-
<button type="button" className="text-xs" onClick={() => otherState.setState({ templateRerender: true })}>
21+
<button type="button" className="text-xs" onClick={() => {
22+
trackEvent('Template', 'rerender', 'Manual template rerender');
23+
otherState.setState({ templateRerender: true });
24+
}}>
2125
<div className="inline-block">
2226
<VscRefresh className="w-4 h-4 mt-1" />
2327
</div>

apps/studio/src/components/Terminal/ProblemsTab.tsx

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { FunctionComponent, useCallback, useMemo, useRef, useState } from 'react';
1+
import React, { FunctionComponent, useCallback, useMemo, useRef, useState, useEffect } from 'react';
22
import { VscError, VscWarning, VscInfo, VscLightbulb, VscSearch, VscClose, VscSettingsGear } from 'react-icons/vsc';
33
import { useModal } from '@ebay/nice-modal-react';
44
import { DiagnosticSeverity } from '@asyncapi/parser';
@@ -11,6 +11,7 @@ import { debounce } from '../../helpers';
1111
import { useDocumentsState, useSettingsState } from '../../state';
1212

1313
import type { Diagnostic } from '@asyncapi/parser';
14+
import { trackEvent } from '@/helpers/analytics';
1415

1516
interface ProblemsTabProps {}
1617

@@ -207,6 +208,10 @@ export const ProblemsTabContent: FunctionComponent<ProblemsTabProps> = () => {
207208
const [search, setSearch] = useState<string>('');
208209
const inputRef = useRef<HTMLInputElement>(null);
209210

211+
useEffect(() => {
212+
trackEvent('Diagnostics', 'panel_opened', 'Problems tab opened');
213+
}, []);
214+
210215
const setActiveFn = useCallback((severity: DiagnosticSeverity) => {
211216
setActive(acc => {
212217
if (acc.some(s => s === severity)) {
@@ -236,11 +241,19 @@ export const ProblemsTabContent: FunctionComponent<ProblemsTabProps> = () => {
236241
<SeverityButtons active={active} setActive={setActiveFn} />
237242
<div className='ml-2 flex-1 flex flex-row items-center justify-center rounded-md border border-transparent shadow-xs px-2 py-1 bg-gray-700 text-xs font-medium'>
238243
<VscSearch />
239-
<input ref={inputRef} placeholder='Filter diagnostics...' className='w-full bg-gray-700 border-transparent ml-2 focus:border-transparent focus:ring-0 focus:outline-none' onChange={debounce((e) => setSearch(e.target.value), 250)} />
244+
<input ref={inputRef} placeholder='Filter diagnostics...' className='w-full bg-gray-700 border-transparent ml-2 focus:border-transparent focus:ring-0 focus:outline-none' onChange={debounce((e) => {
245+
setSearch(e.target.value)
246+
if (e.target.value.length > 2 || e.target.value.length === 0) {
247+
trackEvent('Diagnostics', 'search', `Search term: "${e.target.value}"`);
248+
}
249+
}, 250)} />
240250
<button type='button' className={`hover:bg-gray-900 rounded-sm border border-transparent ${search ? 'opacity-100' : 'opacity-0'}`} onClick={() => {
241251
if (inputRef.current) {
242252
inputRef.current.value = '';
243253
}
254+
if (search) {
255+
trackEvent('Diagnostics', 'search_clear', 'Search cleared');
256+
}
244257
setSearch('');
245258
}}>
246259
<VscClose />
@@ -250,7 +263,14 @@ export const ProblemsTabContent: FunctionComponent<ProblemsTabProps> = () => {
250263
<button
251264
type="button"
252265
className={'justify-center border border-transparent shadow-xs px-2 py-1 ml-2 text-xs rounded-md font-medium text-white hover:bg-gray-900 focus:outline-none focus:ring-1 focus:ring-offset-1 focus:ring-gray-700'}
253-
onClick={() => modal.show({ activeTab: 'governance' })}
266+
onClick={() => {
267+
trackEvent(
268+
'Diagnostics',
269+
'settings_open',
270+
'Opened governance settings'
271+
);
272+
modal.show({ activeTab: 'governance' })
273+
}}
254274
>
255275
<VscSettingsGear className='w-4 h-4' />
256276
</button>
@@ -274,14 +294,23 @@ export const ProblemsTabContent: FunctionComponent<ProblemsTabProps> = () => {
274294
<td className="px-2 py-1 text-right"><SeverityIcon severity={severity} /></td>
275295
<td
276296
className="px-2 py-1 cursor-pointer"
277-
onClick={() =>
297+
onClick={() => {
298+
trackEvent('Diagnostics', 'diagnostic_click', `Clicked on diagnostic at line ${range.start.line + 1}`);
278299
navigationSvc.scrollToEditorLine(
279300
range.start.line + 1,
280301
range.start.character + 1,
281302
)
282303
}
304+
}
283305
onKeyDown={(e) => {
284306
if (e.key === 'Enter') {
307+
trackEvent(
308+
'Diagnostics',
309+
'diagnostic_click',
310+
`Clicked on diagnostic at line ${
311+
range.start.line + 1
312+
}`
313+
);
285314
navigationSvc.scrollToEditorLine(
286315
range.start.line + 1,
287316
range.start.character + 1,

0 commit comments

Comments
 (0)