#96: contact us popup and footer links

This commit is contained in:
SleepWalker 2016-05-22 20:25:38 +03:00
parent 8015626170
commit f68f28a291
20 changed files with 332 additions and 3 deletions

View File

@ -4,6 +4,7 @@ import { FormattedMessage as Message } from 'react-intl';
import { Button } from 'components/ui/form'; import { Button } from 'components/ui/form';
import { LangMenu } from 'components/langMenu'; import { LangMenu } from 'components/langMenu';
import { FooterMenu } from 'components/footerMenu';
import styles from './appInfo.scss'; import styles from './appInfo.scss';
import messages from './AppInfo.intl.json'; import messages from './AppInfo.intl.json';
@ -39,6 +40,7 @@ export default class AppInfo extends Component {
</div> </div>
<div className={styles.langMenu}> <div className={styles.langMenu}>
<FooterMenu skin="light" />
<LangMenu /> <LangMenu />
</div> </div>
</div> </div>

View File

@ -56,4 +56,6 @@
bottom: 10px; bottom: 10px;
left: 0; left: 0;
right: 0; right: 0;
text-align: center;
line-height: 1.5;
} }

View File

@ -0,0 +1,78 @@
import React, { Component, PropTypes } from 'react';
import classNames from 'classnames';
import { FormattedMessage as Message } from 'react-intl';
import { Input, TextArea, Button, Form, FormModel } from 'components/ui/form';
import site from 'services/api/site';
import icons from 'components/ui/icons.scss';
import styles from './contactForm.scss';
import messages from './contactForm.intl.json';
export default class ContactForm extends Component {
static displayName = 'ContactForm';
static propTypes = {
onClose: PropTypes.func.isRequired
};
form = new FormModel();
render() {
const {onClose} = this.props;
const {form} = this;
return (
<div className={styles.contactForm}>
<div className={styles.header}>
<h2 className={styles.title}>
<Message {...messages.title} />
</h2>
<span className={classNames(icons.close, styles.close)} onClick={onClose} />
</div>
<Form form={form} onSubmit={this.onSubmit}>
<Input
{...form.bindField('subject')}
required
label={messages.subject}
skin="light"
/>
<Input
{...form.bindField('email')}
required
label={messages.email}
type="email"
skin="light"
/>
<TextArea
{...form.bindField('message')}
required
label={messages.message}
skin="light"
/>
<div className={styles.footer}>
<Button label={messages.send} block />
</div>
</Form>
</div>
);
}
onSubmit = () => {
site.contact(this.form.serialize())
.then(this.props.onClose)
.catch((resp) => {
if (resp.errors) {
this.form.setErrors(resp.errors);
}
return Promise.reject(resp);
})
;
};
}

View File

@ -0,0 +1,7 @@
{
"title": "Contact Us",
"subject": "Subject",
"email": "E-mail",
"message": "Message",
"send": "Send"
}

View File

@ -0,0 +1,31 @@
.contactForm {
}
.header {
position: relative;
margin: -30px;
margin-bottom: 0;
padding: 10px 10px 0;
border-bottom: 1px solid #D6D6D6;
background: #F5F5F5;
}
.title {
color: #444;
font-size: 30px;
line-height: 68px;
text-align: center;
}
.close {
position: absolute;
right: 10px;
top: 10px;
cursor: pointer;
}
.footer {
margin: -30px;
margin-top: 0;
}

View File

@ -0,0 +1,43 @@
import React, { Component, PropTypes } from 'react';
import { Link } from 'react-router';
import { FormattedMessage as Message } from 'react-intl';
import styles from './footerMenu.scss';
import messages from './footerMenu.intl.json';
export default class FooterMenu extends Component {
static displayName = 'FooterMenu';
static propTypes = {
skin: PropTypes.oneOf(['light', 'dark']),
createPopup: PropTypes.func.isRequired
};
render() {
return (
<div className={styles.footerMenu}>
<Link to="/rules">
<Message {...messages.rules} />
</Link>
{' | '}
<a href="#" onClick={this.onContact}>
<Message {...messages.contactUs} />
</a>
</div>
);
}
onContact = (event) => {
event.preventDefault();
this.props.createPopup();
};
}
import { connect } from 'react-redux';
import ContactForm from 'components/contact/ContactForm';
import { create as createPopup } from 'components/ui/popup/actions';
export default connect(null, {
createPopup: () => createPopup(ContactForm)
})(FooterMenu);

