diff --git a/package-lock.json b/package-lock.json index a532227482..450e463298 100644 --- a/package-lock.json +++ b/package-lock.json @@ -47,6 +47,7 @@ "json-refs": "^3.0.15", "lodash": "^4.17.21", "logrocket": "^8.1.2", + "logrocket-react": "^5.0.1", "luxon": "^3.4.3", "material-ui-popup-state": "^5.0.10", "mnemonist": "^0.39.8", @@ -10779,6 +10780,17 @@ "resolved": "https://registry.npmjs.org/logrocket/-/logrocket-8.1.2.tgz", "integrity": "sha512-9D9ZiDkRkoDqjY/vV5L31Ry7dtNRDGiEFniPWmDUILBRdrCX/kPTMjXlD/RdHd/mUbRYM827lXJBIuoVHpShtA==" }, + "node_modules/logrocket-react": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/logrocket-react/-/logrocket-react-5.0.1.tgz", + "integrity": "sha512-d5RAi1giur9Yv7/lDK/c2S1hviopN5K1XMMpfpSFRDG3Rr/j5RmGuvNHdXNTJUwGuxTVrpnKUOB43ceQMJfO1Q==", + "license": "MIT", + "peerDependencies": { + "logrocket": ">=2.0", + "react": ">=17.0", + "react-dom": ">=17.0" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", diff --git a/package.json b/package.json index 0da5ea4b41..2659a111dc 100644 --- a/package.json +++ b/package.json @@ -72,6 +72,7 @@ "json-refs": "^3.0.15", "lodash": "^4.17.21", "logrocket": "^8.1.2", + "logrocket-react": "^5.0.1", "luxon": "^3.4.3", "material-ui-popup-state": "^5.0.10", "mnemonist": "^0.39.8", diff --git a/src/app/guards/LegalGuard.tsx b/src/app/guards/LegalGuard.tsx index ca40141a23..ac87b0b96b 100644 --- a/src/app/guards/LegalGuard.tsx +++ b/src/app/guards/LegalGuard.tsx @@ -3,6 +3,7 @@ import ClickToAccept from 'directives/ClickToAccept'; import FullPageWrapper from 'app/FullPageWrapper'; import { FormattedMessage } from 'react-intl'; import { BaseComponentProps } from 'types'; +import { Box } from '@mui/material'; import useDirectiveGuard from './hooks'; const SELECTED_DIRECTIVE = 'clickToAccept'; @@ -37,11 +38,17 @@ function LegalGuard({ children }: BaseComponentProps) { if (status !== 'fulfilled') { return ( - + + + ); } else { diff --git a/src/app/guards/OnboardGuard.tsx b/src/app/guards/OnboardGuard.tsx index ff40345440..caa1f2e418 100644 --- a/src/app/guards/OnboardGuard.tsx +++ b/src/app/guards/OnboardGuard.tsx @@ -5,6 +5,8 @@ import { createOnboardingStore } from 'directives/Onboard/Store/create'; import { useMemo } from 'react'; import { OnboardingStoreNames } from 'stores/names'; import { BaseComponentProps } from 'types'; +import CustomerQuote from 'directives/Onboard/CustomerQuote'; +import { Grid } from '@mui/material'; import useDirectiveGuard from './hooks'; const SELECTED_DIRECTIVE = 'betaOnboard'; @@ -34,14 +36,26 @@ function OnboardGuard({ children, forceDisplay, grantsMutate }: Props) { return null; } else if (forceDisplay || status !== 'fulfilled') { return ( - - - - + + + + + + + + + + + ); } else { diff --git a/src/app/guards/RegistrationProgress.tsx b/src/app/guards/RegistrationProgress.tsx new file mode 100644 index 0000000000..b8207aafda --- /dev/null +++ b/src/app/guards/RegistrationProgress.tsx @@ -0,0 +1,37 @@ +import { LinearProgress, Stack, Typography } from '@mui/material'; +import { useIntl } from 'react-intl'; +import { RegistrationProgressProps } from './types'; + +const totalSteps = 2; + +function RegistrationProgress({ + loading, + status, + step, +}: RegistrationProgressProps) { + const intl = useIntl(); + + if (status === 'outdated') { + return null; + } + + return ( + + + {intl.formatMessage( + { id: 'login.progress.indicator' }, + { + step, + totalSteps, + } + )} + + + + ); +} + +export default RegistrationProgress; diff --git a/src/app/guards/types.ts b/src/app/guards/types.ts new file mode 100644 index 0000000000..aef199c318 --- /dev/null +++ b/src/app/guards/types.ts @@ -0,0 +1,7 @@ +import { DirectiveStates } from 'directives/types'; + +export interface RegistrationProgressProps { + status: DirectiveStates; + step: 1 | 2; + loading?: boolean; +} diff --git a/src/components/collection/Selector/List/Header/Toggle/MenuContent.tsx b/src/components/collection/Selector/List/Header/Toggle/MenuContent.tsx index d70819e402..eccec5fb73 100644 --- a/src/components/collection/Selector/List/Header/Toggle/MenuContent.tsx +++ b/src/components/collection/Selector/List/Header/Toggle/MenuContent.tsx @@ -18,6 +18,11 @@ interface Props { updateScope: (event: SyntheticEvent, newScope: Scopes) => void; } +// TODO (accessibility) this menu is not acessible and we have some options +// - wait for https://github.com/mui/material-ui/issues/43330 to be fixed +// - Write some tricky CSS in the parent that allows things to look the way they do but have all the items have a `MenuItem` around them +// - fork MUI's Menu just for these and disable the up/down keyboard interactions +// - Overhaul the entire approach to this menu and redesign it to somehow not need menu... let's not do this one function ScopeMenuContent({ closeMenu, initialScope, diff --git a/src/components/login/Providers/buttons/SSO.tsx b/src/components/login/Providers/buttons/SSO.tsx index 7f8ea41e08..8d93d7a812 100644 --- a/src/components/login/Providers/buttons/SSO.tsx +++ b/src/components/login/Providers/buttons/SSO.tsx @@ -1,37 +1,46 @@ -import { Box, Button, useTheme } from '@mui/material'; +import { Button, Typography, useTheme } from '@mui/material'; import { loginButtonStyling } from 'context/Theme'; -import { FormattedMessage } from 'react-intl'; +import { useIntl } from 'react-intl'; import { unauthenticatedRoutes } from 'app/routes'; -import { Lock } from 'iconoir-react'; -import MessageWithLink from 'components/content/MessageWithLink'; +import { Lock, OpenNewWindow } from 'iconoir-react'; +import { HTMLAttributeAnchorTarget, ReactNode } from 'react'; import { LoginProps } from '../types'; function SSOButton({ isRegister }: LoginProps) { + const intl = useIntl(); const theme = useTheme(); + let href: string = unauthenticatedRoutes.sso.login.fullPath; + let endIcon: ReactNode | undefined; + let startIcon: ReactNode | undefined = ; + let labelMessageId: string = 'cta.login.sso'; + let target: HTMLAttributeAnchorTarget = '_self'; if (isRegister) { - return ( - - - + endIcon = ( + + + ); + href = intl.formatMessage({ + id: 'login.sso.register.message.help.docPath', + }); + startIcon = undefined; + labelMessageId = 'login.sso.register.message.help'; + target = '_blank'; } return ( ); } diff --git a/src/components/login/Providers/index.tsx b/src/components/login/Providers/index.tsx index d8093726ae..0683a30657 100644 --- a/src/components/login/Providers/index.tsx +++ b/src/components/login/Providers/index.tsx @@ -1,5 +1,5 @@ import { Divider, Stack } from '@mui/material'; -import { FormattedMessage } from 'react-intl'; +import { useIntl } from 'react-intl'; import { getLoginSettings } from 'utils/env-utils'; import LoginButton from './LoginButton'; import { LoginProvidersProps } from './types'; @@ -13,6 +13,7 @@ function LoginProviders({ isRegister, providers = ['google', 'github', 'azure'], }: LoginProvidersProps) { + const intl = useIntl(); const { login } = useLoginHandler(grantToken, isRegister); return ( @@ -35,7 +36,11 @@ function LoginProviders({ {loginSettings.showSSO ? ( <> - + {intl.formatMessage({ + id: isRegister + ? 'login.sso.separator' + : 'login.separator', + })} diff --git a/src/components/login/SSO/index.tsx b/src/components/login/SSO/index.tsx index 81259b32a3..e0c007c113 100644 --- a/src/components/login/SSO/index.tsx +++ b/src/components/login/SSO/index.tsx @@ -10,7 +10,7 @@ import { supabaseClient } from 'context/GlobalProviders'; import React, { useState } from 'react'; import AlertBox from 'components/shared/AlertBox'; import { useSnackbar, VariantType } from 'notistack'; -import { FormattedMessage, useIntl } from 'react-intl'; +import { useIntl } from 'react-intl'; import { useNavigate } from 'react-router'; import { hasLength } from 'utils/misc-utils'; @@ -109,14 +109,14 @@ const SSOForm = ({ grantToken }: DefaultLoginProps) => { return ( - + {intl.formatMessage({ id: 'login.sso.header' })} {submitError ? ( {submitError} - + {intl.formatMessage({ id: 'error.tryAgain' })} @@ -126,11 +126,11 @@ const SSOForm = ({ grantToken }: DefaultLoginProps) => {
{ diff --git a/src/context/Theme.tsx b/src/context/Theme.tsx index 6f03a65253..94f371f155 100644 --- a/src/context/Theme.tsx +++ b/src/context/Theme.tsx @@ -328,6 +328,20 @@ export const editorToolBarSx: SxProps = { alignItems: 'center', }; +export const hiddenButAccessibleInput: SxProps = { + position: 'fixed', + opacity: 0, + pointerEvents: 'none', +}; + +export const hiddenButAccessibleRadio: SxProps = { + '& .MuiRadio-root, & .MuiRadio-root input': { + position: 'fixed', + opacity: 0, + pointerEvents: 'none', + }, +}; + export const defaultBoxShadow = 'rgb(50 50 93 / 7%) 0px 3px 6px -1px, rgb(0 0 0 / 10%) 0px -2px 4px -1px, rgb(0 0 0 / 10%) 0px 2px 4px -1px'; diff --git a/src/directives/AcceptGrant.tsx b/src/directives/AcceptGrant.tsx index 8103388d10..56289dc15c 100644 --- a/src/directives/AcceptGrant.tsx +++ b/src/directives/AcceptGrant.tsx @@ -1,4 +1,4 @@ -import { Box, Stack, Typography } from '@mui/material'; +import { Box, LinearProgress, Stack, Typography } from '@mui/material'; import { PostgrestError, PostgrestSingleResponse, @@ -103,8 +103,11 @@ function AcceptGrant({ } }; + // TODO (RegistrationProgress) get this wired up to know what step it is and use the RegistrationProgress component return ( + {saving ? : null} + {serverError ? ( { + const intl = useIntl(); + + return ( + + + + {intl.formatMessage({ + id: primaryMessageId, + })} + + + ); +}; + +export default Actions; diff --git a/src/directives/BetaOnboard.tsx b/src/directives/BetaOnboard.tsx index c6c9d3dd7f..8f77dcd9af 100644 --- a/src/directives/BetaOnboard.tsx +++ b/src/directives/BetaOnboard.tsx @@ -1,16 +1,8 @@ -import { - Box, - Stack, - Toolbar, - Typography, - useMediaQuery, - useTheme, -} from '@mui/material'; +import { Box, Stack, Typography } from '@mui/material'; import { PostgrestError } from '@supabase/postgrest-js'; import { submitDirective } from 'api/directives'; -import SafeLoadingButton from 'components/SafeLoadingButton'; +import RegistrationProgress from 'app/guards/RegistrationProgress'; import AlertBox from 'components/shared/AlertBox'; -import CustomerQuote from 'directives/Onboard/CustomerQuote'; import OrganizationNameField from 'directives/Onboard/OrganizationName'; import { useOnboardingStore_nameInvalid, @@ -22,16 +14,18 @@ import { } from 'directives/Onboard/Store/hooks'; import OnboardingSurvey from 'directives/Onboard/Survey'; import useJobStatusPoller from 'hooks/useJobStatusPoller'; -import { useMemo, useState } from 'react'; -import { FormattedMessage } from 'react-intl'; +import HeaderMessage from 'pages/login/HeaderMessage'; +import { useState } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; import { useMount, useUnmount } from 'react-use'; import { fireGtmEvent } from 'services/gtm'; import { hasLength } from 'utils/misc-utils'; +import Actions from './Actions'; import { jobStatusQuery, trackEvent } from './shared'; import { DirectiveProps } from './types'; const directiveName = 'betaOnboard'; -const nameTaken = 'is already in use'; +const NAME_TAKEN_MESSAGE = 'is already in use'; const submit_onboard = async ( requestedTenant: string, @@ -46,9 +40,8 @@ const submit_onboard = async ( ); }; -const BetaOnboard = ({ directive, mutate }: DirectiveProps) => { - const theme = useTheme(); - const belowMd = useMediaQuery(theme.breakpoints.down('md')); +const BetaOnboard = ({ directive, mutate, status }: DirectiveProps) => { + const intl = useIntl(); const { jobStatusPoller } = useJobStatusPoller(); @@ -60,6 +53,7 @@ const BetaOnboard = ({ directive, mutate }: DirectiveProps) => { const surveyResponse = useOnboardingStore_surveyResponse(); const resetOnboardingState = useOnboardingStore_resetState(); + const [nameTaken, setNameTaken] = useState(false); const [saving, setSaving] = useState(false); const [serverError, setServerError] = useState(null); @@ -103,108 +97,107 @@ const BetaOnboard = ({ directive, mutate }: DirectiveProps) => { void mutate(); }, async (payload: any) => { + const tenantTaken = Boolean( + payload?.job_status?.error?.includes( + NAME_TAKEN_MESSAGE + ) + ); + + // Handle tracking right away + fireGtmEvent('RegisterFailed', { + tenantAlreadyTaken: tenantTaken, + tenant: requestedTenant, + }); trackEvent(`${directiveName}:Error`, directive); + + // Update local state setSaving(false); - setServerError(payload.job_status.error); + setServerError(payload?.job_status?.error); + setNameTaken(tenantTaken); } ); } }, }; - const nameAlreadyUsed = useMemo( - () => serverError?.includes(nameTaken), - [serverError] - ); - useMount(() => { trackEvent(`${directiveName}:Viewed`); }); useUnmount(() => resetOnboardingState()); return ( - - + <> + + + + + + {serverError ? ( + + + {serverError} + + + ) : null} + + {nameMissing ? ( + + } + > + + {intl.formatMessage({ + id: 'tenant.errorMessage.empty', + })} + + + + ) : null} + - +
- - - - - {nameMissing ? ( - - } - > - - - - ) : null} - - {serverError ? ( - - } - > - {serverError} - - - ) : null} - + - - - - - - - - - - - - - -
-
+ + + +
+ + ); }; diff --git a/src/directives/ClickToAccept.tsx b/src/directives/ClickToAccept.tsx index a66b60e8a0..e63991c6c0 100644 --- a/src/directives/ClickToAccept.tsx +++ b/src/directives/ClickToAccept.tsx @@ -7,14 +7,16 @@ import { } from '@mui/material'; import { PostgrestError } from '@supabase/postgrest-js'; import { submitDirective } from 'api/directives'; -import SafeLoadingButton from 'components/SafeLoadingButton'; +import RegistrationProgress from 'app/guards/RegistrationProgress'; import AlertBox from 'components/shared/AlertBox'; import ExternalLink from 'components/shared/ExternalLink'; import useJobStatusPoller from 'hooks/useJobStatusPoller'; +import HeaderMessage from 'pages/login/HeaderMessage'; import { useState } from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import { useMount } from 'react-use'; import { getUrls } from 'utils/env-utils'; +import Actions from './Actions'; import { CLICK_TO_ACCEPT_LATEST_VERSION, jobStatusQuery, @@ -34,6 +36,7 @@ const submit_clickToAccept = async (directive: any) => { }; const ClickToAccept = ({ directive, status, mutate }: DirectiveProps) => { + const intl = useIntl(); const { jobStatusPoller } = useJobStatusPoller(); const [acknowledgedDocuments, setAcknowledgedDocuments] = @@ -104,23 +107,26 @@ const ClickToAccept = ({ directive, status, mutate }: DirectiveProps) => { alignItems: 'center', }} > - - - + + + {showErrors ? ( } + title={intl.formatMessage({ id: 'error.title' })} > - + {intl.formatMessage({ id: 'legal.docs.errorMessage' })} ) : null} @@ -128,7 +134,7 @@ const ClickToAccept = ({ directive, status, mutate }: DirectiveProps) => { } + title={intl.formatMessage({ id: 'common.fail' })} > {serverError} @@ -146,11 +152,11 @@ const ClickToAccept = ({ directive, status, mutate }: DirectiveProps) => { - + {intl.formatMessage({ id: 'legal.docs.privacy' })} - + {intl.formatMessage({ id: 'legal.docs.terms' })}
@@ -173,30 +179,21 @@ const ClickToAccept = ({ directive, status, mutate }: DirectiveProps) => { } onChange={handlers.update} name="accept" - label={ - - ), - terms: ( - - ), - }} - /> - } + label={intl.formatMessage( + { id: 'legal.docs.accept' }, + { + privacy: intl.formatMessage({ + id: 'legal.docs.privacy', + }), + terms: intl.formatMessage({ + id: 'legal.docs.terms', + }), + } + )} /> - - - + ); diff --git a/src/directives/Onboard/CustomerQuote.tsx b/src/directives/Onboard/CustomerQuote.tsx index 119c3bc75c..1229032b1b 100644 --- a/src/directives/Onboard/CustomerQuote.tsx +++ b/src/directives/Onboard/CustomerQuote.tsx @@ -1,26 +1,66 @@ -import { Box, useTheme } from '@mui/material'; +import { Box, useMediaQuery, useTheme } from '@mui/material'; +import { useIntl } from 'react-intl'; import customerQuoteDark from 'images/customer_quote-dark.png'; import customerQuoteLight from 'images/customer_quote-light.png'; -import { useIntl } from 'react-intl'; -interface Props { - hideQuote: boolean; -} -function CustomerQuote({ hideQuote }: Props) { +function CustomerQuote() { const theme = useTheme(); const intl = useIntl(); + const belowMd = useMediaQuery(theme.breakpoints.down('md')); - if (hideQuote) { + if (belowMd) { return null; } else { return ( + // TODO (customer quote) should switch this to HTML to load faster + // + // + // + // + // + // {`"${intl.formatMessage({ + // id: 'tenant.customer.quote', + // })}"`} + // + // + // {intl.formatMessage({ + // + // {intl.formatMessage({ ); diff --git a/src/directives/Onboard/Survey/OriginOption.tsx b/src/directives/Onboard/Survey/OriginOption.tsx new file mode 100644 index 0000000000..97408cde2b --- /dev/null +++ b/src/directives/Onboard/Survey/OriginOption.tsx @@ -0,0 +1,36 @@ +import { Chip, FormControlLabel, Radio } from '@mui/material'; +import { chipOutlinedStyling } from 'context/Theme'; +import { useOnboardingStore_surveyResponse } from 'directives/Onboard/Store/hooks'; +import { OriginOptionProps } from './types'; + +function OriginOption({ optionLabel: option }: OriginOptionProps) { + const surveyResponse = useOnboardingStore_surveyResponse(); + const currentOption = surveyResponse.origin === option; + + const labelId = `${option} label`; + const inputId = `${option} input`; + + return ( + } + htmlFor={inputId} + label={ + + } + /> + ); +} + +export default OriginOption; diff --git a/src/directives/Onboard/Survey.tsx b/src/directives/Onboard/Survey/index.tsx similarity index 56% rename from src/directives/Onboard/Survey.tsx rename to src/directives/Onboard/Survey/index.tsx index 172b776100..057b6969fe 100644 --- a/src/directives/Onboard/Survey.tsx +++ b/src/directives/Onboard/Survey/index.tsx @@ -1,11 +1,5 @@ -import { - FormControl, - FormControlLabel, - FormLabel, - Radio, - RadioGroup, - TextField, -} from '@mui/material'; +import { FormControl, FormLabel, RadioGroup } from '@mui/material'; +import { hiddenButAccessibleRadio } from 'context/Theme'; import { useOnboardingStore_setSurveyResponse, useOnboardingStore_surveyOptionOther, @@ -14,11 +8,7 @@ import { import { ChangeEvent } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; import useConstant from 'use-constant'; - -export interface SurveyResponse { - origin: string; - details: string; -} +import OriginOption from './OriginOption'; function OnboardingSurvey() { const intl = useIntl(); @@ -31,12 +21,11 @@ function OnboardingSurvey() { const originOptions: string[] = useConstant(() => [ intl.formatMessage({ id: 'tenant.origin.radio.browserSearch.label' }), - intl.formatMessage({ id: 'tenant.origin.radio.linkedIn.label' }), - intl.formatMessage({ id: 'tenant.origin.radio.referral.label' }), - intl.formatMessage({ id: 'tenant.origin.radio.youTube.label' }), - intl.formatMessage({ id: 'tenant.origin.radio.email.label' }), - intl.formatMessage({ id: 'tenant.origin.radio.gitHub.label' }), + intl.formatMessage({ id: 'tenant.origin.radio.socialMedia.label' }), intl.formatMessage({ id: 'tenant.origin.radio.paidAdvertising.label' }), + intl.formatMessage({ id: 'tenant.origin.radio.content.label' }), + intl.formatMessage({ id: 'tenant.origin.radio.referral.label' }), + intl.formatMessage({ id: 'tenant.origin.radio.webinar.label' }), surveyOptionOther, ]); @@ -57,38 +46,47 @@ function OnboardingSurvey() { }; return ( - - + + - {originOptions.map((option, index) => ( - } - label={option} - /> - ))} - - - - handlers.updateSurveyDetails(event.target.value) - } + row sx={{ - 'maxWidth': 400, - 'ml': 3, - '& .MuiInputBase-root': { borderRadius: 3 }, + ...hiddenButAccessibleRadio, + 'gap': 1, + '& .MuiFormControlLabel-root': { + ml: 0, + mr: 0, + }, + '& .MuiChip-root': { + p: 1, + }, }} - /> + > + {originOptions.map((option, index) => { + return ( + + ); + })} + ); } diff --git a/src/directives/Onboard/Survey/types.ts b/src/directives/Onboard/Survey/types.ts new file mode 100644 index 0000000000..4bd5fd183d --- /dev/null +++ b/src/directives/Onboard/Survey/types.ts @@ -0,0 +1,3 @@ +export interface OriginOptionProps { + optionLabel: string; +} diff --git a/src/directives/types.ts b/src/directives/types.ts index d5a7b47db8..2c398a3076 100644 --- a/src/directives/types.ts +++ b/src/directives/types.ts @@ -14,6 +14,11 @@ export interface Directives { storageMappings: DirectiveSettings; } +export interface ActionsProps { + primaryMessageId: string; + saving: boolean; +} + export type DirectiveStates = | 'unfulfilled' | 'in progress' @@ -61,6 +66,9 @@ export interface DirectiveSettings { calculateStatus: ( appliedDirective?: AppliedDirective | null ) => DirectiveStates; + // TODO (RegistrationProgress) - we need to know if a directive was used during the current session (this can be just in memory) + // so we need to store off if the user used something. That way we know which directive is which step in the process. + // updatedThisSession: boolean; } export interface DirectiveProps { diff --git a/src/lang/en-US/Authentication.ts b/src/lang/en-US/Authentication.ts index 2796fd9b18..fe5b09d364 100644 --- a/src/lang/en-US/Authentication.ts +++ b/src/lang/en-US/Authentication.ts @@ -24,8 +24,9 @@ export const Authentication: Record = { 'login.sso.back': `Back to Sign In`, 'login.sso.header': `Enter your company email to access ${CommonMessages.productName} via Single Sign-On.`, - 'login.sso.register.message.help': `To register with Single Sign-On {docLink}.`, - 'login.sso.register.message.help.docLink': `${CTAs['cta.contactUs']}`, + 'login.sso.separator': `Or to register with Single Sign-On`, + + 'login.sso.register.message.help': `${CTAs['cta.contactUs']}`, 'login.sso.register.message.help.docPath': `${Navigation['helpMenu.contact.link']}`, 'login.sso.message.help': `To enable Single Sign-On on your account {docLink}.`, @@ -56,6 +57,8 @@ export const Authentication: Record = { 'login.registerFailed.github': 'Failed to register with GitHub', 'login.userNotFound': 'User not found. Please sign up below.', + 'login.progress.indicator': 'Step {step} of {totalSteps}', + 'login.marketPlace.loggedOut': `To apply marketplace subscription, please login below.`, // Legal @@ -72,7 +75,6 @@ export const Authentication: Record = { 'legal.error.failedToFetch.message': `There was an issue while checking if you have accepted the latest {privacy} and {terms}.`, // Tenant - 'tenant.heading': `Let's get started`, 'tenant.message.1': `The organization name will be used as a prefix for everything you create within Estuary. It will only be public if you share data with other organizations.`, 'tenant.expectations': `You can use ${CommonMessages['catalogName.limitations']}`, @@ -80,19 +82,21 @@ export const Authentication: Record = { 'tenant.input.label': `Organization Name`, 'tenant.input.placeholder': `acmeCo`, - 'tenant.errorMessage.empty': `You must provide a name before continuing.`, + 'tenant.errorMessage.empty': `You must provide an organization name before continuing.`, 'tenant.docs.message': `To see a detailed explanation please view our {link}`, 'tenant.docs.message.link': `https://docs.estuary.dev/concepts/catalogs/#namespace`, - 'tenant.origin.radioGroup.label': `How'd you hear about us?`, - 'tenant.origin.radio.browserSearch.label': `Search (Google, Bing, etc.)`, - 'tenant.origin.radio.linkedIn.label': `LinkedIn`, - 'tenant.origin.radio.referral.label': `Referral by a Partner`, - 'tenant.origin.radio.youTube.label': `YouTube`, - 'tenant.origin.radio.email.label': `Email`, - 'tenant.origin.radio.gitHub.label': `GitHub`, - 'tenant.origin.radio.paidAdvertising.label': `Paid Advertising`, + 'tenant.customer.quote': `We're a big fan of Estuary's real-time, no code model. It's magic that we're getting real time data without much effort and we don't have to spend time thinking about broken pipelines. We've also experienced fantastic support!`, + + 'tenant.origin.radioGroup.label': `Where did you hear about ${CommonMessages.company}?`, + + 'tenant.origin.radio.browserSearch.label': `Google / Search Engine`, + 'tenant.origin.radio.socialMedia.label': `Social Media`, + 'tenant.origin.radio.paidAdvertising.label': `Online Ads`, + 'tenant.origin.radio.content.label': `Blog`, + 'tenant.origin.radio.referral.label': `Word of Mouth`, + 'tenant.origin.radio.webinar.label': `Webinar`, 'tenant.origin.radio.other.label': `Other`, 'tenant.grantDirective.header': `Tenant shared with you`, diff --git a/src/lang/en-US/CTAs.ts b/src/lang/en-US/CTAs.ts index 49608fac7a..b0cf2c1ea7 100644 --- a/src/lang/en-US/CTAs.ts +++ b/src/lang/en-US/CTAs.ts @@ -11,6 +11,7 @@ export const CTAs: Record = { 'cta.logout': `Logout`, 'cta.materialize': `Materialize`, 'cta.register': `Sign up`, + 'cta.registerFinish': `Complete Registration`, 'cta.resetPassword': `Reset Password`, 'cta.magicLink': `Sign in with magic link`, 'cta.verifyOTP': `Sign in with OTP`, diff --git a/src/pages/login/HeaderMessage.tsx b/src/pages/login/HeaderMessage.tsx new file mode 100644 index 0000000000..4a04f017db --- /dev/null +++ b/src/pages/login/HeaderMessage.tsx @@ -0,0 +1,25 @@ +import { Stack, Typography } from '@mui/material'; +import Logo from 'components/navigation/Logo'; +import { FormattedMessage } from 'react-intl'; +import { HeaderMessageProps } from './types'; + +function HeaderMessage({ isRegister, headerMessageId }: HeaderMessageProps) { + return ( + + + + + + + + ); +} + +export default HeaderMessage; diff --git a/src/pages/login/Wrapper.tsx b/src/pages/login/Wrapper.tsx index 8cde6c0092..e78b5936de 100644 --- a/src/pages/login/Wrapper.tsx +++ b/src/pages/login/Wrapper.tsx @@ -1,21 +1,13 @@ -import { Button, Stack, Typography } from '@mui/material'; +import { Button, Stack } from '@mui/material'; import { unauthenticatedRoutes } from 'app/routes'; import FullPageDialog from 'components/fullPage/Dialog'; -import Logo from 'components/navigation/Logo'; import useLoginBodyClass from 'hooks/login/useLoginBodyClass'; import { NavArrowLeft } from 'iconoir-react'; import { FormattedMessage } from 'react-intl'; -import { BaseComponentProps } from 'types'; +import HeaderMessage from './HeaderMessage'; import RegisterPerk from './Perk'; import LoginTabs from './Tabs'; - -interface Props extends BaseComponentProps { - isRegister: boolean; - tabIndex: number; - handleChange?: (event: any, val: any) => void; - headerMessageId?: string; - showBack?: boolean; -} +import { LoginWrapperProps } from './types'; const LoginWrapper = ({ children, @@ -24,7 +16,7 @@ const LoginWrapper = ({ showBack, tabIndex, headerMessageId, -}: Props) => { +}: LoginWrapperProps) => { useLoginBodyClass(); return ( @@ -48,20 +40,10 @@ const LoginWrapper = ({ - - - {/*Using h1 as this is the "most important" text on the page and might help with SEO*/} - - - - + {isRegister ? ( void; + headerMessageId?: string; + showBack?: boolean; +} + +export type HeaderMessageProps = Pick< + LoginWrapperProps, + 'isRegister' | 'headerMessageId' +>; diff --git a/src/services/gtm.ts b/src/services/gtm.ts index 421d0488a7..27ace26b19 100644 --- a/src/services/gtm.ts +++ b/src/services/gtm.ts @@ -3,7 +3,11 @@ import { getGoogleTageManagerSettings } from 'utils/env-utils'; // GTM is loaded/initialized in index.html -type EVENTS = 'Connector_Search' | 'Register' | 'Payment_Entered'; +type EVENTS = + | 'Connector_Search' + | 'Register' + | 'RegisterFailed' + | 'Payment_Entered'; const { allowedToRun } = getGoogleTageManagerSettings(); diff --git a/src/services/logrocket.ts b/src/services/logrocket.ts index 4b4eff6166..e8a7583ee9 100644 --- a/src/services/logrocket.ts +++ b/src/services/logrocket.ts @@ -2,6 +2,7 @@ import { User } from '@supabase/supabase-js'; import { includeKeys } from 'filter-obj'; import { isEmpty } from 'lodash'; import LogRocket from 'logrocket'; +import setupLogRocketReact from 'logrocket-react'; import { DEFAULT_FILTER, getUserDetails, @@ -201,6 +202,7 @@ export const initLogRocket = () => { } LogRocket.init(logRocketSettings.appID, settings); + setupLogRocketReact(LogRocket); } };