From 3507463c4eb660e6765fd0daf7137b1333d3faec Mon Sep 17 00:00:00 2001 From: M Fahru Date: Mon, 30 Jan 2023 10:16:45 -0700 Subject: [PATCH] Add new event tracking to react and angular version of the empty project page (#11472) GitOrigin-RevId: 28ffe919851121bdba82eea3b6ef29cd6fffeb76 --- .../views/project/list/empty-project-list.pug | 42 ++++- .../components/new-project-button.tsx | 155 +++++++++++++++++- .../components/welcome-message.tsx | 23 ++- .../shared/components/controlled-dropdown.tsx | 36 +++- services/web/types/exposed-settings.ts | 1 + 5 files changed, 240 insertions(+), 17 deletions(-) diff --git a/services/web/app/views/project/list/empty-project-list.pug b/services/web/app/views/project/list/empty-project-list.pug index 29f71819aa..0e21cf4fee 100644 --- a/services/web/app/views/project/list/empty-project-list.pug +++ b/services/web/app/views/project/list/empty-project-list.pug @@ -4,9 +4,21 @@ div.welcome.text-centered(ng-cloak) h2 #{translate("welcome_to_sl")} p #{translate("new_to_latex_look_at")} - a(href="/templates") #{translate("templates").toLowerCase()} + a( + href="/templates" + event-tracking="welcome-page-templates-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default"}' + ) #{translate("templates").toLowerCase()} | #{translate("or")} - a(href="/learn") #{translate("latex_help_guide")} + a( + href="/learn" + event-tracking="welcome-page-latex-help-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default"}' + ) #{translate("latex_help_guide")} .row @@ -16,7 +28,12 @@ href="#", data-toggle="dropdown", dropdown-toggle + event-tracking="welcome-page-create-first-project-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "main-button", "dropdownOpen": "null" }' ) + //- We can't know if dropdown is open or not, so will send "dropdownOpen: null" as a segmentation above | Create First Project ul.dropdown-menu.minimal-create-proj-dropdown-menu(role="menu") @@ -24,23 +41,42 @@ a( href, ng-click="openCreateProjectModal()" + event-tracking="welcome-page-create-first-project-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "blank-project", "dropdownOpen": "true" }' ) #{translate("blank_project")} li a( href, ng-click="openCreateProjectModal('example')" + event-tracking="welcome-page-create-first-project-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "example-project", "dropdownOpen": "true" }' ) #{translate("example_project")} li a( href, ng-click="openUploadProjectModal()" + event-tracking="welcome-page-create-first-project-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation='{"project-dashboard-react": "default", "dropdownMenu": "upload-project", "dropdownOpen": "true" }' ) #{translate("upload_project")} != moduleIncludes("newProjectMenu", locals) if (templates) li.divider li.dropdown-header #{translate("templates")} each item in templates + - var eventSegmentation = '{"project-dashboard-react": "default", "dropdownMenu":"' + item.trackingKey + '", "dropdownOpen": "true" }' li - a.menu-indent(href=item.url) #{translate(item.name)} + a.menu-indent( + href=item.url + event-tracking="welcome-page-create-first-project-click" + event-tracking-mb="true" + event-tracking-trigger="click" + event-segmentation=eventSegmentation + ) #{translate(item.name)} diff --git a/services/web/frontend/js/features/project-list/components/new-project-button.tsx b/services/web/frontend/js/features/project-list/components/new-project-button.tsx index 984d7a5eca..0e937caa1b 100644 --- a/services/web/frontend/js/features/project-list/components/new-project-button.tsx +++ b/services/web/frontend/js/features/project-list/components/new-project-button.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { Dropdown, MenuItem } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { ExposedSettings } from '../../../../../types/exposed-settings' @@ -9,17 +9,35 @@ import NewProjectButtonModal, { NewProjectButtonModalVariant, } from './new-project-button/new-project-button-modal' import { Nullable } from '../../../../../types/utils' +import { sendMB } from '../../../infrastructure/event-tracking' + +type SendTrackingEvent = { + dropdownMenu: string + dropdownOpen: boolean + institutionTemplateName?: string +} + +type Segmentation = SendTrackingEvent & { + 'project-dashboard-react': 'enabled' +} + +type ModalMenuClickOptions = { + modalVariant: NewProjectButtonModalVariant + dropdownMenuEvent: string +} type NewProjectButtonProps = { id: string buttonText?: string className?: string + trackingKey?: string } function NewProjectButton({ id, buttonText, className, + trackingKey, }: NewProjectButtonProps) { const { t } = useTranslation() const { templateLinks } = getMeta('ol-ExposedSettings') as ExposedSettings @@ -27,9 +45,100 @@ function NewProjectButton({ useState>(null) const portalTemplates = getMeta('ol-portalTemplates') as PortalTemplate[] + const sendTrackingEvent = useCallback( + ({ + dropdownMenu, + dropdownOpen, + institutionTemplateName, + }: SendTrackingEvent) => { + if (trackingKey) { + let segmentation: Segmentation = { + 'project-dashboard-react': 'enabled', + dropdownMenu, + dropdownOpen, + } + + if (institutionTemplateName) { + segmentation = { + ...segmentation, + institutionTemplateName, + } + } + + sendMB(trackingKey, segmentation) + } + }, + [trackingKey] + ) + + const handleMainButtonClick = useCallback( + (dropdownOpen: boolean) => { + sendTrackingEvent({ + dropdownMenu: 'main-button', + dropdownOpen, + }) + }, + [sendTrackingEvent] + ) + + const handleModalMenuClick = useCallback( + ( + e: React.MouseEvent>, + { modalVariant, dropdownMenuEvent }: ModalMenuClickOptions + ) => { + // avoid invoking the "onClick" callback on the main dropdown button + e.stopPropagation() + + sendTrackingEvent({ + dropdownMenu: dropdownMenuEvent, + dropdownOpen: true, + }) + + setModal(modalVariant) + }, + [sendTrackingEvent] + ) + + const handlePortalTemplateClick = useCallback( + ( + e: React.MouseEvent>, + institutionTemplateName: string + ) => { + // avoid invoking the "onClick" callback on the main dropdown button + e.stopPropagation() + + sendTrackingEvent({ + dropdownMenu: 'institution-template', + dropdownOpen: true, + institutionTemplateName, + }) + }, + [sendTrackingEvent] + ) + + const handleStaticTemplateClick = useCallback( + ( + e: React.MouseEvent>, + templateTrackingKey: string + ) => { + // avoid invoking the "onClick" callback on the main dropdown button + e.stopPropagation() + + sendTrackingEvent({ + dropdownMenu: templateTrackingKey, + dropdownOpen: true, + }) + }, + [sendTrackingEvent] + ) + return ( <> - + - setModal('blank_project')}> + + handleModalMenuClick(e, { + modalVariant: 'blank_project', + dropdownMenuEvent: 'blank-project', + }) + } + > {t('blank_project')} - setModal('example_project')}> + + handleModalMenuClick(e, { + modalVariant: 'example_project', + dropdownMenuEvent: 'example-project', + }) + } + > {t('example_project')} - setModal('upload_project')}> + + handleModalMenuClick(e, { + modalVariant: 'upload_project', + dropdownMenuEvent: 'upload-project', + }) + } + > {t('upload_project')} - setModal('import_from_github')}> + + handleModalMenuClick(e, { + modalVariant: 'import_from_github', + dropdownMenuEvent: 'import-from-github', + }) + } + > {t('import_from_github')} {portalTemplates?.length > 0 ? ( @@ -60,6 +197,9 @@ function NewProjectButton({ + handlePortalTemplateClick(e, portalTemplate.name) + } > {portalTemplate.name} @@ -72,6 +212,9 @@ function NewProjectButton({ + handleStaticTemplateClick(e, templateLink.trackingKey) + } > {templateLink.name === 'view_all' ? t('view_all') diff --git a/services/web/frontend/js/features/project-list/components/welcome-message.tsx b/services/web/frontend/js/features/project-list/components/welcome-message.tsx index eb64c87e33..0f7989f7dc 100644 --- a/services/web/frontend/js/features/project-list/components/welcome-message.tsx +++ b/services/web/frontend/js/features/project-list/components/welcome-message.tsx @@ -1,19 +1,37 @@ +import { useCallback } from 'react' import { Col, Row } from 'react-bootstrap' import { useTranslation } from 'react-i18next' +import { sendMB } from '../../../infrastructure/event-tracking' import NewProjectButton from './new-project-button' export default function WelcomeMessage() { const { t } = useTranslation() + const handleTemplatesClick = useCallback(() => { + sendMB('welcome-page-templates-click', { + 'project-dashboard-react': 'enabled', + }) + }, []) + + const handleLatexHelpClick = useCallback(() => { + sendMB('welcome-page-latex-help-click', { + 'project-dashboard-react': 'enabled', + }) + }, []) + return (

{t('welcome_to_sl')}

{t('new_to_latex_look_at')}  - {t('templates').toLowerCase()} + + {t('templates').toLowerCase()} +  {t('or')}  - {t('latex_help_guide')} + + {t('latex_help_guide')} +

@@ -21,6 +39,7 @@ export default function WelcomeMessage() {
diff --git a/services/web/frontend/js/shared/components/controlled-dropdown.tsx b/services/web/frontend/js/shared/components/controlled-dropdown.tsx index c870cbe846..c4b6157111 100644 --- a/services/web/frontend/js/shared/components/controlled-dropdown.tsx +++ b/services/web/frontend/js/shared/components/controlled-dropdown.tsx @@ -1,14 +1,38 @@ -import { Children, cloneElement, type FC, isValidElement } from 'react' +import React, { + Children, + cloneElement, + type FC, + isValidElement, + useCallback, +} from 'react' import { Dropdown, DropdownProps } from 'react-bootstrap' import useDropdown from '../hooks/use-dropdown' -const ControlledDropdown: FC< - DropdownProps & { defaultOpen?: boolean } -> = props => { - const dropdownProps = useDropdown(Boolean(props.defaultOpen)) +type ControlledDropdownProps = DropdownProps & { + defaultOpen?: boolean + onMainButtonClick?: (dropdownOpen: boolean) => void +} + +const ControlledDropdown: FC = ({ + defaultOpen, + onMainButtonClick, + ...props +}) => { + const { onClick, ...dropdownProps } = useDropdown(Boolean(defaultOpen)) + + const handleClick = useCallback( + (e: React.MouseEvent) => { + onClick(e) + + if (onMainButtonClick) { + onMainButtonClick(dropdownProps.open) + } + }, + [onClick, onMainButtonClick, dropdownProps.open] + ) return ( - + {Children.map(props.children, child => { if (!isValidElement(child)) { return child diff --git a/services/web/types/exposed-settings.ts b/services/web/types/exposed-settings.ts index 547e82484e..7cf39cffcf 100644 --- a/services/web/types/exposed-settings.ts +++ b/services/web/types/exposed-settings.ts @@ -1,6 +1,7 @@ type TemplateLink = { name: string url: string + trackingKey: string } export type ExposedSettings = {