View File

@ -0,0 +1,4 @@
{
"rules": "Rules",
"contactUs": "Contact Us"
}

View File

@ -0,0 +1,16 @@
.footerMenu {
font-size: 12px;
color: #666;
a {
color: #666;
border-bottom: 1px dotted #666;
text-decoration: none;
transition: .25s;
&:hover {
border-bottom-color: #777;
color: #777;
}
}
}

View File

@ -0,0 +1,5 @@
import FooterMenu from './FooterMenu';
export {
FooterMenu
};

View File

@ -165,6 +165,7 @@ export default class ChangeEmail extends Component {
{this[`renderStep${step}`]({ {this[`renderStep${step}`]({
email, email,
code, code,
isCodeSpecified,
form, form,
isActiveStep: step === activeStep isActiveStep: step === activeStep
})} })}

View File

@ -0,0 +1,81 @@
import React, { PropTypes } from 'react';
import classNames from 'classnames';
import { uniqueId } from 'functions';
import styles from './form.scss';
import FormInputComponent from './FormInputComponent';
export default class TextArea extends FormInputComponent {
static displayName = 'TextArea';
static propTypes = {
placeholder: PropTypes.oneOfType([
PropTypes.shape({
id: PropTypes.string
}),
PropTypes.string
]),
label: PropTypes.oneOfType([
PropTypes.shape({
id: PropTypes.string
}),
PropTypes.string
]),
error: PropTypes.string,
skin: PropTypes.oneOf(['dark', 'light']),
color: PropTypes.oneOf(['green', 'blue', 'red', 'lightViolet', 'darkBlue', 'violet'])
};
render() {
let { color = 'green', skin = 'dark', label } = this.props;
const props = {
type: 'text',
...this.props
};
if (label) {
if (!props.id) {
props.id = uniqueId('textarea');
}
label = this.formatMessage(label);
label = (
<label className={styles.textFieldLabel} htmlFor={props.id}>
{label}
</label>
);
}
props.placeholder = this.formatMessage(props.placeholder);
return (
<div className={styles.formRow}>
{label}
<div className={styles.textAreaContainer}>
<textarea ref={this.setEl}
className={classNames(
styles.textArea,
styles[`${skin}TextField`],
styles[`${color}TextField`]
)}
{...props}
/>
</div>
{this.renderError()}
</div>
);
}
getValue() {
return this.el.value;
}
focus() {
this.el.focus();
setTimeout(this.el.focus.bind(this.el), 10);
}
}

View File

@ -153,6 +153,17 @@
} }
} }
.textAreaContainer {
height: auto;
}
.textArea {
height: 150px;
padding: 5px 10px;
resize: vertical;
position: relative;
}
@include input-theme('green', $green); @include input-theme('green', $green);
@include input-theme('blue', $blue); @include input-theme('blue', $blue);
@include input-theme('red', $red); @include input-theme('red', $red);

View File

