From 4e2967cd984b2e3ec5b2e31b1ff97bbb39bf0147 Mon Sep 17 00:00:00 2001 From: Alysson Melo Date: Thu, 1 Oct 2026 12:20:26 -0300 Subject: [PATCH 01/15] Instrument BaaS free->paid upgrade funnel Adds baas_upgrade_gate_viewed / baas_upgrade_gate_clicked on every paywall (web hosting, custom domain, email templates, parse options, jobs, collaborators, compliance, MongoDB 8.0, DB profiler, overview plan card) and baas_checkout_opened / baas_checkout_closed around the Paddle checkout. Paywall links pass ?gate= to plan-usage so the checkout events and "At Checkout - Subscription Successful" carry source and gate. Events are prefixed baas_ so the Agent's upgrade_* charts are unaffected. Co-Authored-By: Claude Opus 5.5 --- src/dashboard/AppPlan/AppPlan.react.js | 24 ++++++++- .../CustomParseOptions.react.js | 4 +- .../Data/AppOverview/AppOverview.react.js | 5 +- .../Data/AppOverview/AppPlanCard.react.js | 5 +- .../Data/AppOverview/ComplianceCard.react.js | 6 ++- src/dashboard/Data/Jobs/Jobs.react.js | 5 +- .../DatabaseProfiler.react.js | 25 +++++++++- .../DomainSettings/DomainSettings.react.js | 10 ++-- .../Notification/EmailPasswordReset.react.js | 4 +- .../Notification/EmailVerification.react.js | 4 +- src/dashboard/Settings/Collaborators.react.js | 8 ++- src/lib/upgradeEvents.js | 50 +++++++++++++++++++ 12 files changed, 134 insertions(+), 16 deletions(-) create mode 100644 src/lib/upgradeEvents.js diff --git a/src/dashboard/AppPlan/AppPlan.react.js b/src/dashboard/AppPlan/AppPlan.react.js index afbe4fec35..57ea17c24e 100644 --- a/src/dashboard/AppPlan/AppPlan.react.js +++ b/src/dashboard/AppPlan/AppPlan.react.js @@ -19,6 +19,8 @@ import B4aToggle from 'components/Toggle/B4aToggle.react'; import Icon from 'components/Icon/Icon.react'; import { initializePaddle } from '@paddle/paddle-js'; import B4aModal from 'components/B4aModal/B4aModal.react'; +import { amplitudeLogEvent } from 'lib/amplitudeEvents'; +import { UpgradeEvent, getGateFromSearch } from 'lib/upgradeEvents'; const prices = [ { @@ -210,8 +212,11 @@ class AppPlan extends DashboardView { pwCustomer: {} } + // Paddle calls this as a plain function, so keep a handle on the component for the funnel context. + const component = this; const paddleEventCallback = async function (data) { if (data.name === 'checkout.completed') { + component.checkoutCompleted = true; window.dataLayer = window.dataLayer || []; window.dataLayer.push({ event: 'paddle_purchase', @@ -254,6 +259,8 @@ class AppPlan extends DashboardView { planName: paymentData.planName, planType: data.data.items[0].billing_cycle.interval, subscriptionTotal: data.data.totals.total, + source: component.checkoutContext?.source, + gate: component.checkoutContext?.gate, } } ] @@ -320,6 +327,16 @@ class AppPlan extends DashboardView { } async handleOnClickPlan(plan) { + const gate = getGateFromSearch(this.props.location?.search); + this.checkoutContext = { + app_id: this.context.applicationId, + plan: plan.name, + cycle: this.state.billingCycle === 0 ? 'monthly' : 'yearly', + source: gate ? 'gate' : 'plan_page', + gate, + }; + this.checkoutCompleted = false; + amplitudeLogEvent(UpgradeEvent.CHECKOUT_OPENED, this.checkoutContext); this.setState({ selectedPlan: plan, openCheckout: true }, () => { const priceId = this.state.billingCycle === 0 ? plan.monthlyPlanId : plan.annuallyPlanId; const productId = this.state.billingCycle === 0 ? plan.monthlyProductId : plan.annuallyProductId; @@ -491,7 +508,12 @@ class AppPlan extends DashboardView { type={B4aModal.Types.DEFAULT} width={'80vw'} customFooter={
} - onCancel={() => this.setState({ openCheckout: false })} + onCancel={() => { + if (!this.checkoutCompleted) { + amplitudeLogEvent(UpgradeEvent.CHECKOUT_CLOSED, this.checkoutContext); + } + this.setState({ openCheckout: false }); + }} >
diff --git a/src/dashboard/CustomParseOptions/CustomParseOptions.react.js b/src/dashboard/CustomParseOptions/CustomParseOptions.react.js index 5573e06a4e..2e0e91e77d 100644 --- a/src/dashboard/CustomParseOptions/CustomParseOptions.react.js +++ b/src/dashboard/CustomParseOptions/CustomParseOptions.react.js @@ -29,6 +29,7 @@ import Icon from 'components/Icon/Icon.react'; import B4aModal from 'components/B4aModal/B4aModal.react'; import B4aCodeEditor from 'components/CodeEditor/B4aCodeEditor.react'; import { Link } from 'react-router-dom'; +import { UpgradeGate, UpgradeGateView, logGateClicked, planUsagePath } from 'lib/upgradeEvents'; import deepmerge from 'deepmerge'; import renderFlowFooterChanges from 'lib/renderFlowFooterChanges'; @@ -462,7 +463,8 @@ class CustomParseOptions extends DashboardView { } input={
- + logGateClicked(UpgradeGate.PARSE_OPTIONS, this.context.applicationId)}> +
diff --git a/src/dashboard/Data/AppOverview/ComplianceCard.react.js b/src/dashboard/Data/AppOverview/ComplianceCard.react.js index 6c87c71a5a..f45e5f6528 100644 --- a/src/dashboard/Data/AppOverview/ComplianceCard.react.js +++ b/src/dashboard/Data/AppOverview/ComplianceCard.react.js @@ -2,19 +2,23 @@ import React from 'react'; import styles from './AppOverview.scss'; import Icon from 'components/Icon/Icon.react'; import { amplitudeLogEvent } from 'lib/amplitudeEvents'; +import { UpgradeGate, UpgradeGateView, logGateClicked } from 'lib/upgradeEvents'; const complianceTypes = { 'HIPAA': { name: 'HIPAA', description: 'Healthcare privacy & security regulation', + gate: UpgradeGate.COMPLIANCE_HIPAA, }, 'SOC 2': { name: 'SOC 2', description: 'Audit-backed controls for secure data handling', + gate: UpgradeGate.COMPLIANCE_SOC2, }, 'ISO 27001': { name: 'ISO 27001', description: 'Global information-security management standard', + gate: UpgradeGate.COMPLIANCE_ISO27001, }, } @@ -71,7 +75,7 @@ const ComplianceItem = ({ type, enabled, appId, isSignedBAA }) => {
{complianceTypes[type].description}
- {showUpgrade && amplitudeLogEvent(`On Click - At Upgrade Plan for ${type} compliance`)} target="_blank" rel="noopener noreferrer">} + {showUpgrade && { amplitudeLogEvent(`On Click - At Upgrade Plan for ${type} compliance`); logGateClicked(complianceTypes[type].gate, appId); }} target="_blank" rel="noopener noreferrer">} {showSignBAA && amplitudeLogEvent(`On Click - At Sign BAA for ${type} compliance`)} target="_blank" rel="noopener noreferrer">} diff --git a/src/dashboard/Data/Jobs/Jobs.react.js b/src/dashboard/Data/Jobs/Jobs.react.js index 2cb8a76df3..f2cc6a0522 100644 --- a/src/dashboard/Data/Jobs/Jobs.react.js +++ b/src/dashboard/Data/Jobs/Jobs.react.js @@ -18,6 +18,7 @@ import FormNote from 'components/FormNote/FormNote.react'; import Popover from 'components/Popover/Popover.react'; import Position from 'lib/Position'; import React from 'react'; +import { UpgradeGate, UpgradeGateView, logGateClicked } from 'lib/upgradeEvents'; import ReleaseInfo from 'components/ReleaseInfo/ReleaseInfo'; import RunNowButton from 'dashboard/Data/Jobs/RunNowButton.react'; import SidebarAction from 'components/Sidebar/SidebarAction'; @@ -434,9 +435,11 @@ class Jobs extends TableView { buttonsInCenter={false} onCancel={() => this.setState({ jobLimitReached: false })} onConfirm={() => { - this.props.navigate(generatePath(this.context, 'plan-usage')); + logGateClicked(UpgradeGate.JOBS, this.context.applicationId); + this.props.navigate(generatePath(this.context, `plan-usage?gate=${UpgradeGate.JOBS}`)); }} > +
Upgrade your plan to schedule additional background jobs.
diff --git a/src/dashboard/DatabaseProfiler/DatabaseProfiler.react.js b/src/dashboard/DatabaseProfiler/DatabaseProfiler.react.js index 824f1eb5ca..b6e76b90dd 100644 --- a/src/dashboard/DatabaseProfiler/DatabaseProfiler.react.js +++ b/src/dashboard/DatabaseProfiler/DatabaseProfiler.react.js @@ -20,6 +20,8 @@ import { prices } from 'dashboard/AppPlan/AppPlan.react'; import Button from 'components/Button/Button.react'; import B4aModal from 'components/B4aModal/B4aModal.react'; import { initializePaddle } from '@paddle/paddle-js'; +import { amplitudeLogEvent } from 'lib/amplitudeEvents'; +import { UpgradeEvent, UpgradeGate, UpgradeGateView, logGateClicked } from 'lib/upgradeEvents'; import AccountManager from 'lib/AccountManager'; @withRouter @@ -113,6 +115,7 @@ class DatabaseProfile extends DashboardView { const paddleEventCallback = async (data) => { if (data.name === 'checkout.completed') { + this.checkoutCompleted = true; const paymentData = { appId: data.data.custom_data.app_id, customerId: data.data.customer.id, @@ -147,6 +150,8 @@ class DatabaseProfile extends DashboardView { planName: paymentData.planName, planType: data.data.items[0].billing_cycle.interval, subscriptionTotal: data.data.totals.total, + source: 'gate', + gate: UpgradeGate.DB_PROFILER, } } ] @@ -233,6 +238,16 @@ class DatabaseProfile extends DashboardView { // Find Dedicated plan const dedicatedPlan = prices.find(plan => plan.name === 'Dedicated'); if (!dedicatedPlan) { return; } + logGateClicked(UpgradeGate.DB_PROFILER, this.context.applicationId); + this.checkoutContext = { + app_id: this.context.applicationId, + plan: dedicatedPlan.name, + cycle: 'monthly', + source: 'gate', + gate: UpgradeGate.DB_PROFILER, + }; + this.checkoutCompleted = false; + amplitudeLogEvent(UpgradeEvent.CHECKOUT_OPENED, this.checkoutContext); this.setState({ openCheckout: true }, () => { const productId = dedicatedPlan.monthlyProductId; const planId = dedicatedPlan.monthlyPlanId; @@ -258,7 +273,7 @@ class DatabaseProfile extends DashboardView { }; handleComparePlansClick = (applicationId) => { - window.location.href = `${b4aSettings.BACKEND_DASHBOARD_PATH}/apps/${applicationId}/plan-usage`; + window.location.href = `${b4aSettings.BACKEND_DASHBOARD_PATH}/apps/${applicationId}/plan-usage?gate=${UpgradeGate.DB_PROFILER}`; }; renderError() { @@ -278,6 +293,7 @@ class DatabaseProfile extends DashboardView { title="Upgrade Required" description="Query Performance Monitor is available exclusively on Dedicated plans." /> + {/* Dedicated plan highlight card */}
} - onCancel={() => this.setState({ openCheckout: false })} + onCancel={() => { + if (!this.checkoutCompleted) { + amplitudeLogEvent(UpgradeEvent.CHECKOUT_CLOSED, this.checkoutContext); + } + this.setState({ openCheckout: false }); + }} >
diff --git a/src/dashboard/DomainSettings/DomainSettings.react.js b/src/dashboard/DomainSettings/DomainSettings.react.js index 2a199bb810..72382ce0ff 100644 --- a/src/dashboard/DomainSettings/DomainSettings.react.js +++ b/src/dashboard/DomainSettings/DomainSettings.react.js @@ -22,6 +22,7 @@ import Field from 'components/Field/Field.react'; import TextInput from 'components/TextInput/TextInput.react'; import Fieldset from 'components/Fieldset/Fieldset.react'; import { amplitudeLogEvent } from 'lib/amplitudeEvents'; +import { UpgradeGate, UpgradeGateView, logGateClicked, planUsagePath } from 'lib/upgradeEvents'; import B4aNotification from 'dashboard/Data/Browser/B4aNotification.react'; import browserStyles from 'dashboard/Data/Browser/Browser.scss'; import { Link } from 'react-router-dom'; @@ -407,7 +408,8 @@ class DomainSettings extends DashboardView { } input={
- + logGateClicked(UpgradeGate.WEB_HOSTING, this.context.applicationId)}> +
+ ) : ( + + Compare all plans + + )} + +
+ {error ? ( +
We could not load the checkout. Please refresh the page and try again.
+ ) : null} +
+
+ + + ); +}; + +// Paywall entry point: logs the gate view, and on click opens the direct checkout. +// `renderTrigger(open)` lets anchors and custom buttons keep their look. +export const UpgradeGateButton = ({ gate, renderTrigger, value = 'Upgrade Plan' }) => { + const context = useContext(CurrentApp); + const [open, setOpen] = useState(false); + const openCheckout = event => { + if (event && event.preventDefault) { + event.preventDefault(); + } + logGateClicked(gate, context.applicationId); + setOpen(true); + }; + + return ( + <> + + {renderTrigger ? renderTrigger(openCheckout) : } + {showUpgrade && ( + ( + + )} + /> + )} {showSignBAA && amplitudeLogEvent(`On Click - At Sign BAA for ${type} compliance`)} target="_blank" rel="noopener noreferrer">} diff --git a/src/dashboard/DomainSettings/DomainSettings.react.js b/src/dashboard/DomainSettings/DomainSettings.react.js index 72382ce0ff..0b340e40c2 100644 --- a/src/dashboard/DomainSettings/DomainSettings.react.js +++ b/src/dashboard/DomainSettings/DomainSettings.react.js @@ -22,10 +22,10 @@ import Field from 'components/Field/Field.react'; import TextInput from 'components/TextInput/TextInput.react'; import Fieldset from 'components/Fieldset/Fieldset.react'; import { amplitudeLogEvent } from 'lib/amplitudeEvents'; -import { UpgradeGate, UpgradeGateView, logGateClicked, planUsagePath } from 'lib/upgradeEvents'; +import { UpgradeGate } from 'lib/upgradeEvents'; +import { UpgradeGateButton } from 'components/UpgradeCheckout/UpgradeCheckout.react'; import B4aNotification from 'dashboard/Data/Browser/B4aNotification.react'; import browserStyles from 'dashboard/Data/Browser/Browser.scss'; -import { Link } from 'react-router-dom'; @withRouter class DomainSettings extends DashboardView { @@ -408,13 +408,7 @@ class DomainSettings extends DashboardView { } input={
- logGateClicked(UpgradeGate.WEB_HOSTING, this.context.applicationId)}> - -
} theme={Field.Theme.BLUE} /> @@ -433,13 +427,7 @@ class DomainSettings extends DashboardView { disabled={!this.state.canChangeSubdomain && this.state.currentSubdomain.trim().length === 0} /> ) : ( - logGateClicked(UpgradeGate.WEB_HOSTING, this.context.applicationId)}> - -
Upgrade to {plan.name}
{offer.headline}
@@ -180,7 +190,7 @@ export const UpgradeCheckoutModal = ({ gate, onClose }) => {
- + ); }; diff --git a/src/components/UpgradeCheckout/UpgradeCheckout.scss b/src/components/UpgradeCheckout/UpgradeCheckout.scss index 0999118ba0..800ca1157c 100644 --- a/src/components/UpgradeCheckout/UpgradeCheckout.scss +++ b/src/components/UpgradeCheckout/UpgradeCheckout.scss @@ -1,123 +1,198 @@ @import 'stylesheets/globals.scss'; @import 'stylesheets/back4app.scss'; +// One surface for the summary and the Paddle frame, in the user's light or dark preference. +.dark { + --uc-bg: #{$dark}; + --uc-panel: #15233b; + --uc-text: #f9f9f9; + --uc-muted: #9aa5b8; + --uc-border: rgba(255, 255, 255, 0.1); + --uc-option: rgba(255, 255, 255, 0.03); + --uc-option-selected: rgba(39, 174, 96, 0.12); +} + +.light { + --uc-bg: #ffffff; + --uc-panel: #f5f7fa; + --uc-text: #10203a; + --uc-muted: #5b6577; + --uc-border: #e2e6ec; + --uc-option: #ffffff; + --uc-option-selected: rgba(39, 174, 96, 0.08); +} + .checkout { + @include modalAnimation(); + @include InterFont; + position: absolute; + top: 50%; + left: 50%; display: flex; flex-wrap: wrap; - max-height: 80vh; + width: min(1040px, 92vw); + max-height: 90vh; overflow: auto; + border: 1px solid var(--uc-border); + border-radius: 12px; + background: var(--uc-bg); + color: var(--uc-text); text-align: left; + box-shadow: 0 24px 64px rgba(0, 0, 0, 0.35); +} + +.close { + position: absolute; + top: 16px; + right: 16px; + z-index: 1; + display: flex; + padding: 6px; + border: none; + border-radius: 6px; + background: transparent; + cursor: pointer; + + &:hover { + background: var(--uc-border); + } } .summary { - @include InterFont; - flex: 1 1 320px; - padding: 1.5rem 2rem; - background: $dark-blue; - color: $white; - border-radius: 4px 0 0 4px; + flex: 1 1 340px; + padding: 2.5rem 2.5rem 2rem; + background: var(--uc-panel); + border-right: 1px solid var(--uc-border); } .payment { - flex: 1 1 360px; - padding: 1rem; + flex: 1 1 380px; + padding: 2.5rem 2rem 1.5rem; } .eyebrow { font-size: 0.75rem; - letter-spacing: 0.1em; + font-weight: 600; + letter-spacing: 0.12em; text-transform: uppercase; - opacity: 0.7; + color: $cta-green; } .headline { - margin-top: 0.5rem; - font-size: 1.5rem; - font-weight: 600; - line-height: 1.3; + margin-top: 0.75rem; + font-size: 1.625rem; + font-weight: 700; + line-height: 1.25; } .price { - margin-top: 1.25rem; + margin-top: 1.5rem; } .priceValue { - font-size: 2.5rem; + font-size: 2.75rem; font-weight: 700; + letter-spacing: -0.02em; } .priceUnit { margin-left: 0.25rem; - opacity: 0.7; + color: var(--uc-muted); } .priceNote { + margin-top: 0.25rem; font-size: 0.875rem; - opacity: 0.7; + color: var(--uc-muted); } .cycles { display: flex; flex-direction: column; gap: 0.5rem; - margin-top: 1rem; + margin-top: 1.5rem; } .cycle { display: flex; align-items: center; - gap: 0.5rem; - font-size: 0.875rem; + gap: 0.625rem; + padding: 0.75rem 0.875rem; + border: 1px solid var(--uc-border); + border-radius: 8px; + background: var(--uc-option); + font-size: 0.9375rem; cursor: pointer; + transition: border-color 0.15s ease, background 0.15s ease; + + input { + margin: 0; + accent-color: $cta-green; + } + + &:has(input:checked) { + border-color: $cta-green; + background: var(--uc-option-selected); + } } .save { - color: $green; + margin-left: auto; + padding: 2px 8px; + border-radius: 999px; + background: rgba(39, 174, 96, 0.15); + color: $cta-green; + font-size: 0.75rem; font-weight: 600; } .benefits { display: flex; flex-direction: column; - gap: 0.5rem; - margin-top: 1.25rem; - padding-top: 1.25rem; - border-top: 1px solid rgba(255, 255, 255, 0.15); - font-size: 0.875rem; + gap: 0.625rem; + margin-top: 1.5rem; + padding-top: 1.5rem; + border-top: 1px solid var(--uc-border); + font-size: 0.9375rem; } .benefit { display: flex; align-items: center; - gap: 0.5rem; + gap: 0.625rem; } .total { display: flex; justify-content: space-between; - margin-top: 1.25rem; - padding-top: 1rem; - border-top: 1px solid rgba(255, 255, 255, 0.15); - font-weight: 600; + margin-top: 1.5rem; + padding-top: 1.25rem; + border-top: 1px solid var(--uc-border); + font-size: 1rem; + font-weight: 700; } .comparePlans { display: inline-block; - margin-top: 1rem; + margin-top: 1.25rem; font-size: 0.875rem; - color: $white; - opacity: 0.7; + color: var(--uc-muted); text-decoration: underline; + + &:hover { + color: var(--uc-text); + } } .done { display: flex; align-items: center; justify-content: space-between; - margin-top: 1rem; + gap: 1rem; + margin-top: 1.25rem; } .error { - padding: 1rem; - color: $dark-blue; + padding: 1rem 0; + color: var(--uc-text); } diff --git a/src/lib/tests/UpgradeCheckout.test.js b/src/lib/tests/UpgradeCheckout.test.js index e77d24a986..9ca43c6100 100644 --- a/src/lib/tests/UpgradeCheckout.test.js +++ b/src/lib/tests/UpgradeCheckout.test.js @@ -6,16 +6,9 @@ jest.mock('../paddleCheckout', () => { return { ...actual, initPaddle: jest.fn(), recordSubscription: jest.fn(() => Promise.resolve()) }; }); jest.mock('../../components/Icon/Icon.react', () => ({ __esModule: true, default: () => null })); -jest.mock('../../components/B4aModal/B4aModal.react', () => { +jest.mock('../../components/Popover/Popover.react', () => { const React = require('react'); - const B4aModal = ({ children, onCancel }) => ( -
-
- ); - B4aModal.Types = { DEFAULT: 'default' }; - return { __esModule: true, default: B4aModal }; + return { __esModule: true, default: ({ children }) =>
{children}
}; }); jest.mock('context/currentApp', () => ({ CurrentApp: require('react').createContext(null) }), { virtual: true }); jest.mock( @@ -130,7 +123,7 @@ describe('UpgradeCheckoutModal', () => { const onClose = jest.fn(); const tree = await mount(); - renderer.act(() => tree.root.findByProps({ id: 'modal-close' }).props.onClick()); + renderer.act(() => tree.root.findByProps({ 'aria-label': 'Close' }).props.onClick()); expect(loggedEvents('baas_checkout_closed')).toEqual([ { app_id: 'app-1', plan: 'MVP', cycle: 'monthly', source: 'gate', gate: 'email_templates' }, @@ -153,13 +146,26 @@ describe('UpgradeCheckoutModal', () => { // Tests run in the node environment, so stub the window the component reloads. const reload = jest.fn(); global.window = { location: { reload } }; - renderer.act(() => tree.root.findByProps({ id: 'modal-close' }).props.onClick()); + renderer.act(() => tree.root.findByProps({ 'aria-label': 'Close' }).props.onClick()); delete global.window; expect(reload).toHaveBeenCalled(); expect(loggedEvents('baas_checkout_closed')).toEqual([]); }); + it('follows the light or dark preference, including the Paddle frame', async () => { + await mount( {}} />); + expect(paddle.Checkout.open.mock.calls[0][0].settings.theme).toBe('light'); + + paddle.Checkout.open.mockClear(); + global.window = { matchMedia: query => ({ matches: query === '(prefers-color-scheme: dark)' }) }; + const tree = await mount( {}} />); + delete global.window; + + expect(paddle.Checkout.open.mock.calls[0][0].settings.theme).toBe('dark'); + expect(tree.root.findAll(node => typeof node.props.className === 'string' && node.props.className.includes('dark')).length).toBeGreaterThan(0); + }); + it('offers the plan that actually unlocks each compliance badge', async () => { await mount( {}} />); expect(paddle.Checkout.open.mock.calls[0][0].items[0].priceId).toBe('pri_payg_m'); From c88b2ee2306106bd1766fa0ba018700c18085358 Mon Sep 17 00:00:00 2001 From: Alysson Melo Date: Thu, 1 Oct 2026 15:31:25 -0300 Subject: [PATCH 08/15] Rename "Domain Settings" to Domains and "Server URL & Live Query" to Real-time Updates "Web hosting" meant two different things: the Server URL subdomain used by LiveQuery (every plan, Free with card validation) and the b4a.app Web URL + custom domains for the public folder (MVP+). Name them after what they do. Routes stay the same. serverInfo keeps the old names next to the new ones, because menu access is matched by name. Plan cards: MVP lists "Web URL & custom domain", Pay As You Go lists "HTTPS on your custom domain". Co-Authored-By: Claude Opus 5.5 --- src/dashboard/AppPlan/AppPlan.react.js | 5 ++++- src/dashboard/DashboardView.react.js | 4 ++-- src/dashboard/DomainSettings/DomainSettings.react.js | 12 ++++++------ .../ServerURLLiveQuery/ServerURLLiveQuery.react.js | 8 ++++---- src/lib/serverInfo.js | 2 ++ 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/dashboard/AppPlan/AppPlan.react.js b/src/dashboard/AppPlan/AppPlan.react.js index 409c3f128a..90f1b14605 100644 --- a/src/dashboard/AppPlan/AppPlan.react.js +++ b/src/dashboard/AppPlan/AppPlan.react.js @@ -37,7 +37,7 @@ const prices = [ savePercent: '40%', details: [ { - text: 'Custom domain & web hosting', + text: 'Web URL & custom domain', }, { text: 'Daily Backups', @@ -104,6 +104,9 @@ const prices = [ { text: 'Daily Backups', }, + { + text: 'HTTPS on your custom domain', + }, { text: 'SOC 2 and ISO 27001', }, diff --git a/src/dashboard/DashboardView.react.js b/src/dashboard/DashboardView.react.js index 3b8bb7440e..9a81c0c781 100644 --- a/src/dashboard/DashboardView.react.js +++ b/src/dashboard/DashboardView.react.js @@ -269,12 +269,12 @@ export default class DashboardView extends React.Component { // }); settingsSections.push({ - name: 'Domain Settings', + name: 'Domains', link: '/domain-settings', }); settingsSections.push({ - name: 'Server URL & Live Query', + name: 'Real-time Updates', link: '/server-url-live-query', }); diff --git a/src/dashboard/DomainSettings/DomainSettings.react.js b/src/dashboard/DomainSettings/DomainSettings.react.js index 0b340e40c2..5a3433250a 100644 --- a/src/dashboard/DomainSettings/DomainSettings.react.js +++ b/src/dashboard/DomainSettings/DomainSettings.react.js @@ -32,7 +32,7 @@ class DomainSettings extends DashboardView { constructor() { super(); this.section = 'App Settings'; - this.subsection = 'Domain Settings'; + this.subsection = 'Domains'; this.state = { isLoading: true, domainSettings: {}, @@ -191,7 +191,7 @@ class DomainSettings extends DashboardView { renderToolbar() { return ( - + @@ -406,7 +406,7 @@ class DomainSettings extends DashboardView { !this.state.canChangeSubdomain && !this.state.isActivated && this.state.currentSubdomain.trim().length === 0 && this.state.customDomainArray.length === 0) { content =
} + label={