Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions frontend/rspack/rspack.config.local.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,18 @@ const base = require('../rspack.config')
module.exports = {
...base,
devServer: {
client: {
overlay: {
// Same as the dev server's defaults, which this object replaces.
errors: true,
// HubSpot's embedded forms resize their iframe from a ResizeObserver,
// which browsers report as this benign error (Sentry drops it too).
// Self-contained on purpose: the dev server rebuilds it in the browser.
runtimeErrors: (error) =>
!/ResizeObserver loop/.test(String(error && error.message)),
warnings: false,
},
},
historyApiFallback: true,
hot: true,
liveReload: false,
Expand Down
83 changes: 83 additions & 0 deletions frontend/web/components/modals/payment/ContactSalesForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { FC, useEffect } from 'react'
import { useScript } from 'common/hooks/useScript'
import AccountStore from 'common/stores/account-store'
import { AccountModel } from 'common/types/responses'
import {
CONTACT_SALES_FORM,
CONTACT_SALES_URL,
HUBSPOT_FORMS_SCRIPT_URL,
} from './constants'

// HubSpot passes a jQuery object when jQuery is on the page, as it is here.
type HubSpotFormArg = HTMLFormElement | { jquery: string; 0: HTMLFormElement }

type HubSpotFormOptions = typeof CONTACT_SALES_FORM & {
target: string
onFormReady: (form: HubSpotFormArg) => void
}

declare global {
interface Window {
hbspt?: { forms: { create: (options: HubSpotFormOptions) => void } }
}
}

const TARGET_ID = 'contact-sales-form'

// The form renders in an iframe, so set values the way HubSpot documents:
// assign, then fire an input event so the form registers them.
const prefill = (arg: HubSpotFormArg) => {
const form = 'jquery' in arg ? arg[0] : arg
const user = AccountStore.getUser() as AccountModel | undefined
if (!form || !user) return
const values: Record<string, string | undefined> = {
email: user.email,
firstname: user.first_name,
lastname: user.last_name,
}
Object.entries(values).forEach(([name, value]) => {
const input = form.querySelector<HTMLInputElement>(`input[name="${name}"]`)
if (!input || !value) return
input.value = value
input.dispatchEvent(new Event('input', { bubbles: true }))
})
}

const ContactSalesForm: FC = () => {
const { error, ready } = useScript(HUBSPOT_FORMS_SCRIPT_URL)

useEffect(() => {
if (ready && window.hbspt) {
window.hbspt.forms.create({
...CONTACT_SALES_FORM,
onFormReady: prefill,
target: `#${TARGET_ID}`,
})
}
}, [ready])

// Ad blockers commonly block HubSpot, so always leave a way through.
if (error || (ready && !window.hbspt)) {
return (
<div className='p-4'>
We couldn't load the form here. Please{' '}
<a href={CONTACT_SALES_URL} target='_blank' rel='noreferrer'>
contact us on our website
</a>
.
</div>
)
}

// HubSpot styles the form itself (dark labels), so it sits on a white
// card to stay readable in dark mode.
return (
<div className='p-4'>
<div className='bg-white rounded p-3'>
<div id={TARGET_ID} />
</div>
</div>
)
}

export default ContactSalesForm
10 changes: 8 additions & 2 deletions frontend/web/components/modals/payment/PricingPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Icon from 'components/icons/Icon'
import Button from 'components/base/forms/Button'
import { PricingFeaturesList } from './PricingFeaturesList'
import { PaymentButton } from './PaymentButton'
import { openChat } from 'common/loadChat'
import ContactSalesForm from './ContactSalesForm'
import { PricingFeature } from './types'

export type PricingPanelProps = {
Expand Down Expand Up @@ -110,7 +110,13 @@ export const PricingPanel = ({

{isEnterprise && (
<Button
onClick={() => openChat()}
onClick={() =>
openModal(
'Contact Sales',
<ContactSalesForm />,
'side-modal',
)
}
className='full-width btn-lg btn-tertiary mt-3'
>
Contact Sales
Expand Down
11 changes: 11 additions & 0 deletions frontend/web/components/modals/payment/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,14 @@ export const ON_PREMISE_HOSTING_URL =
// Support
export const SUPPORT_EMAIL = 'support@flagsmith.com'
export const SUPPORT_EMAIL_URL = 'mailto:support@flagsmith.com'

// HubSpot "Contact Enterprise Sales" form, the same one as
// www.flagsmith.com/contact-sales
export const CONTACT_SALES_URL = 'https://www.flagsmith.com/contact-sales'
export const HUBSPOT_FORMS_SCRIPT_URL =
'https://js-eu1.hsforms.net/forms/embed/v2.js'
export const CONTACT_SALES_FORM = {
formId: 'd71fdd04-bb77-485e-af7f-bd9c4e1c3bf5',
portalId: '143451822',
region: 'eu1',
}
Loading