From f7bd078bcce2d6cad419fceb55ab71415cda4d33 Mon Sep 17 00:00:00 2001 From: Tulio Casagrande Date: Tue, 21 Feb 2017 13:51:41 -0300 Subject: [#907] Display error message for mobile version with @anikarni --- web-ui/app/images/dead-mail.svg | 30 +++++++++++++++++ web-ui/app/locales/en_US/translation.json | 6 +++- web-ui/app/locales/pt_BR/translation.json | 6 +++- web-ui/src/interstitial/interstitial.js | 4 ++- web-ui/src/login/app.js | 14 +++++--- web-ui/src/login/app.scss | 2 +- web-ui/src/login/error/auth-error.scss | 21 ++++++++++++ web-ui/src/login/error/auth_error.js | 31 ++++++++++++++++++ web-ui/src/login/error/generic-error.scss | 38 ++++++++++++++++++++++ web-ui/src/login/error/generic_error.js | 37 +++++++++++++++++++++ web-ui/src/login/login.js | 2 +- web-ui/src/util.js | 7 ++-- web-ui/test/unit/login/app.spec.js | 20 ++++++++---- web-ui/test/unit/login/error/auth_error.spec.js | 17 ++++++++++ web-ui/test/unit/login/error/generic_error.spec.js | 17 ++++++++++ web-ui/test/unit/util.spec.js | 4 +-- 16 files changed, 234 insertions(+), 22 deletions(-) create mode 100644 web-ui/app/images/dead-mail.svg create mode 100644 web-ui/src/login/error/auth-error.scss create mode 100644 web-ui/src/login/error/auth_error.js create mode 100644 web-ui/src/login/error/generic-error.scss create mode 100644 web-ui/src/login/error/generic_error.js create mode 100644 web-ui/test/unit/login/error/auth_error.spec.js create mode 100644 web-ui/test/unit/login/error/generic_error.spec.js diff --git a/web-ui/app/images/dead-mail.svg b/web-ui/app/images/dead-mail.svg new file mode 100644 index 00000000..0dc673e2 --- /dev/null +++ b/web-ui/app/images/dead-mail.svg @@ -0,0 +1,30 @@ + + + Created with Sketch. + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/web-ui/app/locales/en_US/translation.json b/web-ui/app/locales/en_US/translation.json index 7aad4fe8..f42a612d 100644 --- a/web-ui/app/locales/en_US/translation.json +++ b/web-ui/app/locales/en_US/translation.json @@ -65,7 +65,11 @@ "timeout": "A timeout occurred", "general": "Problems talking to server", "parse": "Got invalid response from server", - "auth": "Invalid email or password" + "auth": "Invalid email or password", + "login": { + "title": "Oh, something went wrong :(", + "message": "Try to login again in a few minutes. If the problem persists, contact your account administrator." + } }, "tags": { "inbox": "Inbox", diff --git a/web-ui/app/locales/pt_BR/translation.json b/web-ui/app/locales/pt_BR/translation.json index b38b7535..336784be 100644 --- a/web-ui/app/locales/pt_BR/translation.json +++ b/web-ui/app/locales/pt_BR/translation.json @@ -65,7 +65,11 @@ "timeout": "A operação excedeu o limite de tempo", "general": "Problemas ao se comunicar com o servidor", "parse": "Obteve uma resposta inválida do servidor", - "auth": "E-mail ou senha inválidos" + "auth": "E-mail ou senha inválidos", + "login": { + "title": "Ops, algo deu errado :(", + "message": "Tente entrar novamente em alguns minutos. Se o problema persistir, contate o administrador da sua conta." + } }, "tags": { "inbox": "Caixa de Entrada", diff --git a/web-ui/src/interstitial/interstitial.js b/web-ui/src/interstitial/interstitial.js index 78a17190..c9a29c21 100644 --- a/web-ui/src/interstitial/interstitial.js +++ b/web-ui/src/interstitial/interstitial.js @@ -44,8 +44,10 @@ $(function () { method: 'GET', url: '/status' }).success(function (data) { - if (data.status === 'completed' || data.status === 'error') { + if (data.status === 'completed') { window.location="/"; + } else if (data.status === 'error') { + window.location="/login?error"; } }); }, 2000); diff --git a/web-ui/src/login/app.js b/web-ui/src/login/app.js index 07099c60..54f5fd66 100644 --- a/web-ui/src/login/app.js +++ b/web-ui/src/login/app.js @@ -19,18 +19,21 @@ import React from 'react'; import { translate } from 'react-i18next'; import InputField from 'src/common/input_field/input_field'; import SubmitButton from 'src/common/submit_button/submit_button'; +import AuthError from 'src/login/error/auth_error'; +import GenericError from 'src/login/error/generic_error'; import './app.scss'; -const errorMessage = (t, authError) => { - if (authError) return

{t('error.auth')}

; +const errorMessage = (t, authError, error) => { + if (authError) return ; + else if (error) return ; return
; }; -export const App = ({ t, authError }) => ( +export const App = ({ t, authError, error }) => (
Pixelated logo - {errorMessage(t, authError)} + {errorMessage(t, authError, error)}
@@ -41,7 +44,8 @@ export const App = ({ t, authError }) => ( App.propTypes = { t: React.PropTypes.func.isRequired, - authError: React.PropTypes.bool + authError: React.PropTypes.bool, + error: React.PropTypes.bool }; export default translate('', { wait: true })(App); diff --git a/web-ui/src/login/app.scss b/web-ui/src/login/app.scss index f6e6bc11..61b618d8 100644 --- a/web-ui/src/login/app.scss +++ b/web-ui/src/login/app.scss @@ -37,7 +37,6 @@ } #login_form { - padding: 20px 0; width: 70%; .input-field-group { @@ -51,6 +50,7 @@ .logo { width: 70%; + margin-bottom: 1em; } @media only screen and (min-width : 500px) { diff --git a/web-ui/src/login/error/auth-error.scss b/web-ui/src/login/error/auth-error.scss new file mode 100644 index 00000000..31210ae7 --- /dev/null +++ b/web-ui/src/login/error/auth-error.scss @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2017 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +.auth-error { + color: #D72A25; + margin: 10px 0 0 0; +} diff --git a/web-ui/src/login/error/auth_error.js b/web-ui/src/login/error/auth_error.js new file mode 100644 index 00000000..ceeaf42b --- /dev/null +++ b/web-ui/src/login/error/auth_error.js @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2017 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +import React from 'react'; +import { translate } from 'react-i18next'; + +import './auth-error.scss'; + +export const AuthError = ({ t }) => ( +

{t('error.auth')}

+); + +AuthError.propTypes = { + t: React.PropTypes.func.isRequired +}; + +export default translate('', { wait: true })(AuthError); diff --git a/web-ui/src/login/error/generic-error.scss b/web-ui/src/login/error/generic-error.scss new file mode 100644 index 00000000..a44de793 --- /dev/null +++ b/web-ui/src/login/error/generic-error.scss @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2017 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +@import "~scss/base/colors"; + +.generic-error { + font-size: 0.9em; + color: $medium_grey; + display: flex; + padding: 0 1.5em; + font-weight: 300; + + h2 { + font-weight: normal; + font-size: 1.4em; + } +} + +.dead-mail { + width: 20%; + margin-right: 1.5em; + margin-top: 1.7em; + align-self: flex-start; +} diff --git a/web-ui/src/login/error/generic_error.js b/web-ui/src/login/error/generic_error.js new file mode 100644 index 00000000..023e1bcf --- /dev/null +++ b/web-ui/src/login/error/generic_error.js @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2017 ThoughtWorks, Inc. + * + * Pixelated is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Pixelated is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with Pixelated. If not, see . + */ + +import React from 'react'; +import { translate } from 'react-i18next'; + +import './generic-error.scss'; + +export const GenericError = ({ t }) => ( +
+ +
+

{t('error.login.title')}

+

{t('error.login.message')}

+
+
+); + +GenericError.propTypes = { + t: React.PropTypes.func.isRequired +}; + +export default translate('', { wait: true })(GenericError); diff --git a/web-ui/src/login/login.js b/web-ui/src/login/login.js index 74e5a14e..b1d895ae 100644 --- a/web-ui/src/login/login.js +++ b/web-ui/src/login/login.js @@ -28,7 +28,7 @@ if (process.env.NODE_ENV === 'development') a11y(React); render( - + , document.getElementById('root') ); diff --git a/web-ui/src/util.js b/web-ui/src/util.js index 1b244458..bde8ce0a 100644 --- a/web-ui/src/util.js +++ b/web-ui/src/util.js @@ -1,6 +1,7 @@ -export const hasQueryParameter = param => ( - decodeURIComponent(window.location.search.substring(1)).includes(param) -); +export const hasQueryParameter = (param) => { + const decodedUri = decodeURIComponent(window.location.search.substring(1)).split('&'); + return decodedUri.includes(param); +}; export default { hasQueryParameter diff --git a/web-ui/test/unit/login/app.spec.js b/web-ui/test/unit/login/app.spec.js index 347e2b19..c1d63235 100644 --- a/web-ui/test/unit/login/app.spec.js +++ b/web-ui/test/unit/login/app.spec.js @@ -2,25 +2,31 @@ import { shallow } from 'enzyme'; import expect from 'expect'; import React from 'react'; import { App } from 'src/login/app'; +import AuthError from 'src/login/error/auth_error'; +import GenericError from 'src/login/error/generic_error'; describe('App', () => { let app; const mockTranslations = key => key; - beforeEach(() => { - app = shallow(); - }); - it('renders login form', () => { + app = shallow(); expect(app.find('form').props().action).toEqual('/login'); }); it('renders auth error message', () => { app = shallow(); - expect(app.find('.error').length).toEqual(1); + expect(app.find(AuthError).length).toEqual(1); }); - it('does not render auth error message', () => { - expect(app.find('.error').length).toEqual(0); + it('renders generic error message', () => { + app = shallow(); + expect(app.find(GenericError).length).toEqual(1); + }); + + it('does not render error message', () => { + app = shallow(); + expect(app.find(AuthError).length).toEqual(0); + expect(app.find(GenericError).length).toEqual(0); }); }); diff --git a/web-ui/test/unit/login/error/auth_error.spec.js b/web-ui/test/unit/login/error/auth_error.spec.js new file mode 100644 index 00000000..55d8920f --- /dev/null +++ b/web-ui/test/unit/login/error/auth_error.spec.js @@ -0,0 +1,17 @@ +import { shallow } from 'enzyme'; +import expect from 'expect'; +import React from 'react'; +import { AuthError } from 'src/login/error/auth_error'; + +describe('AuthError', () => { + let authError; + const mockTranslations = key => key; + + beforeEach(() => { + authError = shallow(); + }); + + it('renders error message', () => { + expect(authError.find('.auth-error').length).toEqual(1); + }); +}); diff --git a/web-ui/test/unit/login/error/generic_error.spec.js b/web-ui/test/unit/login/error/generic_error.spec.js new file mode 100644 index 00000000..1ef8349d --- /dev/null +++ b/web-ui/test/unit/login/error/generic_error.spec.js @@ -0,0 +1,17 @@ +import { shallow } from 'enzyme'; +import expect from 'expect'; +import React from 'react'; +import { GenericError } from 'src/login/error/generic_error'; + +describe('GenericError', () => { + let genericError; + const mockTranslations = key => key; + + beforeEach(() => { + genericError = shallow(); + }); + + it('renders error message', () => { + expect(genericError.find('.generic-error').length).toEqual(1); + }); +}); diff --git a/web-ui/test/unit/util.spec.js b/web-ui/test/unit/util.spec.js index 84decf6f..968b2d83 100644 --- a/web-ui/test/unit/util.spec.js +++ b/web-ui/test/unit/util.spec.js @@ -5,12 +5,12 @@ describe('Utils', () => { describe('.hasQueryParameter', () => { global.window = { location: { - search: '?auth&lng=pt-BR' + search: '?auth-error&lng=pt-BR' } }; it('checks if param included in query parameters', () => { - expect(Util.hasQueryParameter('auth')).toBe(true); + expect(Util.hasQueryParameter('auth-error')).toBe(true); }); it('checks if param not included in query parameters', () => { -- cgit v1.2.3