Skip to content

Commit c1a6c43

Browse files
authored
feat: add marketo overlay to orgs list and global header (#6353)
* feat: add marketo overlay to orgs list and global header * chore: refactor marketo overlay, add more error handling * chore: refactor marketo component and add more error handling * chore: reorder handlers, adjust org list page biz logic * chore: improve toast, fix css comment, fix marketo enum
1 parent 7c003b5 commit c1a6c43

File tree

8 files changed

+345
-38
lines changed

8 files changed

+345
-38
lines changed

src/identity/components/GlobalHeader/GlobalHeaderDropdown/index.tsx

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ import {event} from 'src/cloud/utils/reporting'
3030

3131
export interface MainMenuItem {
3232
name: string
33+
onClick?: any
3334
iconFont: string
34-
href: string
35+
href?: string
3536
className?: string
3637
showDivider?: boolean
3738
}
@@ -125,6 +126,14 @@ export class GlobalHeaderDropdown extends React.Component<Props, State> {
125126
'global-header--main-dropdown-item',
126127
menuItem.className ?? ''
127128
)
129+
130+
const handleMenuItemClick = () => {
131+
if (menuItem.onClick) {
132+
menuItem.onClick()
133+
}
134+
onCollapse()
135+
}
136+
128137
return (
129138
<div
130139
onClick={this.sendMainMenuEvent(menuItem.name)}
@@ -137,9 +146,9 @@ export class GlobalHeaderDropdown extends React.Component<Props, State> {
137146
selected={false}
138147
>
139148
<Link
140-
to={menuItem.href}
149+
to={menuItem.href ?? '#'}
141150
className="global-header--main-dropdown-item-link"
142-
onClick={onCollapse}
151+
onClick={handleMenuItemClick}
143152
>
144153
{iconEl}
145154
{textEl}

src/identity/components/GlobalHeader/OrgDropdown.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import {
3838
selectOrgCreationAvailableUpgrade,
3939
} from 'src/identity/selectors'
4040
import {CreateOrganizationMenuItem} from 'src/identity/components/GlobalHeader/GlobalHeaderDropdown/CreateOrganization/MenuItem'
41+
import {dismissOverlay, showOverlay} from 'src/overlays/actions/overlays'
4142

4243
type OrgSummaryItem = OrganizationSummaries[number]
4344

@@ -58,6 +59,14 @@ export const OrgDropdown: FC<Props> = ({activeOrg, orgsList}) => {
5859

5960
const dispatch = useDispatch()
6061

62+
const openMarketoOverlay = () => {
63+
dispatch(
64+
showOverlay('marketo-upgrade-account-overlay', null, () =>
65+
dispatch(dismissOverlay())
66+
)
67+
)
68+
}
69+
6170
useEffect(() => {
6271
if (
6372
isFlagEnabled('createDeleteOrgs') &&
@@ -98,15 +107,22 @@ export const OrgDropdown: FC<Props> = ({activeOrg, orgsList}) => {
98107
if (
99108
isFlagEnabled('createDeleteOrgs') &&
100109
!orgCreationAllowed &&
101-
availableUpgrade !== 'none'
110+
(availableUpgrade === 'pay_as_you_go' || availableUpgrade === 'contract')
102111
) {
103-
orgMainMenu.push({
112+
const upgradeAccountMenuItem: MainMenuItem = {
104113
name: 'Add More Organizations',
105114
iconFont: IconFont.CrownSolid_New,
106-
href: '/checkout',
107115
className: 'upgrade-payg-add-org--button',
108116
showDivider: true,
109-
})
117+
}
118+
119+
if (availableUpgrade === 'pay_as_you_go') {
120+
upgradeAccountMenuItem.href = '/checkout'
121+
} else {
122+
upgradeAccountMenuItem.onClick = openMarketoOverlay
123+
}
124+
125+
orgMainMenu.push(upgradeAccountMenuItem)
110126
}
111127

112128
const sendDropdownClickEvent = () => {
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@import '@influxdata/clockface/dist/variables.scss';
2+
3+
.marketo-account-upgrade--form {
4+
display: none;
5+
}
6+
7+
.marketo-upgrade-account-overlay--body {
8+
p {
9+
font-size: $cf-text-base-1;
10+
margin-block-start: 0px;
11+
}
12+
}
Lines changed: 228 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,228 @@
1+
// Libraries
2+
import React, {FC, useCallback, useContext, useEffect, useState} from 'react'
3+
import {useDispatch, useSelector} from 'react-redux'
4+
import {
5+
Button,
6+
ComponentColor,
7+
ComponentStatus,
8+
Overlay,
9+
} from '@influxdata/clockface'
10+
11+
// Components
12+
import {OverlayContext} from 'src/overlays/components/OverlayController'
13+
14+
// Selectors
15+
import {selectCurrentAccountId, selectUser} from 'src/identity/selectors'
16+
17+
// Error Reporting
18+
import {reportErrorThroughHoneyBadger} from 'src/shared/utils/errors'
19+
20+
// Notifications
21+
import {
22+
marketoFormSubmitFailure,
23+
marketoFormSubmitSuccess,
24+
marketoLoadFailure,
25+
} from 'src/shared/copy/notifications/categories/accounts-users-orgs'
26+
27+
// Utils
28+
import {notify} from 'src/shared/actions/notifications'
29+
30+
// Styles
31+
import './MarketoAccountUpgradeOverlay.scss'
32+
33+
const popupLinkStyle = {
34+
color: 'white',
35+
textDecoration: 'underline',
36+
}
37+
38+
// Types
39+
declare let window: WindowObjectPlusMarketoScript
40+
41+
interface WindowObjectPlusMarketoScript extends Window {
42+
MktoForms2?: MarketoScript
43+
}
44+
45+
interface MarketoScript {
46+
loadForm?: Function
47+
getForm?: Function
48+
whenReady?: Function
49+
}
50+
51+
// Constants
52+
const BACKUP_CONTACT_SALES_LINK = 'https://www.influxdata.com/contact-sales-b/'
53+
const MARKETO_SERVER_INSTANCE = '//get.influxdata.com'
54+
const MARKETO_SUBSCRIPTION_ID = '972-GDU-533'
55+
const MARKETO_FORM_ID = 2826
56+
57+
// Error Messaging
58+
enum MarketoError {
59+
FormLoadingError = 'FormLoadingError',
60+
FormSubmitError = 'FormSubmitError',
61+
ScriptFetchError = 'ScriptFetchError',
62+
ScriptRuntimeError = 'ScriptRuntimeError',
63+
}
64+
65+
// If marketo isn't working, still need the user to have some means of contacting sales.
66+
const SalesFormLink = (): JSX.Element => {
67+
return (
68+
<a
69+
data-testid="use-sales-form--link"
70+
href={BACKUP_CONTACT_SALES_LINK}
71+
target="_blank"
72+
style={popupLinkStyle}
73+
>
74+
Click here to contact sales.
75+
</a>
76+
)
77+
}
78+
79+
export const MarketoAccountUpgradeOverlay: FC = () => {
80+
const {onClose} = useContext(OverlayContext)
81+
const dispatch = useDispatch()
82+
83+
const user = useSelector(selectUser)
84+
const accountId = useSelector(selectCurrentAccountId)
85+
86+
const [scriptHasLoaded, setScriptHasLoaded] = useState(false)
87+
const [formHasLoaded, setFormHasLoaded] = useState(false)
88+
89+
const handleCloseOverlay = () => {
90+
// Deleting marketo object guards against inconsistent behavior if user closes then re-opens the overlay.
91+
delete window.MktoForms2
92+
onClose()
93+
}
94+
95+
const handleError = useCallback(
96+
(message: MarketoError, err: Error = new Error(message)) => {
97+
if (message === MarketoError.FormSubmitError) {
98+
dispatch(notify(marketoFormSubmitFailure(SalesFormLink)))
99+
} else {
100+
dispatch(notify(marketoLoadFailure(SalesFormLink)))
101+
}
102+
103+
reportErrorThroughHoneyBadger(err, {
104+
context: {
105+
user,
106+
accountId,
107+
},
108+
name: `Marketo account upgrade form ${message}`,
109+
})
110+
},
111+
[accountId, dispatch, user]
112+
)
113+
114+
const handleFormSubmission = () => {
115+
try {
116+
window.MktoForms2.getForm(MARKETO_FORM_ID).submit()
117+
dispatch(notify(marketoFormSubmitSuccess()))
118+
onClose()
119+
} catch (err) {
120+
handleError(MarketoError.FormSubmitError, err)
121+
}
122+
}
123+
124+
const loadMarketoForm = useCallback(() => {
125+
try {
126+
if (!formHasLoaded) {
127+
// API: https://developers.marketo.com/javascript-api/forms/api-reference/
128+
window.MktoForms2?.loadForm(
129+
MARKETO_SERVER_INSTANCE,
130+
MARKETO_SUBSCRIPTION_ID,
131+
MARKETO_FORM_ID,
132+
() => {
133+
// Marketo fields should remain hidden from user (CSS display: none)
134+
135+
const marketoForm = window.MktoForms2
136+
if (marketoForm) {
137+
// Autofill the form with the current user and account id.
138+
marketoForm.getForm(MARKETO_FORM_ID).setValues({
139+
Quartz_User_ID__c: parseInt(user.id),
140+
Quartz_Account_ID__c: accountId,
141+
})
142+
143+
// Return false after form submission to prevent page reload.
144+
marketoForm.whenReady((form: any) => {
145+
form.onSuccess(() => false)
146+
})
147+
148+
document.querySelectorAll('input').forEach(input => {
149+
input.readOnly = true
150+
input.disabled = true
151+
})
152+
153+
setFormHasLoaded(true)
154+
}
155+
}
156+
)
157+
}
158+
} catch (err) {
159+
handleError(MarketoError.FormLoadingError, err)
160+
}
161+
}, [accountId, formHasLoaded, handleError, user])
162+
163+
useEffect(() => {
164+
try {
165+
if (!scriptHasLoaded) {
166+
const marketoScript = document.createElement('script')
167+
marketoScript.id = 'marketoScript'
168+
marketoScript.src = `${MARKETO_SERVER_INSTANCE}/js/forms2/js/forms2.min.js`
169+
marketoScript.async = true
170+
marketoScript.onload = loadMarketoForm
171+
marketoScript.onerror = () => handleError(MarketoError.ScriptFetchError)
172+
document.body.appendChild(marketoScript)
173+
setScriptHasLoaded(true)
174+
}
175+
} catch (err) {
176+
handleError(MarketoError.ScriptRuntimeError, err)
177+
}
178+
}, [accountId, handleError, loadMarketoForm, scriptHasLoaded, user])
179+
180+
const contactSalesButtonStatus =
181+
scriptHasLoaded && formHasLoaded
182+
? ComponentStatus.Default
183+
: ComponentStatus.Disabled
184+
185+
return (
186+
<Overlay.Container
187+
maxWidth={600}
188+
className="marketo-upgrade-account-overlay--container"
189+
testID="marketo-upgrade-account-overlay--container"
190+
>
191+
<Overlay.Header
192+
className="marketo-upgrade-account-overlay--header"
193+
testID="marketo-upgrade-account-overlay--header"
194+
title="Upgrade Your Account"
195+
onDismiss={handleCloseOverlay}
196+
/>
197+
<Overlay.Body className="marketo-upgrade-account-overlay--body">
198+
<p>
199+
You've reached the organization quota for your current account type.
200+
</p>
201+
<p>
202+
Click 'Contact Sales', and an InfluxData team member will reach out to
203+
you.
204+
</p>
205+
<br />
206+
<form
207+
id={`mktoForm_${MARKETO_FORM_ID.toString()}`}
208+
className="marketo-account-upgrade--form"
209+
/>
210+
</Overlay.Body>
211+
<Overlay.Footer>
212+
<Button
213+
text="Cancel"
214+
testID="marketo-upgrade-account-overlay--cancel-button"
215+
color={ComponentColor.Default}
216+
onClick={handleCloseOverlay}
217+
/>
218+
<Button
219+
text="Contact Sales"
220+
testID="create-org-form-submit"
221+
color={ComponentColor.Primary}
222+
status={contactSalesButtonStatus}
223+
onClick={handleFormSubmission}
224+
/>
225+
</Overlay.Footer>
226+
</Overlay.Container>
227+
)
228+
}

0 commit comments

Comments
 (0)