Skip to content

Commit

Permalink
Collect user data and integrate error page with Intercom (#1111)
Browse files Browse the repository at this point in the history
* Collect user data and integrate error page with Intercom

This commit puts an ErrorBoundary around each Route's parent component.
This way each ErrorBoundary is under the IntlProvider and has Intercom in scope so we integrate and prefill support request messages when crashes happen.
ErrorBoundary component now gets a `component` prop to specify which component it is wrapping and this will be sent with the errbit report.
It is also collecting user name, dbid and email to send with the report, to help gain more insight/context on the crashes.

Note I: The @airbrake-js/browser calls to the Errbit server seem to ignore the extra data added as context, params and session, unless the error is a string or we cast a new instance from the raised error object. To be investigated. See github issue: airbrake/airbrake-js#1193

Note II: MainErrorBoundary has been renamed to ErrorBoundary

Fixes: CHECK-1744

* Remove console log with intentional error 😅

Also to mention that change in helpers.js is to guard agains `null` value for `str`.

Fixes CHECK-1747

* Add missing files 🤦
  • Loading branch information
amoedoamorim committed Apr 25, 2022
1 parent fc82c29 commit 033de3b
Show file tree
Hide file tree
Showing 25 changed files with 885 additions and 783 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[
{
"id": "errorBoundary.message",
"description": "Prefilled support request message when Check UI crashes",
"defaultMessage": "Hello, I'm having trouble with Check. The web interface has just crashed and is blocking me from doing work!"
},
{
"id": "mainErrorBoundary.cardTitle",
"description": "Title for error state card displayed in error page",
"defaultMessage": "An unexpected error happened"
}
]
16 changes: 8 additions & 8 deletions src/app/components/Root.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import NotFound from './NotFound';
import UserConfirmPage from './UserConfirmPage';
import UserPasswordChange from './UserPasswordChange';
import UserPasswordReset from './UserPasswordReset';
import MainErrorBoundary from './error/MainErrorBoundary';
import ErrorBoundary from './error/ErrorBoundary';
import User from './source/User';
import Me from './source/Me';
import Team from './team/Team';
Expand Down Expand Up @@ -55,10 +55,10 @@ class Root extends Component {
window.Check = { store };

return (
<MainErrorBoundary>
<React.Fragment>
<RootLocale locale={locale} />
<IntlProvider locale={locale} messages={translations}>
<React.Fragment>
<RootLocale locale={locale} />
<IntlProvider locale={locale} messages={translations}>
<ErrorBoundary component="Root">
<Provider store={store}>
<Router history={browserHistory} onUpdate={Root.logPageView}>
<Route path="/" component={Home}>
Expand Down Expand Up @@ -106,9 +106,9 @@ class Root extends Component {
</Route>
</Router>
</Provider>
</IntlProvider>
</React.Fragment>
</MainErrorBoundary>
</ErrorBoundary>
</IntlProvider>
</React.Fragment>
);
}
}
Expand Down
155 changes: 79 additions & 76 deletions src/app/components/UserConfirmPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import Box from '@material-ui/core/Box';
import PageTitle from './PageTitle';
import { FormattedGlobalMessage } from './MappedMessage';
import CheckAgreeTerms from './CheckAgreeTerms';
import ErrorBoundary from './error/ErrorBoundary';
import globalStrings from '../globalStrings';
import { stringHelper } from '../customHelpers';
import {
Expand Down Expand Up @@ -46,83 +47,85 @@ function UserConfirmPage({ params }) {
const classes = useStyles();

return (
<Typography component="div" variant="body2" gutterBottom>
<PageTitle>
<Box m={2} align="center">
<FormattedHTMLMessage {...globalStrings.bestViewed} />
</Box>
<ContentColumn center className="user-confirm-page__component">
<StyledCard>
<FormattedGlobalMessage messageKey="appNameHuman">
{appNameHuman => (
<img
className={classes.logo}
alt={appNameHuman}
width="120"
src={stringHelper('LOGO_URL')}
/>
)}
</FormattedGlobalMessage>
<StyledSubHeader className="confirm__heading">
{ params.confirmType === 'confirmed' ?
(<FormattedMessage
id="UserConfirmPage.confirmed"
defaultMessage="Account Confirmed"
description="Message for confirmed accounts"
/>) : null
}
{ params.confirmType === 'already-confirmed' ?
(<FormattedMessage
id="UserConfirmPage.alreadyConfirmed"
defaultMessage="Account Already Confirmed"
description="Message for accounts already confirmed"
/>) : null
}
</StyledSubHeader>
<Typography component="div" align="center" paragraph className="confirm_content">
{ params.confirmType === 'confirmed' ?
(<FormattedMessage
id="userConfirmed.confrimMessage"
defaultMessage="Thanks for confirming your email address! Now you can sign in."
description="Message for confirmed user"
/>) : null
}
{ params.confirmType === 'already-confirmed' ?
(<FormattedMessage
id="userConfirmed.alreadyConfrimMessage"
defaultMessage="Oops! Your account is already confirmed. Please sign in to get started."
description="Message for user who already confirmed before"
/>) : null
}
{ params.confirmType === 'unconfirmed' ?
(<FormattedMessage
id="userConfirmed.unConfrimMessage"
defaultMessage="Sorry, an error occurred while confirming your account. Please try again and contact {supportEmail} if the condition persists."
values={{
supportEmail: stringHelper('SUPPORT_EMAIL'),
}}
description="Message for unconfirmed users"
/>) : null
}
</Typography>
<Typography component="div" align="center">
<Link to="/">
<Button variant="contained" color="primary">
<FormattedMessage
id="userConfirmPage.signIn"
defaultMessage="Sign In"
description="Sign in button"
/>
</Button>
</Link>
</Typography>
</StyledCard>
<Box my={4}>
<CheckAgreeTerms />
<ErrorBoundary component="UserConfirmPage">
<Typography component="div" variant="body2" gutterBottom>
<PageTitle>
<Box m={2} align="center">
<FormattedHTMLMessage {...globalStrings.bestViewed} />
</Box>
</ContentColumn>
</PageTitle>
</Typography>
<ContentColumn center className="user-confirm-page__component">
<StyledCard>
<FormattedGlobalMessage messageKey="appNameHuman">
{appNameHuman => (
<img
className={classes.logo}
alt={appNameHuman}
width="120"
src={stringHelper('LOGO_URL')}
/>
)}
</FormattedGlobalMessage>
<StyledSubHeader className="confirm__heading">
{ params.confirmType === 'confirmed' ?
(<FormattedMessage
id="UserConfirmPage.confirmed"
defaultMessage="Account Confirmed"
description="Message for confirmed accounts"
/>) : null
}
{ params.confirmType === 'already-confirmed' ?
(<FormattedMessage
id="UserConfirmPage.alreadyConfirmed"
defaultMessage="Account Already Confirmed"
description="Message for accounts already confirmed"
/>) : null
}
</StyledSubHeader>
<Typography component="div" align="center" paragraph className="confirm_content">
{ params.confirmType === 'confirmed' ?
(<FormattedMessage
id="userConfirmed.confrimMessage"
defaultMessage="Thanks for confirming your email address! Now you can sign in."
description="Message for confirmed user"
/>) : null
}
{ params.confirmType === 'already-confirmed' ?
(<FormattedMessage
id="userConfirmed.alreadyConfrimMessage"
defaultMessage="Oops! Your account is already confirmed. Please sign in to get started."
description="Message for user who already confirmed before"
/>) : null
}
{ params.confirmType === 'unconfirmed' ?
(<FormattedMessage
id="userConfirmed.unConfrimMessage"
defaultMessage="Sorry, an error occurred while confirming your account. Please try again and contact {supportEmail} if the condition persists."
values={{
supportEmail: stringHelper('SUPPORT_EMAIL'),
}}
description="Message for unconfirmed users"
/>) : null
}
</Typography>
<Typography component="div" align="center">
<Link to="/">
<Button variant="contained" color="primary">
<FormattedMessage
id="userConfirmPage.signIn"
defaultMessage="Sign In"
description="Sign in button"
/>
</Button>
</Link>
</Typography>
</StyledCard>
<Box my={4}>
<CheckAgreeTerms />
</Box>
</ContentColumn>
</PageTitle>
</Typography>
</ErrorBoundary>
);
}

Expand Down
107 changes: 55 additions & 52 deletions src/app/components/UserPasswordChange.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import PageTitle from './PageTitle';
import ChangePasswordComponent from './ChangePasswordComponent';
import { FormattedGlobalMessage } from './MappedMessage';
import CheckAgreeTerms from './CheckAgreeTerms';
import ErrorBoundary from './error/ErrorBoundary';
import globalStrings from '../globalStrings';
import { stringHelper } from '../customHelpers';
import {
Expand Down Expand Up @@ -46,60 +47,62 @@ function UserPasswordChange() {
const classes = useStyles();

return (
<PageTitle>
<Box m={2} align="center">
<FormattedHTMLMessage {...globalStrings.bestViewed} />
</Box>
<ContentColumn center className="user-password-reset__component">
<StyledCard>
<FormattedGlobalMessage messageKey="appNameHuman">
{appNameHuman => (
<img
className={classes.logo}
alt={appNameHuman}
width="120"
src={stringHelper('LOGO_URL')}
/>
)}
</FormattedGlobalMessage>
<StyledSubHeader className="reset-password__heading">
<ErrorBoundary component="UserPasswordChange">
<PageTitle>
<Box m={2} align="center">
<FormattedHTMLMessage {...globalStrings.bestViewed} />
</Box>
<ContentColumn center className="user-password-reset__component">
<StyledCard>
<FormattedGlobalMessage messageKey="appNameHuman">
{appNameHuman => (
<img
className={classes.logo}
alt={appNameHuman}
width="120"
src={stringHelper('LOGO_URL')}
/>
)}
</FormattedGlobalMessage>
<StyledSubHeader className="reset-password__heading">
{ showConfirmDialog ?
<FormattedMessage id="passwordChange.successTitle" defaultMessage="Password updated" />
: <FormattedMessage id="passwordChange.title" defaultMessage="Change password" />
}
</StyledSubHeader>

{ showConfirmDialog ?
<FormattedMessage id="passwordChange.successTitle" defaultMessage="Password updated" />
: <FormattedMessage id="passwordChange.title" defaultMessage="Change password" />
<React.Fragment>
<CardContent>
<FormattedMessage
id="passwordChange.successMsg"
defaultMessage="You're all set. Now you can log in with your new password."
/>
</CardContent>
<CardActions className="user-password-change__actions">
<Button color="primary" onClick={handleSignIn}>
<FormattedMessage id="passwordChange.signIn" defaultMessage="Got it" />
</Button>
</CardActions>
</React.Fragment> :
<div className="user-password-change__card">
<CardContent>
<ChangePasswordComponent
type="reset-password"
showCurrentPassword={false}
token={token}
showConfirm={showConfirm}
/>
</CardContent>
</div>
}
</StyledSubHeader>

{ showConfirmDialog ?
<React.Fragment>
<CardContent>
<FormattedMessage
id="passwordChange.successMsg"
defaultMessage="You're all set. Now you can log in with your new password."
/>
</CardContent>
<CardActions className="user-password-change__actions">
<Button color="primary" onClick={handleSignIn}>
<FormattedMessage id="passwordChange.signIn" defaultMessage="Got it" />
</Button>
</CardActions>
</React.Fragment> :
<div className="user-password-change__card">
<CardContent>
<ChangePasswordComponent
type="reset-password"
showCurrentPassword={false}
token={token}
showConfirm={showConfirm}
/>
</CardContent>
</div>
}
</StyledCard>
<Box my={4}>
<CheckAgreeTerms />
</Box>
</ContentColumn>
</PageTitle>
</StyledCard>
<Box my={4}>
<CheckAgreeTerms />
</Box>
</ContentColumn>
</PageTitle>
</ErrorBoundary>
);
}

Expand Down
Loading

0 comments on commit 033de3b

Please sign in to comment.