Merge branch 'v4/ds-migration' into webhooks-listview-ds

This commit is contained in:
ronronscelestes 2021-08-17 15:03:41 +02:00
commit e8c352eb8e
17 changed files with 264 additions and 284 deletions

View File

@ -1,7 +1,3 @@
'use strict';
const GoogleStrategy = require('passport-google-oauth2');
module.exports = ({ env }) => ({
host: env('HOST', '0.0.0.0'),
port: env.int('PORT', 1337),
@ -9,32 +5,6 @@ module.exports = ({ env }) => ({
// autoOpen: true,
auth: {
secret: env('ADMIN_JWT_SECRET', 'example-token'),
providers: [
{
uid: 'google',
displayName: 'Google',
icon: 'https://cdn2.iconfinder.com/data/icons/social-icons-33/128/Google-512.png',
createStrategy: strapi =>
new GoogleStrategy(
{
clientID: env('GOOGLE_CLIENT_ID'),
clientSecret: env('GOOGLE_CLIENT_SECRET'),
scope: [
'https://www.googleapis.com/auth/userinfo.email',
'https://www.googleapis.com/auth/userinfo.profile',
],
callbackURL: strapi.admin.services.passport.getStrategyCallbackURL('google'),
},
(request, accessToken, refreshToken, profile, done) => {
done(null, {
email: profile.email,
firstname: profile.given_name,
lastname: profile.family_name,
});
}
),
},
],
},
},
});

View File

@ -0,0 +1,9 @@
import styled from 'styled-components';
import { Button } from '@strapi/parts/Button';
const AuthButton = styled(Button)`
display: inline-block;
width: 100%;
`;
export default AuthButton;

View File

@ -0,0 +1,14 @@
import styled from 'styled-components';
import { FieldAction } from '@strapi/parts/Field';
const FieldActionWrapper = styled(FieldAction)`
svg {
height: 1rem;
width: 1rem;
path {
fill: ${({ theme }) => theme.colors.neutral600};
}
}
`;
export default FieldActionWrapper;

View File

@ -6,36 +6,21 @@ import {
H1,
Text,
Subtitle,
Button,
Checkbox,
TextInput,
Main,
FieldAction,
Row,
Link,
} from '@strapi/parts';
import { Form } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { useIntl } from 'react-intl';
import { Formik } from 'formik';
import { Column, LayoutContent } from '../../../../layouts/UnauthenticatedLayout';
import Form from './Form';
import Logo from '../Logo';
const AuthButton = styled(Button)`
display: inline-block;
width: 100%;
`;
const FieldActionWrapper = styled(FieldAction)`
svg {
height: 16px;
width: 16px;
path {
fill: ${({ theme }) => theme.colors.neutral600};
}
}
`;
import AuthButton from '../AuthButton';
import FieldActionWrapper from '../FieldActionWrapper';
const Login = ({ onSubmit, schema, children }) => {
const [passwordShown, setPasswordShown] = useState(false);
@ -49,6 +34,7 @@ const Login = ({ onSubmit, schema, children }) => {
initialValues={{
email: '',
password: '',
rememberMe: false,
}}
onSubmit={onSubmit}
validationSchema={schema}
@ -91,7 +77,6 @@ const Login = ({ onSubmit, schema, children }) => {
name="password"
type={passwordShown ? 'text' : 'password'}
endAction={
// eslint-disable-next-line react/jsx-wrap-multilines
<FieldActionWrapper
onClick={e => {
e.stopPropagation();

View File

@ -1,28 +0,0 @@
import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { Text } from '@buffetjs/core';
import { useIntl } from 'react-intl';
const CustomLabel = ({ id, onClick, values }) => {
const { formatMessage } = useIntl();
return (
<Text
as="span"
fontWeight="regular"
lineHeight="18px"
onClick={onClick}
style={{ cursor: 'pointer' }}
>
{formatMessage({ id }, values)}
</Text>
);
};
CustomLabel.propTypes = {
id: PropTypes.string.isRequired,
onClick: PropTypes.func.isRequired,
values: PropTypes.object.isRequired,
};
export default memo(CustomLabel);

View File

@ -1,8 +0,0 @@
import styled from 'styled-components';
const InputWrapper = styled.div`
display: flex;
justify-content: space-between;
`;
export default InputWrapper;

View File

@ -1,13 +0,0 @@
import styled from 'styled-components';
import { Text } from '@buffetjs/core';
const Span = styled(Text)`
color: #0097f7;
cursor: pointer;
`;
Span.defaultProps = {
as: 'span',
};
export default Span;

View File

@ -1,173 +1,219 @@
/* eslint-disable jsx-a11y/click-events-have-key-events */
/* eslint-disable jsx-a11y/no-static-element-interactions */
import React from 'react';
import { Checkbox, Flex, Padded, Text } from '@buffetjs/core';
import { useIntl, FormattedMessage } from 'react-intl';
import { get } from 'lodash';
import React, { useState } from 'react';
import { useIntl } from 'react-intl';
import styled from 'styled-components';
import { Show, Hide } from '@strapi/icons';
import {
Main,
Box,
H1,
Subtitle,
Stack,
TextInput,
Grid,
GridItem,
Checkbox,
Link,
Row,
} from '@strapi/parts';
import { Form } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
import Button from '../../../../components/FullWidthButton';
import AuthLink from '../AuthLink';
import Input from '../Input';
import { Formik } from 'formik';
import UnauthenticatedLayout, {
Column,
LayoutContent,
} from '../../../../layouts/UnauthenticatedLayout';
import Logo from '../Logo';
import Section from '../Section';
import CustomLabel from './CustomLabel';
import Box from '../Box';
import InputWrapper from './InputWrapper';
import Span from './Span';
import AuthButton from '../AuthButton';
import FieldActionWrapper from '../FieldActionWrapper';
const Register = ({
fieldsToDisable,
formErrors,
inputsPrefix,
modifiedData,
noSignin,
onChange,
onSubmit,
requestError,
}) => {
const CenteredBox = styled(Box)`
text-align: center;
`;
const A = styled.a`
color: ${({ theme }) => theme.colors.primary600};
`;
const Register = ({ fieldsToDisable, noSignin, onSubmit, modifiedData, schema }) => {
const [passwordShown, setPasswordShown] = useState(false);
const [confirmPasswordShown, setConfirmPasswordShown] = useState(false);
const { formatMessage } = useIntl();
const handleClick = (e, to) => {
e.preventDefault();
e.stopPropagation();
const win = window.open(`https://strapi.io/${to}`, '_blank');
win.focus();
};
const terms = (
<FormattedMessage id="Auth.privacy-policy-agreement.terms" key="1">
{content => <Span onClick={e => handleClick(e, 'terms')}>{content}</Span>}
</FormattedMessage>
);
const policy = (
<FormattedMessage id="Auth.privacy-policy-agreement.policy" key="2">
{content => <Span onClick={e => handleClick(e, 'privacy')}>{content}</Span>}
</FormattedMessage>
);
return (
<>
<Section textAlign="center">
<Logo />
</Section>
<Section withBackground>
<Padded top size="25px">
<Box errorMessage={get(requestError, 'errorMessage', null)}>
<form onSubmit={onSubmit}>
<InputWrapper>
<Input
autoFocus
error={formErrors[`${inputsPrefix}firstname`]}
label="Auth.form.firstname.label"
name={`${inputsPrefix}firstname`}
onChange={onChange}
placeholder="Auth.form.firstname.placeholder"
type="text"
validations={{ required: true }}
value={get(modifiedData, `${inputsPrefix}firstname`, '')}
/>
<Input
error={formErrors[`${inputsPrefix}lastname`]}
label="Auth.form.lastname.label"
name={`${inputsPrefix}lastname`}
onChange={onChange}
placeholder="Auth.form.lastname.placeholder"
type="text"
validations={{ required: true }}
value={get(modifiedData, `${inputsPrefix}lastname`, '')}
/>
</InputWrapper>
<Input
error={formErrors[`${inputsPrefix}email`]}
disabled={fieldsToDisable.includes('email')}
label="Auth.form.email.label"
name={`${inputsPrefix}email`}
onChange={onChange}
placeholder="Auth.form.email.placeholder"
type="email"
validations={{ required: true }}
value={get(modifiedData, `${inputsPrefix}email`, '')}
/>
<Input
error={formErrors[`${inputsPrefix}password`]}
label="Auth.form.password.label"
name={`${inputsPrefix}password`}
onChange={onChange}
type="password"
validations={{ required: true }}
value={get(modifiedData, `${inputsPrefix}password`, '')}
/>
<Input
error={formErrors[`${inputsPrefix}confirmPassword`]}
label="Auth.form.confirmPassword.label"
name={`${inputsPrefix}confirmPassword`}
onChange={onChange}
type="password"
validations={{ required: true }}
value={get(modifiedData, `${inputsPrefix}confirmPassword`, '')}
/>
<Flex alignItems="flex-start">
<Checkbox
name={`${inputsPrefix}news`}
onChange={onChange}
value={get(modifiedData, `${inputsPrefix}news`, false)}
/>
<Padded left size="sm" />
<CustomLabel
id="Auth.form.register.news.label"
values={{ terms, policy }}
onClick={() => {
onChange({
target: {
name: `${inputsPrefix}news`,
value: !get(modifiedData, `${inputsPrefix}news`, false),
},
});
}}
/>
</Flex>
<Padded top size="md">
<Button type="submit" color="primary" textTransform="uppercase">
{formatMessage({ id: 'Auth.form.button.register' })}
</Button>
</Padded>
</form>
<UnauthenticatedLayout>
<LayoutContent>
<Formik
enableReinitialize
initialValues={{
// TODO: Fix the initial data during the clean of the AuthLogin codebase
firstname: modifiedData?.userInfo?.firstname || '',
lastname: modifiedData?.userInfo?.lastname || '',
email: modifiedData?.userInfo?.email || '',
password: modifiedData?.userInfo?.password || '',
confirmPassword: modifiedData?.userInfo?.confirmPassword || '',
news: false,
}}
onSubmit={onSubmit}
validationSchema={schema}
validateOnChange={false}
>
{({ values, errors, handleChange }) => (
<Form noValidate>
<Main labelledBy="welcome">
<Column>
<Logo />
<Box paddingTop="6" paddingBottom="1">
<H1 id="welcome">{formatMessage({ id: 'Auth.form.welcome.title' })}</H1>
</Box>
<CenteredBox paddingBottom="7">
<Subtitle textColor="neutral600">
{formatMessage({ id: 'Auth.form.register.subtitle' })}
</Subtitle>
</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 }) : undefined
}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.firstname.label' })}
/>
</GridItem>
<GridItem col={6}>
<TextInput
name="lastname"
error={errors.lastname ? formatMessage({ id: errors.lastname }) : undefined}
required
value={values.lastname}
onChange={handleChange}
label={formatMessage({ id: 'Auth.form.lastname.label' })}
/>
</GridItem>
</Grid>
<TextInput
name="email"
disabled={fieldsToDisable.includes('email')}
value={values.email}
onChange={handleChange}
error={errors.email ? formatMessage({ id: errors.email }) : undefined}
required
label={formatMessage({ id: 'Auth.form.email.label' })}
type="email"
/>
<TextInput
name="password"
onChange={handleChange}
value={values.password}
error={errors.password ? formatMessage({ id: errors.password }) : undefined}
endAction={
<FieldActionWrapper
onClick={e => {
e.preventDefault();
setPasswordShown(prev => !prev);
}}
label={formatMessage({
id: passwordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
>
{passwordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
required
label={formatMessage({ id: 'Auth.form.password.label' })}
type={passwordShown ? 'text' : 'password'}
/>
<TextInput
name="confirmPassword"
onChange={handleChange}
value={values.confirmPassword}
error={
errors.confirmPassword
? formatMessage({ id: errors.confirmPassword })
: undefined
}
endAction={
<FieldActionWrapper
onClick={e => {
e.preventDefault();
setConfirmPasswordShown(prev => !prev);
}}
label={formatMessage({
id: confirmPasswordShown
? 'Auth.form.password.show-password'
: 'Auth.form.password.hide-password',
})}
>
{confirmPasswordShown ? <Show /> : <Hide />}
</FieldActionWrapper>
}
required
label={formatMessage({ id: 'Auth.form.confirmPassword.label' })}
type={confirmPasswordShown ? 'text' : 'password'}
/>
<Checkbox
onValueChange={checked => {
handleChange({ target: { value: checked, name: 'news' } });
}}
value={values.news}
name="news"
>
{formatMessage(
{ id: 'Auth.form.register.news.label' },
{
terms: (
<A target="_blank" href="https://strapi.io/terms" rel="noreferrer">
{formatMessage({ id: 'Auth.privacy-policy-agreement.terms' })}
</A>
),
policy: (
<A target="_blank" href="https://strapi.io/privacy" rel="noreferrer">
{formatMessage({ id: 'Auth.privacy-policy-agreement.policy' })}
</A>
),
}
)}
</Checkbox>
<AuthButton size="L" type="submit">
{formatMessage({ id: 'Auth.form.button.register' })}
</AuthButton>
</Stack>
</Main>
</Form>
)}
</Formik>
{!noSignin && (
<Box paddingTop={4}>
<Row justifyContent="center">
<Link label="Auth.link.signin" to="/auth/login">
{formatMessage({ id: 'Auth.link.signin.account' })}
</Link>
</Row>
</Box>
</Padded>
</Section>
{!noSignin && (
<AuthLink label="Auth.link.signin" to="/auth/login">
<Text fontSize="md">
{formatMessage({ id: 'Auth.link.signin.account' })}
&nbsp;
<Text fontSize="md" color="#0097f7" as="span">
{formatMessage({ id: 'Auth.link.signin' })}
</Text>
</Text>
</AuthLink>
)}
</>
)}
</LayoutContent>
</UnauthenticatedLayout>
);
};
Register.defaultProps = {
fieldsToDisable: [],
inputsPrefix: '',
noSignin: false,
onSubmit: e => e.preventDefault(),
requestError: null,
};
Register.propTypes = {
fieldsToDisable: PropTypes.array,
formErrors: PropTypes.object.isRequired,
inputsPrefix: PropTypes.string,
modifiedData: PropTypes.object.isRequired,
noSignin: PropTypes.bool,
onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
requestError: PropTypes.object,
schema: PropTypes.shape({ type: PropTypes.string.isRequired }).isRequired,
};
export default Register;

View File

@ -100,7 +100,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
}
if (authType === 'register' || authType === 'register-admin') {
await registerRequest(body, requestURL);
await registerRequest(body, requestURL, { setSubmitting, setErrors });
}
if (authType === 'forgot-password') {
@ -173,7 +173,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
}
};
const registerRequest = async (body, requestURL) => {
const registerRequest = async (body, requestURL, { setSubmitting, setErrors }) => {
try {
const {
data: {
@ -214,7 +214,11 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
type: 'SET_ERRORS',
errors: apiErrors,
});
setErrors({ apiErrors });
}
} finally {
setSubmitting(false);
}
};

View File

@ -58,21 +58,19 @@ const forms = {
fieldsToDisable: ['email'],
fieldsToOmit: ['userInfo.confirmPassword', 'userInfo.news', 'userInfo.email'],
schema: yup.object().shape({
userInfo: yup.object().shape({
firstname: yup.string().required(translatedErrors.required),
lastname: yup.string().required(translatedErrors.required),
password: yup
.string()
.min(8, translatedErrors.minLength)
.matches(/[a-z]/, 'components.Input.error.contain.lowercase')
.matches(/[A-Z]/, 'components.Input.error.contain.uppercase')
.matches(/\d/, 'components.Input.error.contain.number')
.required(translatedErrors.required),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), null], 'components.Input.error.password.noMatch')
.required(translatedErrors.required),
}),
firstname: yup.string().required(translatedErrors.required),
lastname: yup.string().required(translatedErrors.required),
password: yup
.string()
.min(8, translatedErrors.minLength)
.matches(/[a-z]/, 'components.Input.error.contain.lowercase')
.matches(/[A-Z]/, 'components.Input.error.contain.uppercase')
.matches(/\d/, 'components.Input.error.contain.number')
.required(translatedErrors.required),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), null], 'components.Input.error.password.noMatch')
.required(translatedErrors.required),
registrationToken: yup.string().required(translatedErrors.required),
}),
inputsPrefix: 'userInfo.',
@ -93,6 +91,10 @@ const forms = {
.matches(/[A-Z]/, 'components.Input.error.contain.uppercase')
.matches(/\d/, 'components.Input.error.contain.number')
.required(translatedErrors.required),
email: yup
.string()
.email(translatedErrors.email)
.required(translatedErrors.required),
confirmPassword: yup
.string()
.oneOf([yup.ref('password'), null], 'components.Input.error.password.noMatch')

View File

@ -7,7 +7,7 @@
"Auth.form.button.login.providers.error": "We cannot connect you through the selected provider.",
"Auth.form.button.login.providers.see-more": "See more",
"Auth.form.button.login.strapi": "Log in via Strapi",
"Auth.form.button.register": "LET'S START",
"Auth.form.button.register": "Let's start",
"Auth.form.button.reset-password": "Change password",
"Auth.form.confirmPassword.label": "Confirmation Password",
"Auth.form.email.label": "Email",
@ -27,15 +27,16 @@
"Auth.form.error.ratelimit": "Too many attempts, please try again in a minute.",
"Auth.form.error.user.not-exist": "This email does not exist.",
"Auth.form.error.username.taken": "Username is already taken.",
"Auth.form.firstname.label": "First name",
"Auth.form.firstname.label": "Firstname",
"Auth.form.firstname.placeholder": "Kai",
"Auth.form.forgot-password.email.label": "Enter your email",
"Auth.form.forgot-password.email.label.success": "Email successfully sent to",
"Auth.form.lastname.label": "Last name",
"Auth.form.lastname.label": "Lastname",
"Auth.form.lastname.placeholder": "Doe",
"Auth.form.password.label": "Password",
"Auth.form.password.show-password": "Show password",
"Auth.form.password.hide-password": "Hide password",
"Auth.form.register.subtitle": "Your credentials are only used to authenticate yourself on the admin panel. All saved data will be stored in your own database.",
"Auth.form.register.news.label": "Keep me updated about the new features and upcoming improvements (by doing this you accept the {terms} and the {policy}).",
"Auth.form.rememberMe.label": "Remember me",
"Auth.form.username.label": "Username",

View File

@ -7,7 +7,6 @@ import {
Row,
Box,
TableLabel,
Button,
Main,
Subtitle,
H1,
@ -23,14 +22,11 @@ import UnauthenticatedLayout, {
} from '../../../../../../admin/src/layouts/UnauthenticatedLayout';
import SSOProviders from './SSOProviders';
import Logo from '../../../../../../admin/src/pages/AuthPage/components/Logo';
import AuthButton from '../../../../../../admin/src/pages/AuthPage/components/AuthButton';
const DividerFull = styled(Divider)`
flex: 1;
`;
const AuthButton = styled(Button)`
display: inline-block;
width: 100%;
`;
const Providers = () => {
const ssoEnabled = strapi.features.isEnabled(strapi.features.SSO);
@ -77,7 +73,7 @@ const Providers = () => {
</Box>
<DividerFull />
</Row>
<AuthButton onClick={handleClick}>
<AuthButton size="L" onClick={handleClick}>
{formatMessage({ id: 'Auth.form.button.login.strapi' })}
</AuthButton>
</Stack>

View File

@ -41,8 +41,8 @@
"@strapi/babel-plugin-switch-ee-ce": "1.0.0",
"@strapi/helper-plugin": "3.6.6",
"@strapi/utils": "3.6.6",
"@strapi/icons": "0.0.1-alpha.10",
"@strapi/parts": "0.0.1-alpha.10",
"@strapi/icons": "0.0.1-alpha.11",
"@strapi/parts": "0.0.1-alpha.11",
"axios": "^0.21.1",
"babel-loader": "8.2.2",
"babel-plugin-styled-components": "1.12.0",

View File

@ -23,6 +23,7 @@ export { default as HeaderSearch } from './components/HeaderSearch';
export { default as IcoContainer } from './components/IcoContainer';
export { default as InputAddon } from './components/InputAddon';
export { default as EmptyState } from './components/EmptyState';
export { default as Form } from './components/Form';
export * from './components/Tabs';
export * from './components/Select';

View File

@ -73,6 +73,7 @@
"babel-plugin-styled-components": "1.12.0",
"bootstrap": "^4.6.0",
"classnames": "^2.3.1",
"formik": "2.2.9",
"immutable": "^3.8.2",
"invariant": "^2.2.1",
"lodash": "4.17.21",

View File

@ -3497,15 +3497,15 @@
tslib "^2.0.0"
upath "2.0.1"
"@strapi/icons@0.0.1-alpha.10":
version "0.0.1-alpha.10"
resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.10.tgz#ec7424718abeb2d6680c91c7ba666ffbaa7f43e8"
integrity sha512-VbMDJuYl6xwG6COOF6BheXy1JIboWpyO8QqqfhJoJBLcAGVrouUeUMp+YGOsyi9nnGtPZSs8TNdtMkHkstRHqA==
"@strapi/icons@0.0.1-alpha.11":
version "0.0.1-alpha.11"
resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.11.tgz#a92b27f8f3fd6081249c07c6acc6e4aefe38bd68"
integrity sha512-BTl/2bGzCaVqhwj/vcmicL4Ya19J6NZ4A3O8MDDogoRwaDfHceLT1RpzKsID4x4kfXasopbNST1GzrJ2pvz9mw==
"@strapi/parts@0.0.1-alpha.10":
version "0.0.1-alpha.10"
resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.10.tgz#dfed27fc2fb73423af896109b2e3280c5473260a"
integrity sha512-WosaWkioUDucF5R1LC5qP76hAs2abKuL3HaiCUgvIudomItMrPRxman2LPYjGzFfFXPKu6aJwNy5CXMx6owRpw==
"@strapi/parts@0.0.1-alpha.11":
version "0.0.1-alpha.11"
resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.11.tgz#ad42d40d979cabe73df4815faf2c65629a9b497d"
integrity sha512-tyPPZm0QIPh/hSMvtei0hum2S2XM3WmFALOXNGI8rSOyGLYnRzWf+phlyUNOYYuyxwEeNIR3vU+w3n7xpRpXiQ==
dependencies:
"@internationalized/number" "^3.0.2"
compute-scroll-into-view "^1.0.17"
@ -9605,7 +9605,7 @@ formidable@^1.1.1, formidable@^1.2.2:
resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.2.2.tgz#bf69aea2972982675f00865342b982986f6b8dd9"
integrity sha512-V8gLm+41I/8kguQ4/o1D3RIHRmhYFG4pnNyonvua+40rqcEmT4+V71yaZ3B457xbbgCsCfjSPi65u/W6vK1U5Q==
formik@^2.2.6:
formik@2.2.9, formik@^2.2.6:
version "2.2.9"
resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0"
integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA==