diff --git a/packages/core/admin/admin/src/StrapiApp.js b/packages/core/admin/admin/src/StrapiApp.js
index a21d0596d5..7cddb854ef 100644
--- a/packages/core/admin/admin/src/StrapiApp.js
+++ b/packages/core/admin/admin/src/StrapiApp.js
@@ -11,7 +11,7 @@ import { basename, createHook } from './core/utils';
import configureStore from './core/store/configureStore';
import { Plugin } from './core/apis';
import App from './pages/App';
-import AuthLogo from './assets/images/logo_strapi_auth.png';
+import AuthLogo from './assets/images/logo_strapi_auth_v4.png';
import MenuLogo from './assets/images/strapi-img.png';
import Providers from './components/Providers';
import Theme from './components/Theme';
diff --git a/packages/core/admin/admin/src/assets/images/logo_strapi_auth_v4.png b/packages/core/admin/admin/src/assets/images/logo_strapi_auth_v4.png
new file mode 100644
index 0000000000..ff23ded756
Binary files /dev/null and b/packages/core/admin/admin/src/assets/images/logo_strapi_auth_v4.png differ
diff --git a/packages/core/admin/admin/src/components/LeftMenu/index.js b/packages/core/admin/admin/src/components/LeftMenu/index.js
index c4a2170a6e..f96797d1ea 100644
--- a/packages/core/admin/admin/src/components/LeftMenu/index.js
+++ b/packages/core/admin/admin/src/components/LeftMenu/index.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types';
import { FormattedMessage } from 'react-intl';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import get from 'lodash/get';
+import { useHistory } from 'react-router-dom';
import {
MainNav,
NavBrand,
@@ -12,6 +13,7 @@ import {
NavUser,
NavCondense,
Divider,
+ Button,
} from '@strapi/parts';
import ContentIcon from '@strapi/icons/ContentIcon';
import { auth, usePersistentState } from '@strapi/helper-plugin';
@@ -19,6 +21,7 @@ import useConfigurations from '../../hooks/useConfigurations';
const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
const { menuLogo } = useConfigurations();
+ const { push } = useHistory();
const [condensed, setCondensed] = usePersistentState('navbar-condensed', false);
const userInfo = auth.getUserInfo();
@@ -63,6 +66,16 @@ const LeftMenu = ({ generalSectionLinks, pluginsSectionLinks }) => {
))}
+ {/* This is temporary */}
+
) : null}
diff --git a/packages/core/admin/admin/src/components/LocaleToggle/index.js b/packages/core/admin/admin/src/components/LocaleToggle/index.js
index 49c4505ac3..23cfe81c7a 100644
--- a/packages/core/admin/admin/src/components/LocaleToggle/index.js
+++ b/packages/core/admin/admin/src/components/LocaleToggle/index.js
@@ -4,42 +4,22 @@
*
*/
-import React, { useState } from 'react';
+import React from 'react';
import { useIntl } from 'react-intl';
-import { ButtonDropdown, DropdownItem, DropdownMenu, DropdownToggle } from 'reactstrap';
+import { SimpleMenu, MenuItem } from '@strapi/parts';
import useLocalesProvider from '../LocalesProvider/useLocalesProvider';
-import Wrapper from './Wrapper';
const LocaleToggle = () => {
const { changeLocale, localeNames } = useLocalesProvider();
-
- const [isOpen, setIsOpen] = useState(false);
- const toggle = () => setIsOpen(prev => !prev);
const { locale } = useIntl();
return (
-
-
-
- {localeNames[locale]}
-
-
-
- {Object.keys(localeNames).map(lang => {
- return (
- changeLocale(lang)}
- className={`localeToggleItem ${locale === lang ? 'localeToggleItemActive' : ''}`}
- >
- {localeNames[lang]}
-
- );
- })}
-
-
-
+
+ {Object.keys(localeNames).map(lang => (
+
+ ))}
+
);
};
-export default LocaleToggle;
+export default LocaleToggle;
\ No newline at end of file
diff --git a/packages/core/admin/admin/src/components/LocaleToggle/tests/index.test.js b/packages/core/admin/admin/src/components/LocaleToggle/tests/index.test.js
deleted file mode 100644
index 1fe55951ca..0000000000
--- a/packages/core/admin/admin/src/components/LocaleToggle/tests/index.test.js
+++ /dev/null
@@ -1,217 +0,0 @@
-import React from 'react';
-import { render } from '@testing-library/react';
-import LanguageProvider from '../../LanguageProvider';
-import en from '../../../translations/en.json';
-import LocaleToggle from '../index';
-
-const messages = { en };
-const localeNames = { en: 'English' };
-
-describe('', () => {
- it('should not crash', () => {
- const App = (
-
-
-
- );
-
- const { container } = render(App);
- expect(container.firstChild).toMatchInlineSnapshot(`
- .c0 {
- -webkit-font-smoothing: antialiased;
- }
-
- .c0 > div {
- height: 6rem;
- line-height: 5.8rem;
- z-index: 999;
- }
-
- .c0 > div > button {
- width: 100%;
- padding: 0 30px;
- background: transparent;
- border: none;
- border-radius: 0;
- color: #333740;
- font-weight: 500;
- text-align: right;
- cursor: pointer;
- -webkit-transition: background 0.2s ease-out;
- transition: background 0.2s ease-out;
- }
-
- .c0 > div > button:hover,
- .c0 > div > button:focus,
- .c0 > div > button:active {
- color: #333740;
- background-color: #fafafb !important;
- }
-
- .c0 > div > button > i,
- .c0 > div > button > svg {
- margin-left: 10px;
- -webkit-transition: -webkit-transform 0.3s ease-out;
- -webkit-transition: transform 0.3s ease-out;
- transition: transform 0.3s ease-out;
- }
-
- .c0 > div > button > i[alt='true'],
- .c0 > div > button > svg[alt='true'] {
- -webkit-transform: rotateX(180deg);
- -ms-transform: rotateX(180deg);
- transform: rotateX(180deg);
- }
-
- .c0 .localeDropdownContent {
- -webkit-font-smoothing: antialiased;
- }
-
- .c0 .localeDropdownContent span {
- color: #333740;
- font-size: 13px;
- font-family: Lato;
- font-weight: 500;
- -webkit-letter-spacing: 0.5;
- -moz-letter-spacing: 0.5;
- -ms-letter-spacing: 0.5;
- letter-spacing: 0.5;
- vertical-align: baseline;
- }
-
- .c0 .localeDropdownMenu {
- min-width: 90px !important;
- max-height: 162px !important;
- overflow: auto !important;
- margin: 0 !important;
- padding: 0;
- line-height: 1.8rem;
- border: none !important;
- border-top-left-radius: 0 !important;
- border-top-right-radius: 0 !important;
- box-shadow: 0 1px 4px 0px rgba(40,42,49,0.05);
- }
-
- .c0 .localeDropdownMenu:before {
- content: '';
- position: absolute;
- top: -3px;
- left: -1px;
- width: calc(100% + 1px);
- height: 3px;
- box-shadow: 0 1px 2px 0 rgba(40,42,49,0.16);
- }
-
- .c0 .localeDropdownMenu > button {
- height: 40px;
- padding: 0px 15px;
- line-height: 40px;
- color: #f75b1d;
- font-size: 13px;
- font-weight: 500;
- -webkit-letter-spacing: 0.5;
- -moz-letter-spacing: 0.5;
- -ms-letter-spacing: 0.5;
- letter-spacing: 0.5;
- }
-
- .c0 .localeDropdownMenu > button:hover,
- .c0 .localeDropdownMenu > button:focus,
- .c0 .localeDropdownMenu > button:active {
- background-color: #fafafb !important;
- border-radius: 0px;
- cursor: pointer;
- }
-
- .c0 .localeDropdownMenu > button:first-child {
- line-height: 50px;
- margin-bottom: 4px;
- }
-
- .c0 .localeDropdownMenu > button:first-child:hover,
- .c0 .localeDropdownMenu > button:first-child:active {
- color: #333740;
- }
-
- .c0 .localeDropdownMenu > button:not(:first-child) {
- height: 36px;
- line-height: 36px;
- }
-
- .c0 .localeDropdownMenu > button:not(:first-child) > i,
- .c0 .localeDropdownMenu > button:not(:first-child) > svg {
- margin-left: 10px;
- }
-
- .c0 .localeDropdownMenuNotLogged {
- background: transparent !important;
- box-shadow: none !important;
- border: 1px solid #e3e9f3 !important;
- border-top: 0px !important;
- }
-
- .c0 .localeDropdownMenuNotLogged button {
- padding-left: 17px;
- }
-
- .c0 .localeDropdownMenuNotLogged button:hover {
- background-color: #f7f8f8 !important;
- }
-
- .c0 .localeDropdownMenuNotLogged:before {
- box-shadow: none !important;
- }
-
- .c0 .localeToggleItem img {
- max-height: 13.37px;
- margin-left: 9px;
- }
-
- .c0 .localeToggleItem:active {
- color: black;
- }
-
- .c0 .localeToggleItem:hover {
- background-color: #fafafb !important;
- }
-
- .c0 .localeToggleItemActive {
- color: #333740 !important;
- }
-
-
-
-
-
-
-
- `);
- });
-});
diff --git a/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js b/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js
new file mode 100644
index 0000000000..6d99c91460
--- /dev/null
+++ b/packages/core/admin/admin/src/layouts/UnauthenticatedLayout.js
@@ -0,0 +1,43 @@
+import React from 'react';
+import styled from 'styled-components';
+import PropTypes from 'prop-types';
+import { Box, Row } from '@strapi/parts';
+import LocaleToggle from '../components/LocaleToggle';
+
+const Wrapper = styled(Box)`
+ margin: 0 auto;
+ width: 552px;
+`;
+
+export const Column = styled(Row)`
+ flex-direction: column;
+`;
+
+const UnauthenticatedLayout = ({ children }) => {
+ return (
+
+
+
+
+
+
+
+
+ {children}
+
+
+
+ );
+};
+
+UnauthenticatedLayout.propTypes = {
+ children: PropTypes.node.isRequired,
+};
+
+export default UnauthenticatedLayout;
diff --git a/packages/core/admin/admin/src/pages/App/index.js b/packages/core/admin/admin/src/pages/App/index.js
index d1216617c5..1ffa600906 100644
--- a/packages/core/admin/admin/src/pages/App/index.js
+++ b/packages/core/admin/admin/src/pages/App/index.js
@@ -13,6 +13,8 @@ import {
useNotification,
TrackingContext,
} from '@strapi/helper-plugin';
+import { SkipToContent } from '@strapi/parts';
+import { useIntl } from 'react-intl';
import PrivateRoute from '../../components/PrivateRoute';
import { createRoute, makeUniqueRoutes } from '../../utils';
import AuthPage from '../AuthPage';
@@ -26,6 +28,7 @@ const AuthenticatedApp = lazy(() =>
function App() {
const toggleNotification = useNotification();
+ const { formatMessage } = useIntl();
const [{ isLoading, hasAdmin, uuid }, setState] = useState({ isLoading: true, hasAdmin: false });
const authRoutes = useMemo(() => {
@@ -105,6 +108,7 @@ function App() {
return (
}>
+ {formatMessage({ id: 'skipToContent' })}
{authRoutes}
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js b/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
index cf45d1cf1f..6e65046efb 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Login/BaseLogin.js
@@ -1,84 +1,139 @@
-import React from 'react';
-import { Checkbox } from '@buffetjs/core';
-import { useIntl } from 'react-intl';
-import { get } from 'lodash';
+import React, { useState } from 'react';
+import { Show, Hide } from '@strapi/icons';
+import {
+ Box,
+ Stack,
+ H1,
+ Text,
+ Subtitle,
+ Button,
+ Checkbox,
+ TextInput,
+ Main,
+ FieldAction,
+} from '@strapi/parts';
import PropTypes from 'prop-types';
-import { BaselineAlignment } from '@strapi/helper-plugin';
+import styled from 'styled-components';
+import { useIntl } from 'react-intl';
+import { Formik } from 'formik';
-import Button from '../../../../components/FullWidthButton';
-import AuthLink from '../AuthLink';
-import Box from '../Box';
-import Input from '../Input';
+import { Column } from '../../../../layouts/UnauthenticatedLayout';
+import Form from './Form';
import Logo from '../Logo';
-import Section from '../Section';
-const Login = ({ children, formErrors, modifiedData, onChange, onSubmit, requestError }) => {
+const AuthButton = styled(Button)`
+ display: inline-block;
+ width: 100%;
+`;
+const FieldActionWrapper = styled(FieldAction)`
+ svg {
+ height: 16px;
+ width: 16px;
+ path {
+ fill: ${({ theme }) => theme.colors.neutral600};
+ }
+ }
+`;
+
+const Login = ({ onSubmit, schema }) => {
+ const [passwordShown, setPasswordShown] = useState(false);
const { formatMessage } = useIntl();
return (
- <>
-
-
-
- >
+ >
+ {formatMessage({ id: 'Auth.form.rememberMe.label' })}
+
+
+ {formatMessage({ id: 'Auth.form.button.login' })}
+
+
+
+ )}
+
+
);
};
Login.defaultProps = {
- children: null,
- onSubmit: e => e.preventDefault(),
- requestError: null,
+ onSubmit: () => {},
};
Login.propTypes = {
- children: PropTypes.node,
- formErrors: PropTypes.object.isRequired,
- modifiedData: PropTypes.object.isRequired,
- onChange: PropTypes.func.isRequired,
onSubmit: PropTypes.func,
- requestError: PropTypes.object,
+ schema: PropTypes.shape({
+ type: PropTypes.string.isRequired,
+ }).isRequired,
};
export default Login;
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js b/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js
new file mode 100644
index 0000000000..ee273000cb
--- /dev/null
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Login/Form.js
@@ -0,0 +1,43 @@
+import React, { useEffect } from 'react';
+import { Form, useFormikContext, getIn } from 'formik';
+
+const FormWithFocus = props => {
+ const { isSubmitting, isValidating, errors, touched } = useFormikContext();
+
+ useEffect(() => {
+ if (isSubmitting && !isValidating) {
+ const errorNames = Object.keys(touched).reduce((prev, key) => {
+ if (getIn(errors, key)) {
+ prev.push(key);
+ }
+
+ return prev;
+ }, []);
+
+ if (errorNames.length) {
+ let errorEl;
+
+ errorNames.forEach(errorKey => {
+ const selector = `[name="${errorKey}"]`;
+
+ if (!errorEl) {
+ errorEl = document.querySelector(selector);
+ }
+ });
+
+ errorEl.focus();
+ }
+ }
+ if (!isSubmitting && !isValidating && Object.keys(errors).length) {
+ const el = document.getElementById('global-form-error');
+
+ if (el) {
+ el.focus();
+ }
+ }
+ }, [errors, isSubmitting, isValidating, touched]);
+
+ return ;
+};
+
+export default FormWithFocus;
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Login/index.js b/packages/core/admin/admin/src/pages/AuthPage/components/Login/index.js
index 7ca153d3de..2bfc7100c3 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Login/index.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Login/index.js
@@ -1,10 +1,14 @@
import React from 'react';
import PropTypes from 'prop-types';
-
import BaseLogin from './BaseLogin';
+import UnauthenticatedLayout from '../../../../layouts/UnauthenticatedLayout';
const Login = loginProps => {
- return ;
+ return (
+
+
+
+ );
};
Login.defaultProps = {
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Logo/Img.js b/packages/core/admin/admin/src/pages/AuthPage/components/Logo/Img.js
deleted file mode 100644
index a12578ffcc..0000000000
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Logo/Img.js
+++ /dev/null
@@ -1,7 +0,0 @@
-import styled from 'styled-components';
-
-const Img = styled.img`
- height: 40px;
-`;
-
-export default Img;
diff --git a/packages/core/admin/admin/src/pages/AuthPage/components/Logo/index.js b/packages/core/admin/admin/src/pages/AuthPage/components/Logo/index.js
index 359f707cdc..ca2f2edb37 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/components/Logo/index.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/components/Logo/index.js
@@ -1,11 +1,15 @@
import React from 'react';
+import styled from 'styled-components';
import { useConfigurations } from '../../../../hooks';
-import Img from './Img';
+
+const Img = styled.img`
+ height: 72px;
+`;
const Logo = () => {
const { authLogo } = useConfigurations();
- return
;
+ return
;
};
export default Logo;
diff --git a/packages/core/admin/admin/src/pages/AuthPage/index.js b/packages/core/admin/admin/src/pages/AuthPage/index.js
index b249afcf4e..d368e1d48f 100644
--- a/packages/core/admin/admin/src/pages/AuthPage/index.js
+++ b/packages/core/admin/admin/src/pages/AuthPage/index.js
@@ -1,16 +1,11 @@
import React, { useEffect, useReducer } from 'react';
import axios from 'axios';
-import { camelCase, get, omit, upperFirst } from 'lodash';
+import { camelCase, get, omit } from 'lodash';
import { Redirect, useRouteMatch, useHistory } from 'react-router-dom';
-import { BaselineAlignment, auth, useNotification, useQuery } from '@strapi/helper-plugin';
-import { Padded } from '@buffetjs/core';
+import { auth, useNotification, useQuery } from '@strapi/helper-plugin';
import PropTypes from 'prop-types';
import forms from 'ee_else_ce/pages/AuthPage/utils/forms';
import useLocalesProvider from '../../components/LocalesProvider/useLocalesProvider';
-import NavTopRightWrapper from '../../components/NavTopRightWrapper';
-import PageTitle from '../../components/PageTitle';
-import LocaleToggle from '../../components/LocaleToggle';
-import checkFormValidity from '../../utils/checkFormValidity';
import formatAPIErrors from '../../utils/formatAPIErrors';
import init from './init';
import { initialState, reducer } from './reducer';
@@ -95,40 +90,25 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
});
};
- const handleSubmit = async e => {
- e.preventDefault();
+ const handleSubmit = async (e, { setSubmitting, setErrors }) => {
+ setSubmitting(true);
+ const body = omit(e, fieldsToOmit);
+ const requestURL = `/admin/${endPoint}`;
- dispatch({
- type: 'SET_ERRORS',
- errors: {},
- });
+ if (authType === 'login') {
+ await loginRequest(body, requestURL, { setSubmitting, setErrors });
+ }
- const errors = await checkFormValidity(modifiedData, schema);
+ if (authType === 'register' || authType === 'register-admin') {
+ await registerRequest(body, requestURL);
+ }
- dispatch({
- type: 'SET_ERRORS',
- errors: errors || {},
- });
+ if (authType === 'forgot-password') {
+ await forgotPasswordRequest(body, requestURL);
+ }
- if (!errors) {
- const body = omit(modifiedData, fieldsToOmit);
- const requestURL = `/admin/${endPoint}`;
-
- if (authType === 'login') {
- await loginRequest(body, requestURL);
- }
-
- if (authType === 'register' || authType === 'register-admin') {
- await registerRequest(body, requestURL);
- }
-
- if (authType === 'forgot-password') {
- await forgotPasswordRequest(body, requestURL);
- }
-
- if (authType === 'reset-password') {
- await resetPasswordRequest(body, requestURL);
- }
+ if (authType === 'reset-password') {
+ await resetPasswordRequest(body, requestURL);
}
};
@@ -152,7 +132,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
}
};
- const loginRequest = async (body, requestURL) => {
+ const loginRequest = async (body, requestURL, { setSubmitting, setErrors }) => {
try {
const {
data: {
@@ -175,8 +155,8 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
push('/');
} catch (err) {
if (err.response) {
+ setSubmitting(false);
const errorMessage = get(err, ['response', 'data', 'message'], 'Something went wrong');
- const errorStatus = get(err, ['response', 'data', 'statusCode'], 400);
if (camelCase(errorMessage).toLowerCase() === 'usernotactive') {
push('/auth/oops');
@@ -188,11 +168,7 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
return;
}
- dispatch({
- type: 'SET_REQUEST_ERROR',
- errorMessage,
- errorStatus,
- });
+ setErrors({ errorMessage });
}
}
};
@@ -288,24 +264,17 @@ const AuthPage = ({ hasAdmin, setHasAdmin }) => {
}
return (
-
-
-
-
-
-
-
-
-
+
);
};
diff --git a/packages/core/admin/admin/src/translations/en.json b/packages/core/admin/admin/src/translations/en.json
index 23ba3901e7..030477126d 100644
--- a/packages/core/admin/admin/src/translations/en.json
+++ b/packages/core/admin/admin/src/translations/en.json
@@ -3,7 +3,7 @@
"Auth.components.Oops.text": "Your account has been suspended",
"Auth.form.button.forgot-password": "Send Email",
"Auth.form.button.go-home": "GO BACK HOME",
- "Auth.form.button.login": "Log in",
+ "Auth.form.button.login": "Login",
"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",
@@ -34,14 +34,19 @@
"Auth.form.lastname.label": "Last name",
"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.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",
"Auth.form.username.placeholder": "Kai Doe",
+ "Auth.form.welcome.subtitle": "Log in to your Strapi account",
+ "Auth.form.welcome.title": "Welcome back!",
"Auth.link.forgot-password": "Forgot your password?",
"Auth.link.ready": "Ready to sign in?",
"Auth.link.signin": "Sign in",
"Auth.link.signin.account": "Already have an account?",
+ "Auth.login.sso.divider": "Or login with",
"Auth.privacy-policy-agreement.policy": "privacy policy",
"Auth.privacy-policy-agreement.terms": "terms",
"Content Manager": "Content Manager",
@@ -316,7 +321,7 @@
"components.Input.error.contentTypeName.taken": "This name already exists",
"components.Input.error.custom-error": "{errorMessage} ",
"components.Input.error.password.noMatch": "Passwords do not match",
- "components.Input.error.validation.email": "This is not an email",
+ "components.Input.error.validation.email": "This is an invalid email",
"components.Input.error.validation.json": "This doesn't match the JSON format",
"components.Input.error.validation.max": "The value is too high.",
"components.Input.error.validation.maxLength": "The value is too long.",
diff --git a/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js b/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
index cc9ce8020e..fefca630df 100644
--- a/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
+++ b/packages/core/admin/ee/admin/pages/AuthPage/components/Login/index.js
@@ -1,73 +1,48 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { useTheme } from 'styled-components';
-import { Link } from 'react-router-dom';
+import styled from 'styled-components';
+import { Stack, Row, Divider, TableLabel, Box } from '@strapi/parts';
import { useIntl } from 'react-intl';
-import { Flex, Padded, Separator } from '@buffetjs/core';
-import { LoadingIndicator, Tooltip } from '@buffetjs/styles';
-import { Dots } from '@buffetjs/icons';
-import { BaselineAlignment } from '@strapi/helper-plugin';
import BaseLogin from '../../../../../../admin/src/pages/AuthPage/components/Login/BaseLogin';
-import ProviderButton from '../../../../components/ProviderButton';
-import {
- ProviderButtonWrapper,
- ProviderLink,
-} from '../../../../components/ProviderButton/ProviderButtonStyles';
import { useAuthProviders } from '../../../../hooks';
+import UnauthenticatedLayout from '../../../../../../admin/src/layouts/UnauthenticatedLayout';
+import SSOProviders from '../Providers/SSOProviders';
+
+const DividerFull = styled(Divider)`
+ flex: 1;
+`;
const Login = loginProps => {
const ssoEnabled = strapi.features.isEnabled(strapi.features.SSO);
-
- const theme = useTheme();
const { isLoading, data: providers } = useAuthProviders({ ssoEnabled });
const { formatMessage } = useIntl();
if (!ssoEnabled || (!isLoading && providers.length === 0)) {
- return ;
+ return (
+
+
+
+ );
}
return (
-
-
-
-
-
- {isLoading ? (
-
- ) : (
-
- {providers.slice(0, 2).map((provider, index) => (
-
-
-
- ))}
- {providers.length > 2 && (
-
-
-
-
-
-
-
-
- )}
-
- )}
-
-
+
+
+
+
+
+
+
+
+ {formatMessage({ id: 'Auth.login.sso.divider' })}
+
+
+
+
+
+
+
+
);
};
diff --git a/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js
new file mode 100644
index 0000000000..4c9620694e
--- /dev/null
+++ b/packages/core/admin/ee/admin/pages/AuthPage/components/Providers/SSOProviders.js
@@ -0,0 +1,113 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Text, Row, Grid, GridItem, Tooltip } from '@strapi/parts';
+import styled from 'styled-components';
+import { useIntl } from 'react-intl';
+import { Link } from 'react-router-dom';
+
+const SSOButton = styled.a`
+ width: ${136 / 16}rem;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: ${48 / 16}rem;
+ border: 1px solid ${({ theme }) => theme.colors.neutral150};
+ border-radius: ${({ theme }) => theme.borderRadius};
+ text-decoration: inherit;
+ &:link {
+ text-decoration: none;
+ }
+ color: ${({ theme }) => theme.colors.neutral600};
+`;
+
+const SSOProvidersWrapper = styled(Row)`
+ & a:not(:first-child):not(:last-child) {
+ margin: 0 ${({ theme }) => theme.spaces[2]};
+ }
+ & a:first-child {
+ margin-right: ${({ theme }) => theme.spaces[2]};
+ }
+ & a:last-child {
+ margin-left: ${({ theme }) => theme.spaces[2]};
+ }
+`;
+
+const SSOProviderButton = ({ provider }) => {
+ return (
+
+
+ {provider.icon ? (
+
+ ) : (
+ {provider.displayName}
+ )}
+
+
+ );
+};
+
+SSOProviderButton.propTypes = {
+ provider: PropTypes.shape({
+ icon: PropTypes.string,
+ displayName: PropTypes.string.isRequired,
+ uid: PropTypes.string.isRequired,
+ }).isRequired,
+};
+
+const SSOProviders = ({ providers, displayAllProviders }) => {
+ const { formatMessage } = useIntl();
+
+ if (displayAllProviders) {
+ return (
+
+ {providers.map(provider => (
+
+
+
+ ))}
+
+ );
+ }
+
+ if (providers.length > 2 && !displayAllProviders) {
+ return (
+
+ {providers.slice(0, 2).map(provider => (
+
+
+
+ ))}
+
+
+
+ •••
+
+
+
+
+ );
+ }
+
+ return (
+
+ {providers.map(provider => (
+
+ ))}
+
+ );
+};
+
+SSOProviders.defaultProps = {
+ displayAllProviders: true,
+};
+
+SSOProviders.propTypes = {
+ providers: PropTypes.arrayOf(PropTypes.object).isRequired,
+ displayAllProviders: PropTypes.bool,
+};
+
+export default SSOProviders;
diff --git a/packages/core/admin/index.html b/packages/core/admin/index.html
index ff86160ebd..b6d254f0e7 100644
--- a/packages/core/admin/index.html
+++ b/packages/core/admin/index.html
@@ -1,16 +1,16 @@
-
+
-
-
-
-
-
-
- Strapi Admin
-
-
-
-
-
-
+
+
+
+
+
+
+ Strapi Admin
+
+
+
+
+
+
diff --git a/packages/core/admin/package.json b/packages/core/admin/package.json
index 03cdc38911..c560c8c6a9 100644
--- a/packages/core/admin/package.json
+++ b/packages/core/admin/package.json
@@ -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.5",
- "@strapi/parts": "0.0.1-alpha.5",
+ "@strapi/icons": "0.0.1-alpha.6",
+ "@strapi/parts": "0.0.1-alpha.6",
"axios": "^0.21.1",
"babel-loader": "8.2.2",
"babel-plugin-styled-components": "1.12.0",
diff --git a/packages/core/helper-plugin/lib/src/testUtils/commonTrads.json b/packages/core/helper-plugin/lib/src/testUtils/commonTrads.json
index 36eb78d19e..300edca348 100644
--- a/packages/core/helper-plugin/lib/src/testUtils/commonTrads.json
+++ b/packages/core/helper-plugin/lib/src/testUtils/commonTrads.json
@@ -109,7 +109,7 @@
"components.Input.error.attribute.taken": "This field name already exists",
"components.Input.error.contentTypeName.taken": "This name already exists",
"components.Input.error.custom-error": "{errorMessage} ",
- "components.Input.error.validation.email": "This is not an email",
+ "components.Input.error.validation.email": "This is an invalid email",
"components.Input.error.validation.json": "This doesn't match the JSON format",
"components.Input.error.validation.max": "The value is too high.",
"components.Input.error.validation.maxLength": "The value is too long.",
diff --git a/test/config/front/testUtils/commonTrads.json b/test/config/front/testUtils/commonTrads.json
index e4aa37751b..0569a5827e 100644
--- a/test/config/front/testUtils/commonTrads.json
+++ b/test/config/front/testUtils/commonTrads.json
@@ -109,7 +109,7 @@
"components.Input.error.attribute.taken": "This field name already exists",
"components.Input.error.contentTypeName.taken": "This name already exists",
"components.Input.error.custom-error": "{errorMessage} ",
- "components.Input.error.validation.email": "This is not an email",
+ "components.Input.error.validation.email": "This is an invalid email",
"components.Input.error.validation.json": "This doesn't match the JSON format",
"components.Input.error.validation.max": "The value is too high.",
"components.Input.error.validation.maxLength": "The value is too long.",
diff --git a/yarn.lock b/yarn.lock
index d85e671d6d..b158bdf6dc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3490,15 +3490,15 @@
tslib "^2.0.0"
upath "2.0.1"
-"@strapi/icons@0.0.1-alpha.5":
- version "0.0.1-alpha.5"
- resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.5.tgz#04f2d62a516e8da6b14f0b1bacc040b10c5f44db"
- integrity sha512-PCTQXIkBxfV/qWQZPrbABEdkLSYO7w4+RWVHA7eNlSkcKPP/b5Q/ZEW+56GZ/o9DLexivsGdZaC/aVUzK7G7IA==
+"@strapi/icons@0.0.1-alpha.6":
+ version "0.0.1-alpha.6"
+ resolved "https://registry.yarnpkg.com/@strapi/icons/-/icons-0.0.1-alpha.6.tgz#d714c4f0f44d5a53b813989f0c890af62278ce1b"
+ integrity sha512-QB5ghVyTh+vWlFAbDMmZGS//0+mZLWnB0ejxZfHjvT/d3ByVDycxsugLpk+jwALmD7pPdcqK66+3vR9o97Pl1g==
-"@strapi/parts@0.0.1-alpha.5":
- version "0.0.1-alpha.5"
- resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.5.tgz#6abddbcf4ee58da506065aa49f736b0b51e9cbfa"
- integrity sha512-Vb55oaD0G9N9OzsVM4SB3WG30w4Mo3oDITq9lCvwQT9uR0xKuN+GwYK1XdlBTItLcjnJOpRJ3vSoI7sgI1L8rQ==
+"@strapi/parts@0.0.1-alpha.6":
+ version "0.0.1-alpha.6"
+ resolved "https://registry.yarnpkg.com/@strapi/parts/-/parts-0.0.1-alpha.6.tgz#7259e26edb7b4195352713a73f23d54dfefc79c8"
+ integrity sha512-4RhcguoPf41tJ6TbrHrUffOMEmWIJGgT9dxgqy5aFLZFHXnO1x1463Sw5hVSqdT+D05SiJLcBCO3zY+NhpEsDQ==
dependencies:
compute-scroll-into-view "^1.0.17"
prop-types "^15.7.2"