Add StartFreeTrialButton

GitOrigin-RevId: dd9ab4bb12e8b9071b2a869e2d452945b49d9cd7
This commit is contained in:
Alf Eaton 2021-04-30 21:20:16 +01:00 committed by Copybot
parent e6acfda6e3
commit e832c9ed70
11 changed files with 142 additions and 41 deletions

View file

@ -89,4 +89,9 @@ export const decorators = [withTheme]
window.ExposedSettings = {
maxEntitiesPerProject: 10,
maxUploadSize: 5 * 1024 * 1024,
enableSubscriptions: true,
}
window.user = {
id: 'storybook',
}

View file

@ -244,6 +244,7 @@
"unlimited_projects": "",
"unlink_github_repository": "",
"unlinking": "",
"upgrade": "",
"upgrade_for_longer_compiles": "",
"upload": "",
"use_your_own_machine": "",

View file

@ -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 = (
<>
<p>{t('free_accounts_have_timeout_upgrade_to_increase')}</p>
@ -128,12 +124,11 @@ function TimeoutUpgradePrompt({ isProjectOwner }) {
</div>
{isProjectOwner ? (
<p className="text-center">
<button
className="btn btn-success row-spaced-small"
onClick={handleStartFreeTrialClick}
>
{t('start_free_trial')}
</button>
<StartFreeTrialButton
source="compile-timeout"
buttonStyle="success"
classes={{ button: 'row-spaced-small' }}
/>
</p>
) : null}
</>

View file

@ -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() {
<p className="text-center row-spaced-thin">
{window.user.allowedFreeTrial ? (
<Button
bsStyle="success"
onClick={() => {
startFreeTrial(
'projectMembers',
undefined,
undefined,
eventTracking
)
setStartedFreeTrial(true)
}}
>
<Trans i18nKey="start_free_trial" />
</Button>
<StartFreeTrialButton
buttonStyle="success"
setStartedFreeTrial={setStartedFreeTrial}
source="projectMembers"
/>
) : (
<Button
bsStyle="success"
@ -76,7 +65,7 @@ export default function AddCollaboratorsUpgrade() {
setStartedFreeTrial(true)
}}
>
<Trans i18nKey="start_free_trial" />
<Trans i18nKey="upgrade" />
</Button>
)}
</p>

View file

@ -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,
}),
}

View file

@ -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
})
}

View file

@ -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 (
<Button
bsStyle={buttonStyle}
onClick={handleClick}
className={classes.button}
>
{children || t('start_free_trial')}
</Button>
)
}
StartFreeTrialButton.propTypes = {
buttonStyle: PropTypes.string,
children: PropTypes.any,
classes: PropTypes.shape({
button: PropTypes.string.isRequired,
}),
setStartedFreeTrial: PropTypes.func,
source: PropTypes.string.isRequired,
}

View file

@ -4,6 +4,7 @@ export function setupContext() {
window.project_id = '1234'
window.user = {
id: 'fake_user',
allowedFreeTrial: true,
}
let $scope = {}
if (window._ide) {

View file

@ -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 <pre> element with
Wrapped in an HTML <pre> 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
<recently read> \\Zlpha
main.tex, line 23
`,
},
}

View file

@ -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 (
<ApplicationProvider>
<EditorProvider ide={ide} settings={{}}>
<PreviewLogsPane {...args} />
</EditorProvider>
</ApplicationProvider>
)
}
TimedOutError.args = {
errors: {
timedout: {},
},
}
export default {
title: 'Preview Logs / Pane',
component: PreviewLogsPane,
argTypes: {
onLogEntryLocationClick: { action: 'log entry location' },
onClearCache: { action: 'clear cache' },
},
}

View file

@ -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 => {