@ -1,4 +1,5 @@
import Input from './Input'; import Input from './Input';
import TextArea from './TextArea';
import Checkbox from './Checkbox'; import Checkbox from './Checkbox';
import Button from './Button'; import Button from './Button';
import Form from './Form'; import Form from './Form';
@ -6,6 +7,7 @@ import FormModel from './FormModel';
export { export {
Input, Input,
TextArea,
Button, Button,
Checkbox, Checkbox,
Form, Form,

View File

@ -10,9 +10,11 @@
background: rgba(255, 255, 255, 0.8); background: rgba(255, 255, 255, 0.8);
text-align: center; text-align: center;
vertical-align: middle;
white-space: nowrap; white-space: nowrap;
overflow-x: hidden;
overflow-y: auto;
&:before { &:before {
content: ''; content: '';
display: inline-block; display: inline-block;
@ -25,6 +27,8 @@
.popup { .popup {
display: inline-block; display: inline-block;
white-space: normal; white-space: normal;
text-align: left;
vertical-align: middle;
width: 400px; width: 400px;
max-width: 90%; max-width: 90%;

View File

@ -57,6 +57,13 @@
"components.auth.register.termsOfService": "terms of service", "components.auth.register.termsOfService": "terms of service",
"components.auth.register.yourEmail": "Your E-mail", "components.auth.register.yourEmail": "Your E-mail",
"components.auth.register.yourNickname": "Your nickname", "components.auth.register.yourNickname": "Your nickname",
"components.contact.email": "E-mail",
"components.contact.message": "Message",
"components.contact.send": "Send",
"components.contact.subject": "Subject",
"components.contact.title": "Contact Us",
"components.footerMenu.contactUs": "Contact Us",
"components.footerMenu.rules": "Rules",
"components.langMenu.siteLanguage": "Site language", "components.langMenu.siteLanguage": "Site language",
"components.profile.accountDescription": "Ely.by account allows you to get access to many Minecraft resources. Please, take care of your account safety. Use secure password and change it regularly.", "components.profile.accountDescription": "Ely.by account allows you to get access to many Minecraft resources. Please, take care of your account safety. Use secure password and change it regularly.",
"components.profile.accountPreferencesTitle": "Ely.by account preferences", "components.profile.accountPreferencesTitle": "Ely.by account preferences",

View File

@ -57,6 +57,13 @@
"components.auth.register.termsOfService": "правилами сервиса", "components.auth.register.termsOfService": "правилами сервиса",
"components.auth.register.yourEmail": "Ваш E-mail", "components.auth.register.yourEmail": "Ваш E-mail",
"components.auth.register.yourNickname": "Желаемый ник", "components.auth.register.yourNickname": "Желаемый ник",
"components.contact.email": "E-mail",
"components.contact.message": "Сообщение",
"components.contact.send": "Отправить",
"components.contact.subject": "Тема",
"components.contact.title": "Обратная связь",
"components.footerMenu.contactUs": "Обратная связь",
"components.footerMenu.rules": "Правила сайта",
"components.langMenu.siteLanguage": "Язык сайта", "components.langMenu.siteLanguage": "Язык сайта",
"components.profile.accountDescription": "Благодаря аккаунту Ely.by вы можете получить доступ ко многим ресурсам, связанным с Minecraft. Берегите свой аккаунт, используйте надёжный пароль и регулярно его меняйте.", "components.profile.accountDescription": "Благодаря аккаунту Ely.by вы можете получить доступ ко многим ресурсам, связанным с Minecraft. Берегите свой аккаунт, используйте надёжный пароль и регулярно его меняйте.",
"components.profile.accountPreferencesTitle": "Настройки аккаунта Ely.by", "components.profile.accountPreferencesTitle": "Настройки аккаунта Ely.by",

View File

@ -1,7 +1,5 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { connect } from 'react-redux';
import ProfilePage from 'pages/profile/ProfilePage'; import ProfilePage from 'pages/profile/ProfilePage';
import Profile from 'components/profile/Profile'; import Profile from 'components/profile/Profile';
@ -17,6 +15,8 @@ class IndexPage extends Component {
} }
} }
import { connect } from 'react-redux';
export default connect((state) => ({ export default connect((state) => ({
user: state.user user: state.user
}))(IndexPage); }))(IndexPage);

View File

@ -1,5 +1,7 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FooterMenu } from 'components/footerMenu';
import styles from './profile.scss'; import styles from './profile.scss';
class ProfilePage extends Component { class ProfilePage extends Component {
@ -27,6 +29,10 @@ class ProfilePage extends Component {
return ( return (
<div className={styles.container}> <div className={styles.container}>
{this.props.children} {this.props.children}
<div className={styles.footer}>
<FooterMenu />
</div>
</div> </div>
); );
} }

View File

@ -1,3 +1,11 @@
.container { .container {
margin-top: 55px; margin-top: 55px;
} }
.footer {
text-align: center;
position: fixed;
bottom: 10px;
width: 100%;
left: 0;
}

14
src/services/api/site.js Normal file
View File

@ -0,0 +1,14 @@
import request from 'services/request';
export default {
contact({
subject = '',
email = '',
message = ''
}) {
return request.post(
'/api/site/contact',
{subject, email, message}
);
}
};