mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-21 20:47:08 -05:00
Add StartFreeTrialButton
GitOrigin-RevId: dd9ab4bb12e8b9071b2a869e2d452945b49d9cd7
This commit is contained in:
parent
e6acfda6e3
commit
e832c9ed70
11 changed files with 142 additions and 41 deletions
|
@ -89,4 +89,9 @@ export const decorators = [withTheme]
|
|||
window.ExposedSettings = {
|
||||
maxEntitiesPerProject: 10,
|
||||
maxUploadSize: 5 * 1024 * 1024,
|
||||
enableSubscriptions: true,
|
||||
}
|
||||
|
||||
window.user = {
|
||||
id: 'storybook',
|
||||
}
|
||||
|
|
|
@ -244,6 +244,7 @@
|
|||
"unlimited_projects": "",
|
||||
"unlink_github_repository": "",
|
||||
"unlinking": "",
|
||||
"upgrade": "",
|
||||
"upgrade_for_longer_compiles": "",
|
||||
"upload": "",
|
||||
"use_your_own_machine": "",
|
||||
|
|
|
@ -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}
|
||||
</>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
}),
|
||||
}
|
||||
|
|
13
services/web/frontend/js/infrastructure/event-tracking.js
Normal file
13
services/web/frontend/js/infrastructure/event-tracking.js
Normal 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
|
||||
})
|
||||
}
|
|
@ -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,
|
||||
}
|
|
@ -4,6 +4,7 @@ export function setupContext() {
|
|||
window.project_id = '1234'
|
||||
window.user = {
|
||||
id: 'fake_user',
|
||||
allowedFreeTrial: true,
|
||||
}
|
||||
let $scope = {}
|
||||
if (window._ide) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
|
44
services/web/frontend/stories/preview-logs-pane.stories.js
Normal file
44
services/web/frontend/stories/preview-logs-pane.stories.js
Normal 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' },
|
||||
},
|
||||
}
|
|
@ -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 => {
|
||||
|
|
Loading…
Reference in a new issue