Merge branch 'master' into story-134624635

This commit is contained in:
gaba
2017-02-01 10:42:00 -08:00
33 changed files with 573 additions and 233 deletions
@@ -32,6 +32,10 @@ const updateModeration = (updateSettings, mod) => () => {
updateSettings({moderation});
};
const updateEmailConfirmation = (updateSettings, verify) => () => {
updateSettings({requireEmailConfirmation: !verify});
};
const updateInfoBoxEnable = (updateSettings, infoBox) => () => {
const infoBoxEnable = !infoBox;
updateSettings({infoBoxEnable});
@@ -67,106 +71,123 @@ const CommentSettings = ({fetchingSettings, title, updateSettings, settingsError
return <Card shadow="4"><Spinner/>Loading settings...</Card>;
}
// just putting this here for shorthand below
const on = styles.enabledSetting;
const off = styles.disabledSetting;
return (
<div className={styles.commentSettingsSection}>
<h3>{title}</h3>
<Card className={`${styles.configSetting} ${settings.moderation === 'PRE' ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateModeration(updateSettings, settings.moderation)}
checked={settings.moderation === 'PRE'} />
</div>
<div className={styles.content}>
<Card className={`${styles.configSetting} ${settings.moderation === 'PRE' ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateModeration(updateSettings, settings.moderation)}
checked={settings.moderation === 'PRE'} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.enable-pre-moderation')}</div>
<p className={settings.moderation === 'PRE' ? '' : styles.disabledSettingText}>
{lang.t('configure.enable-pre-moderation-text')}
</p>
</div>
</Card>
<Card className={`${styles.configSetting} ${settings.charCountEnable ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateCharCountEnable(updateSettings, settings.charCountEnable)}
checked={settings.charCountEnable} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.comment-count-header')}</div>
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
<span>{lang.t('configure.comment-count-text-pre')}</span>
<input type='text'
className={`${styles.charCountTexfield} ${settings.charCountEnable && styles.charCountTexfieldEnabled}`}
htmlFor='charCount'
onChange={updateCharCount(updateSettings, settingsError)}
value={settings.charCount}/>
<span>{lang.t('configure.comment-count-text-post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{lang.t('configure.comment-count-error')}
</span>
}
</p>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? styles.enabledSetting : styles.disabledSetting}`}>
<div className={styles.action}>
<Checkbox
onChange={updateInfoBoxEnable(updateSettings, settings.infoBoxEnable)}
checked={settings.infoBoxEnable} />
</div>
<div className={styles.content}>
{lang.t('configure.include-comment-stream')}
<p>
{lang.t('configure.include-comment-stream-desc')}
</p>
<div className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
<div className={styles.content}>
<Textfield
onChange={updateInfoBoxContent(updateSettings)}
value={settings.infoBoxContent}
label={lang.t('configure.include-text')}
rows={3}/>
</div>
</Card>
<Card className={`${styles.configSetting} ${settings.requireEmailConfirmation ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateEmailConfirmation(updateSettings, settings.requireEmailConfirmation)}
checked={settings.requireEmailConfirmation} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.require-email-verification')}</div>
<p className={settings.requireEmailConfirmation ? '' : styles.disabledSettingText}>
{lang.t('configure.require-email-verification-text')}
</p>
</div>
</Card>
<Card className={`${styles.configSetting} ${settings.charCountEnable ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateCharCountEnable(updateSettings, settings.charCountEnable)}
checked={settings.charCountEnable} />
</div>
<div className={styles.content}>
<div className={styles.settingsHeader}>{lang.t('configure.comment-count-header')}</div>
<p className={settings.charCountEnable ? '' : styles.disabledSettingText}>
<span>{lang.t('configure.comment-count-text-pre')}</span>
<input type='text'
className={`${styles.charCountTexfield} ${settings.charCountEnable && styles.charCountTexfieldEnabled}`}
htmlFor='charCount'
onChange={updateCharCount(updateSettings, settingsError)}
value={settings.charCount}/>
<span>{lang.t('configure.comment-count-text-post')}</span>
{
errors.charCount &&
<span className={styles.settingsError}>
<br/>
<Icon name="error_outline"/>
{lang.t('configure.comment-count-error')}
</span>
}
</p>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? on : off}`}>
<div className={styles.action}>
<Checkbox
onChange={updateInfoBoxEnable(updateSettings, settings.infoBoxEnable)}
checked={settings.infoBoxEnable} />
</div>
<div className={styles.content}>
{lang.t('configure.include-comment-stream')}
<p>
{lang.t('configure.include-comment-stream-desc')}
</p>
<div className={`${styles.configSettingInfoBox} ${settings.infoBoxEnable ? null : styles.hidden}`} >
<div className={styles.content}>
<Textfield
onChange={updateInfoBoxContent(updateSettings)}
value={settings.infoBoxContent}
label={lang.t('configure.include-text')}
rows={3}/>
</div>
</div>
</Card>
<Card className={styles.configSettingInfoBox}>
<div className={styles.content}>
{lang.t('configure.closed-comments-desc')}
<div>
<Textfield
onChange={updateClosedMessage(updateSettings)}
value={settings.closedMessage}
label={lang.t('configure.closed-comments-label')}
rows={3}/>
</div>
</div>
</Card>
<Card className={styles.configSettingInfoBox}>
<div className={styles.content}>
{lang.t('configure.closed-comments-desc')}
<div>
<Textfield
onChange={updateClosedMessage(updateSettings)}
value={settings.closedMessage}
label={lang.t('configure.closed-comments-label')}
rows={3}/>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox}`}>
<div className={styles.content}>
{lang.t('configure.close-after')}
<br />
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout)}
value={getTimeoutAmount(settings.closedTimeout)}
label={lang.t('configure.closed-comments-label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout, true)}>
<Option value={'hours'}>{lang.t('configure.hours')}</Option>
<Option value={'days'}>{lang.t('configure.days')}</Option>
<Option value={'weeks'}>{lang.t('configure.weeks')}</Option>
</SelectField>
</div>
</div>
</Card>
<Card className={`${styles.configSettingInfoBox}`}>
<div className={styles.content}>
{lang.t('configure.close-after')}
<br />
<Textfield
type='number'
pattern='[0-9]+'
style={{width: 50}}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout)}
value={getTimeoutAmount(settings.closedTimeout)}
label={lang.t('configure.closed-comments-label')} />
<div className={styles.configTimeoutSelect}>
<SelectField
label="comments closed time window"
value={getTimeoutMeasure(settings.closedTimeout)}
onChange={updateClosedTimeout(updateSettings, settings.closedTimeout, true)}>
<Option value={'hours'}>{lang.t('configure.hours')}</Option>
<Option value={'days'}>{lang.t('configure.days')}</Option>
<Option value={'weeks'}>{lang.t('configure.weeks')}</Option>
</SelectField>
</div>
</Card>
</div>
</Card>
</div>
);
};
+4
View File
@@ -50,6 +50,8 @@
"configure": {
"enable-pre-moderation": "Enable pre-moderation",
"enable-pre-moderation-text": "Moderators must approve any comment before it is published.",
"require-email-verification": "Require Email Confirmation",
"require-email-verification-text": "New Users must verify their email before commenting",
"include-comment-stream": "Include Comment Stream Description for Readers.",
"include-comment-stream-desc": "Write a message to be added to the top of your comment stream. Pose a topic, include community guidelines, etc.",
"include-text": "Include your text here.",
@@ -155,6 +157,8 @@
"configure": {
"enable-pre-moderation": "Habilitar pre-moderación",
"enable-pre-moderation-text": "Los moderadores deben aprobar cada comentario antes de que sea publicado.",
"require-email-verification": "Necesita confirmación de correo",
"require-email-verification-text": "Nuevos usuarios deben verificar sus correos antes de comentar",
"include-comment-stream": "Incluir la Descripción a un Hilo de Comentario para los y las Lectoras.",
"include-comment-stream-desc": "Escribir un mensaje que será agregado a la parte de arriba del tu hilo de comentarios. Por ejemplo, un tema, guias de comunidad, etc.",
"include-text": "Incluir tu texto aqui.",
+2 -1
View File
@@ -5,7 +5,7 @@ import isEqual from 'lodash/isEqual';
import {TabBar, Tab, TabContent, Spinner} from '../../coral-ui';
const {logout, showSignInDialog} = authActions;
const {logout, showSignInDialog, requestConfirmEmail} = authActions;
const {addNotification, clearNotification} = notificationActions;
const {fetchAssetSuccess} = assetActions;
@@ -184,6 +184,7 @@ const mapStateToProps = state => ({
});
const mapDispatchToProps = dispatch => ({
requestConfirmEmail: () => dispatch(requestConfirmEmail()),
loadAsset: (asset) => dispatch(fetchAssetSuccess(asset)),
addNotification: (type, text) => dispatch(addNotification(type, text)),
clearNotification: () => dispatch(clearNotification()),
+31 -1
View File
@@ -43,6 +43,7 @@ export const cleanState = () => ({type: actions.CLEAN_STATE});
const signInRequest = () => ({type: actions.FETCH_SIGNIN_REQUEST});
const signInSuccess = (user, isAdmin) => ({type: actions.FETCH_SIGNIN_SUCCESS, user, isAdmin});
const signInFailure = error => ({type: actions.FETCH_SIGNIN_FAILURE, error});
const emailConfirmError = () => ({type: actions.EMAIL_CONFIRM_ERROR});
export const fetchSignIn = (formData) => (dispatch) => {
dispatch(signInRequest());
@@ -52,7 +53,18 @@ export const fetchSignIn = (formData) => (dispatch) => {
dispatch(signInSuccess(user, isAdmin));
dispatch(hideSignInDialog());
})
.catch(() => dispatch(signInFailure(lang.t('error.emailPasswordError'))));
.catch(error => {
if (error.metadata) {
// the user might not have a valid email. prompt the user user re-request the confirmation email
dispatch(signInFailure(lang.t('error.emailNotVerified', error.metadata)));
dispatch(emailConfirmError());
} else {
// invalid credentials
dispatch(signInFailure(lang.t('error.emailPasswordError')));
}
});
};
// Sign In - Facebook
@@ -159,3 +171,21 @@ export const checkLogin = () => dispatch => {
dispatch(checkLoginFailure(`${error.message}`));
});
};
const confirmEmailRequest = () => ({type: actions.CONFIRM_EMAIL_REQUEST});
const confirmEmailSuccess = () => ({type: actions.CONFIRM_EMAIL_SUCCESS});
const confirmEmailFailure = () => ({type: actions.CONFIRM_EMAIL_FAILURE});
export const requestConfirmEmail = email => dispatch => {
dispatch(confirmEmailRequest());
return coralApi('/users/resend-confirm', {method: 'POST', body: {email}})
.then(() => {
dispatch(confirmEmailSuccess());
})
.catch(err => {
console.log('failed to send email confirmation', err);
// email might have already been confirmed
dispatch(confirmEmailFailure());
});
};
+4
View File
@@ -41,3 +41,7 @@ export const CHECK_LOGIN_FAILURE = 'CHECK_LOGIN_FAILURE';
export const CHECK_CSRF_TOKEN = 'CHECK_CSRF_TOKEN';
export const UPDATE_DISPLAYNAME = 'UPDATE_DISPLAYNAME';
export const EMAIL_CONFIRM_ERROR = 'EMAIL_CONFIRM_ERROR';
export const CONFIRM_EMAIL_REQUEST = 'CONFIRM_EMAIL_REQUEST';
export const CONFIRM_EMAIL_SUCCESS = 'CONFIRM_EMAIL_SUCCESS';
export const CONFIRM_EMAIL_FAILURE = 'CONFIRM_EMAIL_FAILURE';
+10 -4
View File
@@ -34,15 +34,21 @@ const buildOptions = (inputOptions = {}) => {
};
const handleResp = res => {
if (res.status === 401) {
throw new Error('Not Authorized to make this request');
} else if (res.status > 399) {
if (res.status > 399) {
return res.json().then(err => {
let message = err.message || res.status;
const error = new Error();
if (err.error && err.error.metadata && err.error.metadata.message) {
error.metadata = err.error.metadata.message;
}
if (err.error && err.error.translation_key) {
message = err.error.translation_key;
}
throw new Error(message);
error.message = message;
throw error;
});
} else if (res.status === 204) {
return res.text();
+1 -1
View File
@@ -70,7 +70,7 @@ class i18n {
};
this.timeago = (time) => {
return this.timeagoInstance.format(time);
return this.timeagoInstance.format(new Date(time));
};
}
}
+16
View File
@@ -12,6 +12,9 @@ const initialState = Map({
error: '',
passwordRequestSuccess: null,
passwordRequestFailure: null,
emailConfirmationFailure: false,
emailConfirmationLoading: false,
emailConfirmationSuccess: false,
successSignUp: false
});
@@ -34,6 +37,9 @@ export default function auth (state = initialState, action) {
error: '',
passwordRequestFailure: null,
passwordRequestSuccess: null,
emailConfirmationFailure: false,
emailConfirmationSuccess: false,
emailConfirmationLoading: false,
successSignUp: false
}));
case actions.SHOW_CREATEDISPLAYNAME_DIALOG :
@@ -120,6 +126,16 @@ export default function auth (state = initialState, action) {
case actions.UPDATE_DISPLAYNAME:
return state
.merge({'user': {'displayName': action.displayName}});
case actions.EMAIL_CONFIRM_ERROR:
return state
.set('emailConfirmationFailure', true)
.set('emailConfirmationLoading', false);
case actions.CONFIRM_EMAIL_REQUEST:
return state.set('emailConfirmationLoading', true);
case actions.CONFIRM_EMAIL_SUCCESS:
return state
.set('emailConfirmationSuccess', true)
.set('emailConfirmationLoading', false);
default :
return state;
}
+2
View File
@@ -14,6 +14,7 @@
"checkTheForm": "Invalid Form. Please, check the fields"
},
"error": {
"emailNotVerified": "Email address {0} not verified.",
"email": "Not a valid E-Mail",
"password": "Password must be at least 8 characters",
"displayName": "Display names can contain letters, numbers and _ only",
@@ -44,6 +45,7 @@
"checkTheForm": "Formulario Invalido. Por favor, verifica los campos"
},
"error": {
"emailNotVerified": "Dirección de correo electrónico {0} no verificada.",
"email": "No es un email válido",
"password": "La contraseña debe tener por lo menos 8 caracteres",
"displayName": "Los nombres pueden contener letras, números y _",
@@ -17,7 +17,12 @@ const SignDialog = ({open, view, handleClose, offset, ...props}) => (
}}>
<span className={styles.close} onClick={handleClose}>×</span>
{view === 'SIGNIN' && <SignInContent {...props} />}
{view === 'SIGNUP' && <SignUpContent {...props} />}
{
view === 'SIGNUP' && <SignUpContent
emailConfirmationLoading={props.emailConfirmationLoading}
emailConfirmationSuccess={props.emailConfirmationSuccess}
{...props} />
}
{view === 'FORGOT' && <ForgotContent {...props} />}
</Dialog>
);
@@ -1,67 +1,100 @@
import React from 'react';
import Button from 'coral-ui/components/Button';
import FormField from './FormField';
import React, {PropTypes} from 'react';
import Alert from './Alert';
import Spinner from 'coral-ui/components/Spinner';
import {Button, FormField, Spinner, Success} from 'coral-ui';
import styles from './styles.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
const lang = new I18n(translations);
const SignInContent = ({handleChange, formData, ...props}) => (
<div>
<div className={styles.header}>
<h1>
{lang.t('signIn.signIn')}
</h1>
</div>
<div className={styles.socialConnections}>
<Button cStyle="facebook" onClick={props.fetchSignInFacebook} full>
{lang.t('signIn.facebookSignIn')}
</Button>
</div>
<div className={styles.separator}>
<h1>
{lang.t('signIn.or')}
</h1>
</div>
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
<form onSubmit={props.handleSignIn}>
<FormField
id="email"
type="email"
label={lang.t('signIn.email')}
value={formData.email}
onChange={handleChange}
/>
<FormField
id="password"
type="password"
label={lang.t('signIn.password')}
value={formData.password}
onChange={handleChange}
/>
<div className={styles.action}>
{
!props.auth.isLoading ?
<Button id='coralLogInButton' type="submit" cStyle="black" className={styles.signInButton} full>
{lang.t('signIn.signIn')}
</Button>
:
<Spinner />
}
const SignInContent = ({
handleChange,
handleChangeEmail,
emailToBeResent,
handleResendConfirmation,
emailConfirmationLoading,
emailConfirmationSuccess,
formData,
...props
}) => {
return (
<div>
<div className={styles.header}>
<h1>
{props.auth.emailConfirmationFailure ? lang.t('signIn.emailConfirmCTA') : lang.t('signIn.signIn')}
</h1>
</div>
<div className={styles.socialConnections}>
<Button cStyle="facebook" onClick={props.fetchSignInFacebook} full>
{lang.t('signIn.facebookSignIn')}
</Button>
</div>
<div className={styles.separator}>
<h1>
{lang.t('signIn.or')}
</h1>
</div>
{ props.auth.error && <Alert>{props.auth.error}</Alert> }
{
props.auth.emailConfirmationFailure
? <form onSubmit={handleResendConfirmation}>
<p>{lang.t('signIn.requestNewConfirmEmail')}</p>
<FormField
id="confirm-email"
type="email"
label={lang.t('signIn.email')}
value={emailToBeResent}
onChange={handleChangeEmail} />
<Button id='resendConfirmEmail' type='submit' cStyle='black' full>Send Email</Button>
{emailConfirmationLoading && <Spinner />}
{emailConfirmationSuccess && <Success />}
</form>
: <form onSubmit={props.handleSignIn}>
<FormField
id="email"
type="email"
label={lang.t('signIn.email')}
value={formData.email}
onChange={handleChange}
/>
<FormField
id="password"
type="password"
label={lang.t('signIn.password')}
value={formData.password}
onChange={handleChange}
/>
<div className={styles.action}>
{
!props.auth.isLoading ?
<Button id='coralLogInButton' type="submit" cStyle="black" className={styles.signInButton} full>
{lang.t('signIn.signIn')}
</Button>
:
<Spinner />
}
</div>
</form>
}
<div className={styles.footer}>
<span><a onClick={() => props.changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')}</a></span>
<span>
{lang.t('signIn.needAnAccount')}
<a onClick={() => props.changeView('SIGNUP')} id='coralRegister'>
{lang.t('signIn.register')}
</a>
</span>
</div>
</form>
<div className={styles.footer}>
<span><a onClick={() => props.changeView('FORGOT')}>{lang.t('signIn.forgotYourPass')}</a></span>
<span>
{lang.t('signIn.needAnAccount')}
<a onClick={() => props.changeView('SIGNUP')} id='coralRegister'>
{lang.t('signIn.register')}
</a>
</span>
</div>
</div>
);
);
};
SignInContent.propTypes = {
emailConfirmationLoading: PropTypes.bool.isRequired,
emailConfirmationSuccess: PropTypes.bool.isRequired,
handleResendConfirmation: PropTypes.func.isRequired,
handleChangeEmail: PropTypes.func.isRequired,
emailToBeResent: PropTypes.string.isRequired
};
export default SignInContent;
@@ -1,9 +1,6 @@
import React from 'react';
import FormField from './FormField';
import Alert from './Alert';
import Button from 'coral-ui/components/Button';
import Spinner from 'coral-ui/components/Spinner';
import Success from 'coral-ui/components/Success';
import {Button, FormField, Spinner, Success} from 'coral-ui';
import styles from './styles.css';
import I18n from 'coral-framework/modules/i18n/i18n';
import translations from '../translations';
+12 -22
View File
@@ -14,28 +14,6 @@
font-size: 1.2em;
}
.formField {
margin-top: 15px;
}
.formField label {
font-size: 1.08em;
font-weight: bold;
margin-bottom: 5px;
}
.formField input {
width: 100%;
display: block;
border: none;
outline: none;
border: 1px solid rgba(0,0,0,.12);
padding: 10px 6px;
box-sizing: border-box;
border-radius: 2px;
margin: 5px auto;
}
.footer {
margin: 20px auto 10px;
text-align: center;
@@ -150,3 +128,15 @@ input.error{
background-color: 1px solid coral;
padding: 10px;
}
.emailConfirmDialog {
margin-top: 15px;
}
.confirmLabel {
display: block;
}
.confirmSubmit {
}
@@ -16,6 +16,7 @@ import {
hideSignInDialog,
fetchSignInFacebook,
fetchForgotPassword,
requestConfirmEmail,
facebookCallback,
invalidForm,
validForm,
@@ -30,6 +31,7 @@ class SignInContainer extends Component {
password: '',
confirmPassword: ''
},
emailToBeResent: '',
errors: {},
showErrors: false
};
@@ -38,9 +40,10 @@ class SignInContainer extends Component {
super(props);
this.state = this.initialState;
this.handleChange = this.handleChange.bind(this);
this.handleChangeEmail = this.handleChangeEmail.bind(this);
this.handleResendConfirmation = this.handleResendConfirmation.bind(this);
this.handleSignUp = this.handleSignUp.bind(this);
this.handleSignIn = this.handleSignIn.bind(this);
this.handleClose = this.handleClose.bind(this);
this.addError = this.addError.bind(this);
}
@@ -71,6 +74,23 @@ class SignInContainer extends Component {
});
}
handleChangeEmail(e) {
const {value} = e.target;
this.setState({emailToBeResent: value});
}
handleResendConfirmation(e) {
e.preventDefault();
this.props.requestConfirmEmail(this.state.emailToBeResent)
.then(() => {
setTimeout(() => {
// allow success UI to be shown for a second, and then close the modal
this.props.handleClose();
}, 2500);
});
}
addError(name, error) {
return this.setState(state => ({
errors: {
@@ -124,12 +144,9 @@ class SignInContainer extends Component {
this.props.fetchSignIn(this.state.formData);
}
handleClose() {
this.props.hideSignInDialog();
}
render() {
const {auth, showSignInDialog, noButton, offset} = this.props;
const {emailConfirmationLoading, emailConfirmationSuccess} = auth;
return (
<div>
{!noButton && <Button id='coralSignInButton' onClick={showSignInDialog} full>
@@ -139,6 +156,8 @@ class SignInContainer extends Component {
open={auth.showSignInDialog}
view={auth.view}
offset={offset}
emailConfirmationLoading={emailConfirmationLoading}
emailConfirmationSuccess={emailConfirmationSuccess}
{...this}
{...this.state}
{...this.props}
@@ -159,6 +178,7 @@ const mapDispatchToProps = dispatch => ({
fetchSignIn: formData => dispatch(fetchSignIn(formData)),
fetchSignInFacebook: () => dispatch(fetchSignInFacebook()),
fetchForgotPassword: formData => dispatch(fetchForgotPassword(formData)),
requestConfirmEmail: email => dispatch(requestConfirmEmail(email)),
showSignInDialog: () => dispatch(showSignInDialog()),
changeView: view => dispatch(changeView(view)),
handleClose: () => dispatch(hideSignInDialog()),
+4
View File
@@ -1,6 +1,8 @@
export default {
en: {
'signIn': {
emailConfirmCTA: 'Please verify your email address.',
requestNewConfirmEmail: 'Request another email:',
notYou: 'Not you?',
loggedInAs: 'Logged in as',
facebookSignIn: 'Sign in with Facebook',
@@ -28,6 +30,8 @@ export default {
},
es: {
'signIn': {
emailConfirmCTA: 'Por favor verifique su correo electronico.',
requestNewConfirmEmail: 'Enviar otro correo:',
notYou: 'No eres tu?',
loggedInAs: 'Entraste como',
facebookSignIn: 'Entrar con Facebook',
+48
View File
@@ -0,0 +1,48 @@
.formField {
margin-top: 15px;
}
.formField label {
font-size: 1.08em;
font-weight: bold;
margin-bottom: 5px;
}
.formField input {
width: 100%;
display: block;
border: none;
outline: none;
border: 1px solid rgba(0,0,0,.12);
padding: 10px 6px;
box-sizing: border-box;
border-radius: 2px;
margin: 5px auto;
}
input.error{
border: solid 2px #f44336;
}
.errorMsg, .hint {
color: grey;
font-weight: 600;
padding: 3px 0 16px;
}
.attention {
display: inline-block;
width: 15px;
height: 15px;
background: #B71C1C;
color: #FFEBEE;
font-weight: bolder;
padding: 4px;
vertical-align: middle;
border-radius: 20px;
box-sizing: border-box;
font-size: 9px;
line-height: 7px;
text-align: center;
margin-right: 5px;
}
@@ -1,5 +1,5 @@
import React from 'react';
import styles from './styles.css';
import React, {PropTypes} from 'react';
import styles from './FormField.css';
const FormField = ({className, showErrors = false, errorMsg, label, ...props}) => (
<div className={`${styles.formField} ${className ? className : ''}`}>
@@ -15,4 +15,12 @@ const FormField = ({className, showErrors = false, errorMsg, label, ...props}) =
</div>
);
FormField.propTypes = {
label: PropTypes.string,
value: PropTypes.string,
onChange: PropTypes.func,
errorMsg: PropTypes.string,
type: PropTypes.string
};
export default FormField;
+2
View File
@@ -13,4 +13,6 @@ export {default as Icon} from './components/Icon';
export {default as List} from './components/List';
export {default as Item} from './components/Item';
export {default as Card} from './components/Card';
export {default as FormField} from './components/FormField';
export {default as Success} from './components/Success';
export {default as Pager} from './components/Pager';