diff --git a/client/coral-admin/src/actions/auth.js b/client/coral-admin/src/actions/auth.js index 6762bd70a..389e8b4d2 100644 --- a/client/coral-admin/src/actions/auth.js +++ b/client/coral-admin/src/actions/auth.js @@ -1,5 +1,29 @@ import * as actions from '../constants/auth'; -import coralApi from '../../../coral-framework/helpers/response'; +import coralApi from 'coral-framework/helpers/response'; + +// Log In. +export const handleLogin = (email, password) => dispatch => { + dispatch({type: actions.LOGIN_REQUEST}); + return coralApi('/auth/local', {method: 'POST', body: {email, password}}) + .then(result => { + const isAdmin = !!result.user.roles.filter(i => i === 'ADMIN').length; + dispatch(checkLoginSuccess(result.user, isAdmin)); + }) + .catch(error => { + dispatch({type: actions.LOGIN_FAILURE, message: error.translation_key}); + }); +}; + +const forgotPassowordRequest = () => ({type: actions.FETCH_FORGOT_PASSWORD_REQUEST}); +const forgotPassowordSuccess = () => ({type: actions.FETCH_FORGOT_PASSWORD_SUCCESS}); +const forgotPassowordFailure = () => ({type: actions.FETCH_FORGOT_PASSWORD_FAILURE}); + +export const requestPasswordReset = email => dispatch => { + dispatch(forgotPassowordRequest(email)); + return coralApi('/account/password/reset', {method: 'POST', body: {email}}) + .then(() => dispatch(forgotPassowordSuccess())) + .catch(error => dispatch(forgotPassowordFailure(error))); +}; // Check Login diff --git a/client/coral-admin/src/components/AdminLogin.js b/client/coral-admin/src/components/AdminLogin.js new file mode 100644 index 000000000..7c7d4b00f --- /dev/null +++ b/client/coral-admin/src/components/AdminLogin.js @@ -0,0 +1,92 @@ +import React, {PropTypes} from 'react'; +import Layout from 'coral-admin/src/components/ui/Layout'; +import styles from './NotFound.css'; +import {Button, TextField, Alert, Success} from 'coral-ui'; +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from '../translations'; +const lang = new I18n(translations); + +class AdminLogin extends React.Component { + + constructor (props) { + super(props); + this.state = {email: '', password: '', requestPassword: false}; + } + + handleSignIn = e => { + e.preventDefault(); + this.props.handleLogin(this.state.email, this.state.password); + } + + handleRequestPassword = e => { + e.preventDefault(); + this.props.requestPasswordReset(this.state.email); + } + + render () { + const {errorMessage} = this.props; + const signInForm = ( +
+ {errorMessage && {lang.t(`errors.${errorMessage}`)}} + this.setState({email: e.target.value})} /> + this.setState({password: e.target.value})} + type='password' /> +
+ +

+ Forgot your password? { + e.preventDefault(); + this.setState({requestPassword: true}); + }}>Request a new one. +

+ + ); + const requestPasswordForm = ( + this.props.passwordRequestSuccess + ?

{ + location.href = location.href; + }}> + {this.props.passwordRequestSuccess} Sign in + +

