From e832c9ed708b97a67ef7e5f052892089371c66b4 Mon Sep 17 00:00:00 2001 From: Alf Eaton Date: Fri, 30 Apr 2021 21:20:16 +0100 Subject: [PATCH] Add StartFreeTrialButton GitOrigin-RevId: dd9ab4bb12e8b9071b2a869e2d452945b49d9cd7 --- services/web/.storybook/preview.js | 5 ++ .../web/frontend/extracted-translations.json | 1 + .../preview/components/preview-error.js | 17 ++---- .../components/add-collaborators-upgrade.js | 27 +++------ .../components/share-project-modal.js | 8 --- .../js/infrastructure/event-tracking.js | 13 ++++ .../components/start-free-trial-button.js | 59 +++++++++++++++++++ .../web/frontend/stories/fixtures/context.js | 1 + .../preview-logs-pane-entry.stories.js | 6 +- .../stories/preview-logs-pane.stories.js | 44 ++++++++++++++ .../stories/share-project-modal.stories.js | 2 + 11 files changed, 142 insertions(+), 41 deletions(-) create mode 100644 services/web/frontend/js/infrastructure/event-tracking.js create mode 100644 services/web/frontend/js/shared/components/start-free-trial-button.js create mode 100644 services/web/frontend/stories/preview-logs-pane.stories.js diff --git a/services/web/.storybook/preview.js b/services/web/.storybook/preview.js index 1fc9cffca6..2b14b3e8b4 100644 --- a/services/web/.storybook/preview.js +++ b/services/web/.storybook/preview.js @@ -89,4 +89,9 @@ export const decorators = [withTheme] window.ExposedSettings = { maxEntitiesPerProject: 10, maxUploadSize: 5 * 1024 * 1024, + enableSubscriptions: true, +} + +window.user = { + id: 'storybook', } diff --git a/services/web/frontend/extracted-translations.json b/services/web/frontend/extracted-translations.json index 751c8e0d22..85a4846a38 100644 --- a/services/web/frontend/extracted-translations.json +++ b/services/web/frontend/extracted-translations.json @@ -244,6 +244,7 @@ "unlimited_projects": "", "unlink_github_repository": "", "unlinking": "", + "upgrade": "", "upgrade_for_longer_compiles": "", "upload": "", "use_your_own_machine": "", diff --git a/services/web/frontend/js/features/preview/components/preview-error.js b/services/web/frontend/js/features/preview/components/preview-error.js index 1a0906fea7..088804e3bf 100644 --- a/services/web/frontend/js/features/preview/components/preview-error.js +++ b/services/web/frontend/js/features/preview/components/preview-error.js @@ -5,7 +5,7 @@ import PreviewLogsPaneEntry from './preview-logs-pane-entry' import Icon from '../../../shared/components/icon' import { useApplicationContext } from '../../../shared/context/application-context' import { useEditorContext } from '../../../shared/context/editor-context' -import { startFreeTrial } from '../../../main/account-upgrade' +import StartFreeTrialButton from '../../../shared/components/start-free-trial-button' function PreviewError({ name }) { const { isProjectOwner } = useEditorContext({ @@ -84,10 +84,6 @@ function PreviewError({ name }) { function TimeoutUpgradePrompt({ isProjectOwner }) { const { t } = useTranslation() - function handleStartFreeTrialClick() { - startFreeTrial('compile-timeout') - } - const timeoutUpgradePromptContent = ( <>

{t('free_accounts_have_timeout_upgrade_to_increase')}

@@ -128,12 +124,11 @@ function TimeoutUpgradePrompt({ isProjectOwner }) { {isProjectOwner ? (

- +

) : null} diff --git a/services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.js b/services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.js index 21977a5a80..cf178c2e13 100644 --- a/services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.js +++ b/services/web/frontend/js/features/share-project-modal/components/add-collaborators-upgrade.js @@ -2,12 +2,10 @@ import React, { useState } from 'react' import { Trans } from 'react-i18next' import { Button } from 'react-bootstrap' import Icon from '../../../shared/components/icon' -import { startFreeTrial, upgradePlan } from '../../../main/account-upgrade' -import { useShareProjectContext } from './share-project-modal' +import { upgradePlan } from '../../../main/account-upgrade' +import StartFreeTrialButton from '../../../shared/components/start-free-trial-button' export default function AddCollaboratorsUpgrade() { - const { eventTracking } = useShareProjectContext() - const [startedFreeTrial, setStartedFreeTrial] = useState(false) return ( @@ -54,20 +52,11 @@ export default function AddCollaboratorsUpgrade() {

{window.user.allowedFreeTrial ? ( - + ) : ( )}

diff --git a/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js b/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js index 9283046c7d..c93c51631c 100644 --- a/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js +++ b/services/web/frontend/js/features/share-project-modal/components/share-project-modal.js @@ -16,9 +16,6 @@ ShareProjectContext.Provider.propTypes = { isAdmin: PropTypes.bool.isRequired, updateProject: PropTypes.func.isRequired, monitorRequest: PropTypes.func.isRequired, - eventTracking: PropTypes.shape({ - sendMB: PropTypes.func.isRequired, - }), inFlight: PropTypes.bool, setInFlight: PropTypes.func, error: PropTypes.oneOfType([PropTypes.bool, PropTypes.string]), @@ -87,7 +84,6 @@ export default function ShareProjectModal({ show, animation = true, isAdmin, - eventTracking, ide, }) { const [inFlight, setInFlight] = useState(false) @@ -148,7 +144,6 @@ export default function ShareProjectModal({ value={{ isAdmin, updateProject, - eventTracking, monitorRequest, inFlight, setInFlight, @@ -176,7 +171,4 @@ ShareProjectModal.propTypes = { $scope: PropTypes.object.isRequired, }).isRequired, show: PropTypes.bool.isRequired, - eventTracking: PropTypes.shape({ - sendMB: PropTypes.func.isRequired, - }), } diff --git a/services/web/frontend/js/infrastructure/event-tracking.js b/services/web/frontend/js/infrastructure/event-tracking.js new file mode 100644 index 0000000000..81efb218d0 --- /dev/null +++ b/services/web/frontend/js/infrastructure/event-tracking.js @@ -0,0 +1,13 @@ +import { postJSON } from './fetch-json' + +export function send(category, action, label, value) { + if (typeof window.ga === 'function') { + window.ga('send', 'event', category, action, label, value) + } +} + +export function sendMB(key, body = {}) { + postJSON(`/event/${key}`, { body }).catch(() => { + // ignore errors + }) +} diff --git a/services/web/frontend/js/shared/components/start-free-trial-button.js b/services/web/frontend/js/shared/components/start-free-trial-button.js new file mode 100644 index 0000000000..ab78a1bc4c --- /dev/null +++ b/services/web/frontend/js/shared/components/start-free-trial-button.js @@ -0,0 +1,59 @@ +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import { Button } from 'react-bootstrap' +import PropTypes from 'prop-types' +import * as eventTracking from '../../infrastructure/event-tracking' + +export default function StartFreeTrialButton({ + buttonStyle = 'info', + children, + classes = {}, + setStartedFreeTrial, + source, +}) { + const { t } = useTranslation() + + const handleClick = useCallback( + event => { + event.preventDefault() + + eventTracking.send('subscription-funnel', 'upgraded-free-trial', source) + + const plan = 'collaborator_free_trial_7_days' + + eventTracking.sendMB('subscription-start-trial', { source, plan }) + + if (setStartedFreeTrial) { + setStartedFreeTrial(true) + } + + const params = new URLSearchParams({ + planCode: plan, + ssp: 'true', + itm_campaign: source, + }) + + window.open(`/user/subscription/new?${params}`) + }, + [setStartedFreeTrial, source] + ) + + return ( + + ) +} +StartFreeTrialButton.propTypes = { + buttonStyle: PropTypes.string, + children: PropTypes.any, + classes: PropTypes.shape({ + button: PropTypes.string.isRequired, + }), + setStartedFreeTrial: PropTypes.func, + source: PropTypes.string.isRequired, +} diff --git a/services/web/frontend/stories/fixtures/context.js b/services/web/frontend/stories/fixtures/context.js index 342135e5b9..72375ef8fd 100644 --- a/services/web/frontend/stories/fixtures/context.js +++ b/services/web/frontend/stories/fixtures/context.js @@ -4,6 +4,7 @@ export function setupContext() { window.project_id = '1234' window.user = { id: 'fake_user', + allowedFreeTrial: true, } let $scope = {} if (window._ide) { diff --git a/services/web/frontend/stories/preview-logs-pane-entry.stories.js b/services/web/frontend/stories/preview-logs-pane-entry.stories.js index 747b328cfa..c8210c1cd6 100644 --- a/services/web/frontend/stories/preview-logs-pane-entry.stories.js +++ b/services/web/frontend/stories/preview-logs-pane-entry.stories.js @@ -100,7 +100,7 @@ function SampleHumanReadableHintComponent() { } export default { - title: 'PreviewLogsPaneEntry', + title: 'Preview Logs / Entry', component: PreviewLogsPaneEntry, args: { sourceLocation: { @@ -113,7 +113,7 @@ export default { The LaTeX compiler output * With a lot of details -Wrapped in an HTML
 element with 
+Wrapped in an HTML 
 element with
       preformatted text which is to be presented exactly
             as written in the HTML file
 
@@ -129,7 +129,7 @@ LaTeX Font Info:    External font \`cmex10' loaded for size
  \\Zlpha
 
  main.tex, line 23
-     
+
 `,
   },
 }
diff --git a/services/web/frontend/stories/preview-logs-pane.stories.js b/services/web/frontend/stories/preview-logs-pane.stories.js
new file mode 100644
index 0000000000..b1b8e97b38
--- /dev/null
+++ b/services/web/frontend/stories/preview-logs-pane.stories.js
@@ -0,0 +1,44 @@
+import React from 'react'
+import PreviewLogsPane from '../js/features/preview/components/preview-logs-pane'
+import { EditorProvider } from '../js/shared/context/editor-context'
+import { ApplicationProvider } from '../js/shared/context/application-context'
+import useFetchMock from './hooks/use-fetch-mock'
+
+export const TimedOutError = args => {
+  useFetchMock(fetchMock => {
+    fetchMock.post('express:/event/:key', 202)
+  })
+
+  const ide = {
+    $scope: {
+      $watch: () => () => null,
+      project: {
+        owner: {
+          _id: window.user.id,
+        },
+      },
+    },
+  }
+
+  return (
+    
+      
+        
+      
+    
+  )
+}
+TimedOutError.args = {
+  errors: {
+    timedout: {},
+  },
+}
+
+export default {
+  title: 'Preview Logs / Pane',
+  component: PreviewLogsPane,
+  argTypes: {
+    onLogEntryLocationClick: { action: 'log entry location' },
+    onClearCache: { action: 'clear cache' },
+  },
+}
diff --git a/services/web/frontend/stories/share-project-modal.stories.js b/services/web/frontend/stories/share-project-modal.stories.js
index 61647983fd..7f8819b0dd 100644
--- a/services/web/frontend/stories/share-project-modal.stories.js
+++ b/services/web/frontend/stories/share-project-modal.stories.js
@@ -71,6 +71,8 @@ const setupFetchMock = () => {
     .post('express:/project/:projectId/invite/:inviteId/resend', 200, {
       delay,
     })
+    // send analytics event
+    .post('express:/event/:key', 200)
 }
 
 const ideWithProject = project => {