mirror of
https://github.com/strapi/strapi.git
synced 2025-09-19 05:23:05 +00:00
Merge pull request #12722 from strapi/chore/events
Add events to monitor first administrator account creation
This commit is contained in:
commit
a79cafc670
@ -15,7 +15,13 @@ import { Grid, GridItem } from '@strapi/design-system/Grid';
|
||||
import { Typography } from '@strapi/design-system/Typography';
|
||||
import EyeStriked from '@strapi/icons/EyeStriked';
|
||||
import Eye from '@strapi/icons/Eye';
|
||||
import { Form, useQuery, useNotification } from '@strapi/helper-plugin';
|
||||
import {
|
||||
Form,
|
||||
useQuery,
|
||||
useNotification,
|
||||
useTracking,
|
||||
getYupInnerErrors,
|
||||
} from '@strapi/helper-plugin';
|
||||
import { useHistory } from 'react-router-dom';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Formik } from 'formik';
|
||||
@ -41,12 +47,14 @@ const PasswordInput = styled(TextInput)`
|
||||
}
|
||||
`;
|
||||
|
||||
const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
|
||||
const Register = ({ authType, fieldsToDisable, noSignin, onSubmit, schema }) => {
|
||||
const toggleNotification = useNotification();
|
||||
const { push } = useHistory();
|
||||
const [passwordShown, setPasswordShown] = useState(false);
|
||||
const [confirmPasswordShown, setConfirmPasswordShown] = useState(false);
|
||||
const [submitCount, setSubmitCount] = useState(0);
|
||||
const [userInfo, setUserInfo] = useState({});
|
||||
const { trackUsage } = useTracking();
|
||||
const { formatMessage } = useIntl();
|
||||
const query = useQuery();
|
||||
const registrationToken = query.get('registrationToken');
|
||||
@ -97,225 +105,218 @@ const Register = ({ fieldsToDisable, noSignin, onSubmit, schema }) => {
|
||||
registrationToken: registrationToken || undefined,
|
||||
news: false,
|
||||
}}
|
||||
onSubmit={(data, formik) => {
|
||||
if (registrationToken) {
|
||||
// We need to pass the registration token in the url param to the api in order to submit another admin user
|
||||
onSubmit({ userInfo: omit(data, ['registrationToken']), registrationToken }, formik);
|
||||
} else {
|
||||
onSubmit(data, formik);
|
||||
onSubmit={async (data, formik) => {
|
||||
try {
|
||||
await schema.validate(data, { abortEarly: false });
|
||||
|
||||
if (submitCount > 0 && authType === 'register-admin') {
|
||||
trackUsage('didSubmitWithErrorsFirstAdmin', { count: submitCount.toString() });
|
||||
}
|
||||
|
||||
if (registrationToken) {
|
||||
// We need to pass the registration token in the url param to the api in order to submit another admin user
|
||||
onSubmit(
|
||||
{ userInfo: omit(data, ['registrationToken']), registrationToken },
|
||||
formik
|
||||
);
|
||||
} else {
|
||||
onSubmit(data, formik);
|
||||
}
|
||||
} catch (err) {
|
||||
const errors = getYupInnerErrors(err);
|
||||
setSubmitCount(submitCount + 1);
|
||||
|
||||
formik.setErrors(errors);
|
||||
}
|
||||
}}
|
||||
validationSchema={schema}
|
||||
// Leaving this part commented when we remove the tracking for the submitCount
|
||||
// validationSchema={schema}
|
||||
validateOnChange={false}
|
||||
>
|
||||
{({ values, errors, handleChange }) => (
|
||||
<Form noValidate>
|
||||
<Main>
|
||||
<Column>
|
||||
<Logo />
|
||||
<Box paddingTop={6} paddingBottom={1}>
|
||||
<Typography as="h1" variant="alpha">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.welcome.title',
|
||||
defaultMessage: 'Welcome!',
|
||||
})}
|
||||
</Typography>
|
||||
</Box>
|
||||
<CenteredBox paddingBottom={7}>
|
||||
<Typography variant="epsilon" textColor="neutral600">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.register.subtitle',
|
||||
defaultMessage:
|
||||
'Your credentials are only used to authenticate yourself on the admin panel. All saved data will be stored in your own database.',
|
||||
})}
|
||||
</Typography>
|
||||
</CenteredBox>
|
||||
</Column>
|
||||
<Stack size={7}>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={6}>
|
||||
<TextInput
|
||||
name="firstname"
|
||||
required
|
||||
value={values.firstname}
|
||||
error={
|
||||
errors.firstname
|
||||
? formatMessage({
|
||||
id: errors.firstname,
|
||||
defaultMessage: 'This value is required.',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
onChange={handleChange}
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.firstname.label',
|
||||
defaultMessage: 'Firstname',
|
||||
{({ values, errors, handleChange }) => {
|
||||
return (
|
||||
<Form noValidate>
|
||||
<Main>
|
||||
<Column>
|
||||
<Logo />
|
||||
<Box paddingTop={6} paddingBottom={1}>
|
||||
<Typography as="h1" variant="alpha">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.welcome.title',
|
||||
defaultMessage: 'Welcome!',
|
||||
})}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem col={6}>
|
||||
<TextInput
|
||||
name="lastname"
|
||||
value={values.lastname}
|
||||
onChange={handleChange}
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.lastname.label',
|
||||
defaultMessage: 'Lastname',
|
||||
</Typography>
|
||||
</Box>
|
||||
<CenteredBox paddingBottom={7}>
|
||||
<Typography variant="epsilon" textColor="neutral600">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.register.subtitle',
|
||||
defaultMessage:
|
||||
'Your credentials are only used to authenticate yourself on the admin panel. All saved data will be stored in your own database.',
|
||||
})}
|
||||
/>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
<TextInput
|
||||
name="email"
|
||||
disabled={fieldsToDisable.includes('email')}
|
||||
value={values.email}
|
||||
onChange={handleChange}
|
||||
error={
|
||||
errors.email
|
||||
? formatMessage({
|
||||
id: errors.email,
|
||||
defaultMessage: 'This value is required.',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.email.label',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
type="email"
|
||||
/>
|
||||
<PasswordInput
|
||||
name="password"
|
||||
onChange={handleChange}
|
||||
value={values.password}
|
||||
error={
|
||||
errors.password
|
||||
? formatMessage({
|
||||
id: errors.password,
|
||||
defaultMessage: 'This value is required',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
endAction={
|
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<FieldActionWrapper
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setPasswordShown(prev => !prev);
|
||||
}}
|
||||
label={formatMessage(
|
||||
passwordShown
|
||||
? {
|
||||
id: 'Auth.form.password.show-password',
|
||||
defaultMessage: 'Show password',
|
||||
}
|
||||
: {
|
||||
id: 'Auth.form.password.hide-password',
|
||||
defaultMessage: 'Hide password',
|
||||
}
|
||||
)}
|
||||
>
|
||||
{passwordShown ? <Eye /> : <EyeStriked />}
|
||||
</FieldActionWrapper>
|
||||
}
|
||||
hint={formatMessage({
|
||||
id: 'Auth.form.password.hint',
|
||||
defaultMessage:
|
||||
'Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number',
|
||||
})}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.password.label',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
type={passwordShown ? 'text' : 'password'}
|
||||
/>
|
||||
<PasswordInput
|
||||
name="confirmPassword"
|
||||
onChange={handleChange}
|
||||
value={values.confirmPassword}
|
||||
error={
|
||||
errors.confirmPassword
|
||||
? formatMessage({
|
||||
id: errors.confirmPassword,
|
||||
defaultMessage: 'This value is required.',
|
||||
})
|
||||
: undefined
|
||||
}
|
||||
endAction={
|
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<FieldActionWrapper
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setConfirmPasswordShown(prev => !prev);
|
||||
}}
|
||||
label={formatMessage(
|
||||
confirmPasswordShown
|
||||
? {
|
||||
id: 'Auth.form.password.show-password',
|
||||
defaultMessage: 'Show password',
|
||||
}
|
||||
: {
|
||||
id: 'Auth.form.password.hide-password',
|
||||
defaultMessage: 'Hide password',
|
||||
}
|
||||
)}
|
||||
>
|
||||
{confirmPasswordShown ? <Eye /> : <EyeStriked />}
|
||||
</FieldActionWrapper>
|
||||
}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.confirmPassword.label',
|
||||
defaultMessage: 'Confirmation Password',
|
||||
})}
|
||||
type={confirmPasswordShown ? 'text' : 'password'}
|
||||
/>
|
||||
<Checkbox
|
||||
onValueChange={checked => {
|
||||
handleChange({ target: { value: checked, name: 'news' } });
|
||||
}}
|
||||
value={values.news}
|
||||
name="news"
|
||||
aria-label="news"
|
||||
>
|
||||
{formatMessage(
|
||||
{
|
||||
id: 'Auth.form.register.news.label',
|
||||
defaultMessage:
|
||||
'Keep me updated about the new features and upcoming improvements (by doing this you accept the {terms} and the {policy}).',
|
||||
},
|
||||
{
|
||||
terms: (
|
||||
<A target="_blank" href="https://strapi.io/terms" rel="noreferrer">
|
||||
{formatMessage({
|
||||
id: 'Auth.privacy-policy-agreement.terms',
|
||||
defaultMessage: 'terms',
|
||||
})}
|
||||
</A>
|
||||
),
|
||||
policy: (
|
||||
<A target="_blank" href="https://strapi.io/privacy" rel="noreferrer">
|
||||
{formatMessage({
|
||||
id: 'Auth.privacy-policy-agreement.policy',
|
||||
defaultMessage: 'policy',
|
||||
})}
|
||||
</A>
|
||||
),
|
||||
</Typography>
|
||||
</CenteredBox>
|
||||
</Column>
|
||||
<Stack size={7}>
|
||||
<Grid gap={4}>
|
||||
<GridItem col={6}>
|
||||
<TextInput
|
||||
name="firstname"
|
||||
required
|
||||
value={values.firstname}
|
||||
error={errors.firstname ? formatMessage(errors.firstname) : undefined}
|
||||
onChange={handleChange}
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.firstname.label',
|
||||
defaultMessage: 'Firstname',
|
||||
})}
|
||||
/>
|
||||
</GridItem>
|
||||
<GridItem col={6}>
|
||||
<TextInput
|
||||
name="lastname"
|
||||
value={values.lastname}
|
||||
onChange={handleChange}
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.lastname.label',
|
||||
defaultMessage: 'Lastname',
|
||||
})}
|
||||
/>
|
||||
</GridItem>
|
||||
</Grid>
|
||||
<TextInput
|
||||
name="email"
|
||||
disabled={fieldsToDisable.includes('email')}
|
||||
value={values.email}
|
||||
onChange={handleChange}
|
||||
error={errors.email ? formatMessage(errors.email) : undefined}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.email.label',
|
||||
defaultMessage: 'Email',
|
||||
})}
|
||||
type="email"
|
||||
/>
|
||||
<PasswordInput
|
||||
name="password"
|
||||
onChange={handleChange}
|
||||
value={values.password}
|
||||
error={errors.password ? formatMessage(errors.password) : undefined}
|
||||
endAction={
|
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<FieldActionWrapper
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setPasswordShown(prev => !prev);
|
||||
}}
|
||||
label={formatMessage(
|
||||
passwordShown
|
||||
? {
|
||||
id: 'Auth.form.password.show-password',
|
||||
defaultMessage: 'Show password',
|
||||
}
|
||||
: {
|
||||
id: 'Auth.form.password.hide-password',
|
||||
defaultMessage: 'Hide password',
|
||||
}
|
||||
)}
|
||||
>
|
||||
{passwordShown ? <Eye /> : <EyeStriked />}
|
||||
</FieldActionWrapper>
|
||||
}
|
||||
)}
|
||||
</Checkbox>
|
||||
<Button fullWidth size="L" type="submit">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.button.register',
|
||||
defaultMessage: "Let's start",
|
||||
})}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Main>
|
||||
</Form>
|
||||
)}
|
||||
hint={formatMessage({
|
||||
id: 'Auth.form.password.hint',
|
||||
defaultMessage:
|
||||
'Password must contain at least 8 characters, 1 uppercase, 1 lowercase and 1 number',
|
||||
})}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.password.label',
|
||||
defaultMessage: 'Password',
|
||||
})}
|
||||
type={passwordShown ? 'text' : 'password'}
|
||||
/>
|
||||
<PasswordInput
|
||||
name="confirmPassword"
|
||||
onChange={handleChange}
|
||||
value={values.confirmPassword}
|
||||
error={
|
||||
errors.confirmPassword ? formatMessage(errors.confirmPassword) : undefined
|
||||
}
|
||||
endAction={
|
||||
// eslint-disable-next-line react/jsx-wrap-multilines
|
||||
<FieldActionWrapper
|
||||
onClick={e => {
|
||||
e.preventDefault();
|
||||
setConfirmPasswordShown(prev => !prev);
|
||||
}}
|
||||
label={formatMessage(
|
||||
confirmPasswordShown
|
||||
? {
|
||||
id: 'Auth.form.password.show-password',
|
||||
defaultMessage: 'Show password',
|
||||
}
|
||||
: {
|
||||
id: 'Auth.form.password.hide-password',
|
||||
defaultMessage: 'Hide password',
|
||||
}
|
||||
)}
|
||||
>
|
||||
{confirmPasswordShown ? <Eye /> : <EyeStriked />}
|
||||
</FieldActionWrapper>
|
||||
}
|
||||
required
|
||||
label={formatMessage({
|
||||
id: 'Auth.form.confirmPassword.label',
|
||||
defaultMessage: 'Confirmation Password',
|
||||
})}
|
||||
type={confirmPasswordShown ? 'text' : 'password'}
|
||||
/>
|
||||
<Checkbox
|
||||
onValueChange={checked => {
|
||||
handleChange({ target: { value: checked, name: 'news' } });
|
||||
}}
|
||||
value={values.news}
|
||||
name="news"
|
||||
aria-label="news"
|
||||
>
|
||||
{formatMessage(
|
||||
{
|
||||
id: 'Auth.form.register.news.label',
|
||||
defaultMessage:
|
||||
'Keep me updated about the new features and upcoming improvements (by doing this you accept the {terms} and the {policy}).',
|
||||
},
|
||||
{
|
||||
terms: (
|
||||
<A target="_blank" href="https://strapi.io/terms" rel="noreferrer">
|
||||
{formatMessage({
|
||||
id: 'Auth.privacy-policy-agreement.terms',
|
||||
defaultMessage: 'terms',
|
||||
})}
|
||||
</A>
|
||||
),
|
||||
policy: (
|
||||
<A target="_blank" href="https://strapi.io/privacy" rel="noreferrer">
|
||||
{formatMessage({
|
||||
id: 'Auth.privacy-policy-agreement.policy',
|
||||
defaultMessage: 'policy',
|
||||
})}
|
||||
</A>
|
||||
),
|
||||
}
|
||||
)}
|
||||
</Checkbox>
|
||||
<Button fullWidth size="L" type="submit">
|
||||
{formatMessage({
|
||||
id: 'Auth.form.button.register',
|
||||
defaultMessage: "Let's start",
|
||||
})}
|
||||
</Button>
|
||||
</Stack>
|
||||
</Main>
|
||||
</Form>
|
||||
);
|
||||
}}
|
||||
</Formik>
|
||||
{!noSignin && (
|
||||
<Box paddingTop={4}>
|
||||
@ -341,10 +342,14 @@ Register.defaultProps = {
|
||||
};
|
||||
|
||||
Register.propTypes = {
|
||||
authType: PropTypes.string.isRequired,
|
||||
fieldsToDisable: PropTypes.array,
|
||||
noSignin: PropTypes.bool,
|
||||
onSubmit: PropTypes.func,
|
||||
schema: PropTypes.shape({ type: PropTypes.string.isRequired }).isRequired,
|
||||
schema: PropTypes.shape({
|
||||
validate: PropTypes.func.isRequired,
|
||||
type: PropTypes.string.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default Register;
|
||||
|
@ -4,7 +4,7 @@ import camelCase from 'lodash/camelCase';
|
||||
import get from 'lodash/get';
|
||||
import omit from 'lodash/omit';
|
||||
import { Redirect, useRouteMatch, useHistory } from 'react-router-dom';
|
||||
import { auth, useQuery, useGuidedTour } from '@strapi/helper-plugin';
|
||||
import { auth, useQuery, useGuidedTour, useTracking } from '@strapi/helper-plugin';
|
||||
import PropTypes from 'prop-types';
|
||||
import forms from 'ee_else_ce/pages/AuthPage/utils/forms';
|
||||
import persistStateToLocaleStorage from '../../components/GuidedTour/utils/persistStateToLocaleStorage';
|
||||
@ -17,6 +17,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
||||
const { push } = useHistory();
|
||||
const { changeLocale } = useLocalesProvider();
|
||||
const { setSkipped } = useGuidedTour();
|
||||
const { trackUsage } = useTracking();
|
||||
const {
|
||||
params: { authType },
|
||||
} = useRouteMatch('/auth/:authType');
|
||||
@ -146,6 +147,8 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
||||
|
||||
const registerRequest = async (body, requestURL, { setSubmitting, setErrors }) => {
|
||||
try {
|
||||
trackUsage('willCreateFirstAdmin');
|
||||
|
||||
const {
|
||||
data: {
|
||||
data: { token, user },
|
||||
@ -189,6 +192,8 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
||||
// Redirect to the homePage
|
||||
push('/');
|
||||
} catch (err) {
|
||||
trackUsage('didNotCreateFirstAdmin');
|
||||
|
||||
if (err.response) {
|
||||
const { data } = err.response;
|
||||
const apiErrors = formatAPIErrors(data);
|
||||
@ -249,6 +254,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
|
||||
return (
|
||||
<Component
|
||||
{...rest}
|
||||
authType={authType}
|
||||
fieldsToDisable={fieldsToDisable}
|
||||
formErrors={formErrors}
|
||||
inputsPrefix={inputsPrefix}
|
||||
|
@ -205,7 +205,13 @@ class Strapi {
|
||||
this.config.get('admin.autoOpen', true) !== false;
|
||||
|
||||
if (shouldOpenAdmin && !isInitialized) {
|
||||
await utils.openBrowser(this.config);
|
||||
try {
|
||||
await utils.openBrowser(this.config);
|
||||
this.telemetry.send('didOpenTab');
|
||||
} catch (e) {
|
||||
this.telemetry.send('didNotOpenTab');
|
||||
}
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user