+ :
+ this.setState({email: e.target.value})} /> + + + ); + return ( + +
+

Team sign in

+

Sign in to interact with your community.

+ { this.state.requestPassword ? requestPasswordForm : signInForm } +
+
+ ); + } +} + +AdminLogin.propTypes = { + handleLogin: PropTypes.func.isRequired, + passwordRequestSuccess: PropTypes.string, + loginError: PropTypes.string +}; + +export default AdminLogin; diff --git a/client/coral-admin/src/components/NotFound.css b/client/coral-admin/src/components/NotFound.css index fc9aedc6b..9977f858a 100644 --- a/client/coral-admin/src/components/NotFound.css +++ b/client/coral-admin/src/components/NotFound.css @@ -1,12 +1,37 @@ .layout { - max-width: 800px; - margin: 0 auto; + max-width: 800px; + margin: 0 auto; +} + +.loginLayout { + max-width: 400px; + margin: 0 auto; +} + +.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess { + text-align: center; + font-size: 16px; +} + +.forgotPasswordLink, .signInLink { + color: blue; + font-weight: normal; + text-decoration: none; +} + +.forgotPasswordLink:hover, .signInLink:hover { + text-decoration: underline; } .layout h1 { - font-size: 40px; + font-size: 40px; } -.layout img { - width: 100%; +.loginHeader { + font-size: 30px; +} + +.passwordRequestSuccess { + cursor: pointer; + padding: 8px 14px; } diff --git a/client/coral-admin/src/components/PermissionRequired.js b/client/coral-admin/src/components/PermissionRequired.js deleted file mode 100644 index c61ab1cef..000000000 --- a/client/coral-admin/src/components/PermissionRequired.js +++ /dev/null @@ -1,13 +0,0 @@ -import React from 'react'; -import {Layout} from 'react-mdl'; -import styles from './NotFound.css'; - -export const PermissionRequired = () => ( - -
-

Permission Required

-

We’re sorry, but you don’t have access to that page.

- Communicorn -
-
-); diff --git a/client/coral-admin/src/components/ui/Drawer.js b/client/coral-admin/src/components/ui/Drawer.js index 8fdf4f19d..9af651f3b 100644 --- a/client/coral-admin/src/components/ui/Drawer.js +++ b/client/coral-admin/src/components/ui/Drawer.js @@ -1,11 +1,11 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import {Navigation, Drawer} from 'react-mdl'; import {IndexLink, Link} from 'react-router'; import styles from './Drawer.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../../translations.json'; -export default ({handleLogout, restricted = false}) => ( +const CoralDrawer = ({handleLogout, restricted = false}) => ( { !restricted ?
@@ -45,5 +45,11 @@ export default ({handleLogout, restricted = false}) => ( ); +CoralDrawer.propTypes = { + handleLogout: PropTypes.func.isRequired, + restricted: PropTypes.bool // hide app elements from a logged out user +}; + const lang = new I18n(translations); +export default CoralDrawer; diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index 556b33b6c..4a2314926 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import {Navigation, Header, IconButton, MenuItem, Menu} from 'react-mdl'; import {Link, IndexLink} from 'react-router'; import styles from './Header.css'; @@ -6,7 +6,7 @@ import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../../translations.json'; import {Logo} from './Logo'; -export default ({handleLogout, restricted = false}) => ( +const CoralHeader = ({handleLogout, restricted = false}) => (
{ @@ -64,4 +64,11 @@ export default ({handleLogout, restricted = false}) => (
); +CoralHeader.propTypes = { + handleLogout: PropTypes.func.isRequired, + restricted: PropTypes.bool // hide elemnts from a user that's logged out +}; + const lang = new I18n(translations); + +export default CoralHeader; diff --git a/client/coral-admin/src/components/ui/Layout.js b/client/coral-admin/src/components/ui/Layout.js index f28e8dd80..c8b5bf57a 100644 --- a/client/coral-admin/src/components/ui/Layout.js +++ b/client/coral-admin/src/components/ui/Layout.js @@ -1,15 +1,22 @@ -import React from 'react'; +import React, {PropTypes} from 'react'; import {Layout as LayoutMDL} from 'react-mdl'; import Header from './Header'; import Drawer from './Drawer'; import styles from './Layout.css'; -export const Layout = ({children, ...props}) => ( +const Layout = ({children, handleLogout = () => {}, restricted = false, ...props}) => ( -
- -
+
+ +
{children}
); + +Layout.propTypes = { + handleLogout: PropTypes.func, + restricted: PropTypes.bool // hide elements from a user that's logged out +}; + +export default Layout; diff --git a/client/coral-admin/src/constants/auth.js b/client/coral-admin/src/constants/auth.js index f77deb670..3c47bfc45 100644 --- a/client/coral-admin/src/constants/auth.js +++ b/client/coral-admin/src/constants/auth.js @@ -7,3 +7,11 @@ export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN'; export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'; export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'; export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'; + +export const LOGIN_REQUEST = 'LOGIN_REQUEST'; +export const LOGIN_SUCCESS = 'LOGIN_SUCCESS'; +export const LOGIN_FAILURE = 'LOGIN_FAILURE'; + +export const FETCH_FORGOT_PASSWORD_REQUEST = 'FETCH_FORGOT_PASSWORD_REQUEST'; +export const FETCH_FORGOT_PASSWORD_SUCCESS = 'FETCH_FORGOT_PASSWORD_SUCCESS'; +export const FETCH_FORGOT_PASSWORD_FAILURE = 'FETCH_FORGOT_PASSWORD_FAILURE'; diff --git a/client/coral-admin/src/containers/Install/InstallContainer.js b/client/coral-admin/src/containers/Install/InstallContainer.js index b70d3b8c9..75c41387a 100644 --- a/client/coral-admin/src/containers/Install/InstallContainer.js +++ b/client/coral-admin/src/containers/Install/InstallContainer.js @@ -2,7 +2,7 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; import styles from './style.css'; import {Wizard, WizardNav} from 'coral-ui'; -import {Layout} from '../../components/ui/Layout'; +import Layout from 'coral-admin/src/components/ui/Layout'; import { goToStep, diff --git a/client/coral-admin/src/containers/LayoutContainer.js b/client/coral-admin/src/containers/LayoutContainer.js index 5ce208503..d122cd23a 100644 --- a/client/coral-admin/src/containers/LayoutContainer.js +++ b/client/coral-admin/src/containers/LayoutContainer.js @@ -1,24 +1,33 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; -import {Layout} from '../components/ui/Layout'; -import {checkLogin, logout} from '../actions/auth'; +import Layout from '../components/ui/Layout'; +import {checkLogin, handleLogin, logout, requestPasswordReset} from '../actions/auth'; import {FullLoading} from '../components/FullLoading'; -import {PermissionRequired} from '../components/PermissionRequired'; +import AdminLogin from '../components/AdminLogin'; class LayoutContainer extends Component { componentWillMount () { const {checkLogin} = this.props; - checkLogin().then(() => { - if (!this.props.auth.isAdmin) { - location.href = '/admin/login'; - } - }); + checkLogin(); } render () { - const {isAdmin, loggedIn, loadingUser} = this.props.auth; + const { + isAdmin, + loggedIn, + loadingUser, + loginError, + passwordRequestSuccess + } = this.props.auth; + const {handleLogout} = this.props; if (loadingUser) { return ; } - if (!isAdmin) { return ; } - if (isAdmin && loggedIn) { return ; } + if (!isAdmin) { + return ; + } + if (isAdmin && loggedIn) { return ; } return ; } } @@ -29,6 +38,8 @@ const mapStateToProps = state => ({ const mapDispatchToProps = dispatch => ({ checkLogin: () => dispatch(checkLogin()), + handleLogin: (username, password) => dispatch(handleLogin(username, password)), + requestPasswordReset: email => dispatch(requestPasswordReset(email)), handleLogout: () => dispatch(logout()) }); diff --git a/client/coral-admin/src/reducers/auth.js b/client/coral-admin/src/reducers/auth.js index 095ef7aac..b52efdb85 100644 --- a/client/coral-admin/src/reducers/auth.js +++ b/client/coral-admin/src/reducers/auth.js @@ -4,7 +4,9 @@ import * as actions from '../constants/auth'; const initialState = Map({ loggedIn: false, user: null, - isAdmin: false + isAdmin: false, + loginError: null, + passwordRequestSuccess: null }); export default function auth (state = initialState, action) { @@ -25,6 +27,14 @@ export default function auth (state = initialState, action) { .set('user', action.user); case actions.LOGOUT_SUCCESS: return initialState; + case actions.LOGIN_REQUEST: + return state.set('loginError', null); + case actions.LOGIN_FAILURE: + return state.set('loginError', action.message); + case actions.FETCH_FORGOT_PASSWORD_REQUEST: + return state.set('passwordRequestSuccess', null); + case actions.FETCH_FORGOT_PASSWORD_SUCCESS: + return state.set('passwordRequestSuccess', 'If you have a registered account, a password reset link was sent to that email.'); default : return state; } diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index 92e0cbbff..96e727567 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -1,5 +1,8 @@ { "en": { + "errors": { + "NOT_AUTHORIZED": "Your username or password is not recognized by our system." + }, "community": { "username_and_email": "Username and Email", "account_creation_date": "Account Creation Date", @@ -134,6 +137,9 @@ } }, "es": { + "errors": { + "NOT_AUTHORIZED": "Acción no autorizada." + }, "community": { "username_and_email": "Usuario y E-mail", "account_creation_date": "Fecha de creación de la cuenta", @@ -215,7 +221,7 @@ }, "bandialog": { "ban_user": "Quieres suspender el Usuario?", - "are_you_sure": "Estas segura que quieres suspender a {props.author.username}?", + "are_you_sure": "Estas segura que quieres suspender a {0}?", "note": "Nota: Suspender este usuario también va a colocar este comentario en la cola de Rechazados.", "cancel": "Cancelar", "yes_ban_user": "Si, Suspendan el usuario" diff --git a/client/coral-framework/graphql/mutations/index.js b/client/coral-framework/graphql/mutations/index.js index 67af2df4a..4c2920e54 100644 --- a/client/coral-framework/graphql/mutations/index.js +++ b/client/coral-framework/graphql/mutations/index.js @@ -40,7 +40,7 @@ export const postComment = graphql(POST_COMMENT, { updateQueries: { AssetQuery: (oldData, {mutationResult:{data:{createComment:{comment}}}}) => { - if (oldData.asset.moderation === 'PRE') { + if (oldData.asset.settings.moderation === 'PRE') { return oldData; } diff --git a/client/coral-sign-in/components/CreateUsernameDialog.js b/client/coral-sign-in/components/CreateUsernameDialog.js index d0e348046..4a7a99b81 100644 --- a/client/coral-sign-in/components/CreateUsernameDialog.js +++ b/client/coral-sign-in/components/CreateUsernameDialog.js @@ -1,8 +1,7 @@ import React from 'react'; import TextField from 'coral-ui/components/TextField'; -import Alert from './Alert'; import Button from 'coral-ui/components/Button'; -import {Dialog} from 'coral-ui'; +import {Dialog, Alert} from 'coral-ui'; import FakeComment from './FakeComment'; import styles from './styles.css'; diff --git a/client/coral-sign-in/components/SignInContent.js b/client/coral-sign-in/components/SignInContent.js index 1f339979b..a0ed5b5b4 100644 --- a/client/coral-sign-in/components/SignInContent.js +++ b/client/coral-sign-in/components/SignInContent.js @@ -1,6 +1,5 @@ import React, {PropTypes} from 'react'; -import Alert from './Alert'; -import {Button, TextField, Spinner, Success} from 'coral-ui'; +import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; import styles from './styles.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; diff --git a/client/coral-sign-in/components/SignUpContent.js b/client/coral-sign-in/components/SignUpContent.js index 216349412..d127cc2c7 100644 --- a/client/coral-sign-in/components/SignUpContent.js +++ b/client/coral-sign-in/components/SignUpContent.js @@ -1,6 +1,5 @@ import React, {PropTypes} from 'react'; -import Alert from './Alert'; -import {Button, TextField, Spinner, Success} from 'coral-ui'; +import {Button, TextField, Spinner, Success, Alert} from 'coral-ui'; import styles from './styles.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../translations'; diff --git a/client/coral-sign-in/components/styles.css b/client/coral-sign-in/components/styles.css index 31d4a4fed..d3cd47b87 100644 --- a/client/coral-sign-in/components/styles.css +++ b/client/coral-sign-in/components/styles.css @@ -65,23 +65,6 @@ input.error{ padding: 3px 0 16px; } -.alert { - padding: 10px; - margin-bottom: 20px; - border-radius: 2px; -} - -.alert--success { - border: solid 1px #1ec00e; - background: #cbf1b8; - color: #006900; -} - -.alert--error { - background: #FFEBEE; - color: #B71C1C; -} - .userBox { padding: 10px 0 20px; letter-spacing: 0.1px; diff --git a/client/coral-ui/components/Alert.css b/client/coral-ui/components/Alert.css new file mode 100644 index 000000000..47b4787a1 --- /dev/null +++ b/client/coral-ui/components/Alert.css @@ -0,0 +1,16 @@ +.alert { + padding: 10px; + margin-bottom: 20px; + border-radius: 2px; +} + +.alert--success { + border: solid 1px #1ec00e; + background: #cbf1b8; + color: #006900; +} + +.alert--error { + background: #FFEBEE; + color: #B71C1C; +} diff --git a/client/coral-sign-in/components/Alert.js b/client/coral-ui/components/Alert.js similarity index 87% rename from client/coral-sign-in/components/Alert.js rename to client/coral-ui/components/Alert.js index 019c3456a..02f0524ff 100644 --- a/client/coral-sign-in/components/Alert.js +++ b/client/coral-ui/components/Alert.js @@ -1,5 +1,5 @@ import React from 'react'; -import styles from './styles.css'; +import styles from './Alert.css'; const Alert = ({cStyle = 'error', children, className, ...props}) => (
{ res.render('password-reset', {redirectUri: process.env.TALK_ROOT_URL}); }); -router.get('/login', (req, res, next) => { - res.render('admin/login'); -}); - router.get('*', (req, res) => { res.render('admin', {basePath: '/client/coral-admin'}); }); diff --git a/views/admin/login.ejs b/views/admin/login.ejs deleted file mode 100644 index e88dd86fd..000000000 --- a/views/admin/login.ejs +++ /dev/null @@ -1,132 +0,0 @@ - - - - - - Admin Login - - - - - - -
-
- Admin Login - - - - -
-
-
- - - -