Merged in 133-language-architecture-frontend (pull request #106)

Ivan - Add supported and allowed languages architecture for frontend
This commit is contained in:
Ivan Diaz 2017-01-04 15:08:41 -03:00
commit 28ae0d1bd1
10 changed files with 142 additions and 68 deletions

View File

@ -1,22 +1,24 @@
import React from 'react'; import React from 'react';
import {connect} from 'react-redux';
import classNames from 'classnames'; import classNames from 'classnames';
import languageList from 'data/language-list';
import DropDown from 'core-components/drop-down'; import DropDown from 'core-components/drop-down';
const codeLanguages = { const languageCodes = Object.keys(languageList);
'English': 'us',
'Spanish': 'es',
'German': 'de',
'French': 'fr',
'Chinese': 'cn',
'Turkish': 'tr',
'Indian': 'in'
};
const languages = Object.keys(codeLanguages);
const languageCodes = languages.map((key) => { return codeLanguages[key]; }).concat(['en']);
class LanguageSelector extends React.Component { class LanguageSelector extends React.Component {
static propTypes = { static propTypes = {
value: React.PropTypes.oneOf(languageCodes) value: React.PropTypes.oneOf(languageCodes),
type: React.PropTypes.oneOf(['allowed', 'supported']),
allowedLanguages: React.PropTypes.array,
supportedLanguages: React.PropTypes.array
};
static defaultProps = {
type: 'allowed',
allowedLanguages: languageCodes,
supportedLanguages: languageCodes
}; };
render() { render() {
@ -28,7 +30,7 @@ class LanguageSelector extends React.Component {
getProps() { getProps() {
return { return {
className: this.getClass(), className: this.getClass(),
items: this.getLanguageList(), items: this.getLanguageItems(),
selectedIndex: this.getSelectedIndex(), selectedIndex: this.getSelectedIndex(),
onChange: this.changeLanguage.bind(this), onChange: this.changeLanguage.bind(this),
size: this.props.size size: this.props.size
@ -45,37 +47,23 @@ class LanguageSelector extends React.Component {
return classNames(classes); return classNames(classes);
} }
getLanguageList() { getLanguageItems() {
return languages.map((language) => { return this.getLanguageList().map((languageKey) => {
return { return {
content: language, content: languageList[languageKey].name,
icon: codeLanguages[language] icon: (languageKey === 'en') ? 'us' : languageKey
}; };
}); });
} }
getSelectedIndex() { getSelectedIndex() {
let selectedIndex = languages.map((key) => codeLanguages[key]).indexOf(this.getPropLanguage()); let selectedIndex = this.getLanguageList().indexOf(this.props.value);
return (selectedIndex != -1) ? selectedIndex : undefined; return (selectedIndex !== -1) ? selectedIndex : undefined;
}
getPropLanguage() {
let language = this.props.value;
if (language === 'en') {
language = 'us';
}
return language;
} }
changeLanguage(event) { changeLanguage(event) {
let language = codeLanguages[languages[event.index]]; let language = this.getLanguageList()[event.index];
if (language === 'us') {
language = 'en';
}
if (this.props.onChange) { if (this.props.onChange) {
this.props.onChange({ this.props.onChange({
@ -85,6 +73,15 @@ class LanguageSelector extends React.Component {
}); });
} }
} }
getLanguageList() {
return (this.props.type === 'supported') ? this.props.supportedLanguages : this.props.allowedLanguages;
}
} }
export default LanguageSelector; export default connect((store) => {
return {
allowedLanguages: store.config.allowedLanguages,
supportedLanguages: store.config.supportedLanguages
};
})(LanguageSelector);

View File

@ -2,6 +2,7 @@ import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import i18n from 'lib-app/i18n'; import i18n from 'lib-app/i18n';
import Icon from 'core-components/icon';
class TicketInfo extends React.Component { class TicketInfo extends React.Component {
static propTypes = { static propTypes = {
@ -13,10 +14,14 @@ class TicketInfo extends React.Component {
<div className="ticket-info"> <div className="ticket-info">
<div className="ticket-info__title"> <div className="ticket-info__title">
{this.props.ticket.title} {this.props.ticket.title}
<span className="ticket-info__flag">
<Icon name={(this.props.ticket.language === 'en') ? 'us' : this.props.ticket.language}/>
</span>
</div> </div>
<div className="ticket-info__description"> <div className="ticket-info__description">
{this.props.ticket.content} {this.props.ticket.content}
</div> </div>
<div className="ticket-info__author"> <div className="ticket-info__author">
{i18n('AUTHOR')}: {this.props.ticket.author.name} {i18n('AUTHOR')}: {this.props.ticket.author.name}
</div> </div>

View File

@ -9,6 +9,10 @@
font-size: $font-size--md; font-size: $font-size--md;
} }
&__flag {
margin-left: 10px;
}
&__description { &__description {
margin-top: 5px; margin-top: 5px;
font-size: small; font-size: small;

View File

@ -15,6 +15,7 @@ import SubmitButton from 'core-components/submit-button';
import DropDown from 'core-components/drop-down'; import DropDown from 'core-components/drop-down';
import Button from 'core-components/button'; import Button from 'core-components/button';
import Message from 'core-components/message'; import Message from 'core-components/message';
import Icon from 'core-components/icon';
class TicketViewer extends React.Component { class TicketViewer extends React.Component {
static propTypes = { static propTypes = {
@ -48,6 +49,9 @@ class TicketViewer extends React.Component {
<div className="ticket-viewer__header row"> <div className="ticket-viewer__header row">
<span className="ticket-viewer__number">#{ticket.ticketNumber}</span> <span className="ticket-viewer__number">#{ticket.ticketNumber}</span>
<span className="ticket-viewer__title">{ticket.title}</span> <span className="ticket-viewer__title">{ticket.title}</span>
<span className="ticket-viewer__flag">
<Icon name={(ticket.language === 'en') ? 'us' : ticket.language}/>
</span>
</div> </div>
{this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()} {this.props.editable ? this.renderEditableHeaders() : this.renderHeaders()}
<div className="ticket-viewer__content"> <div className="ticket-viewer__content">

View File

@ -1,6 +1,7 @@
@import "../scss/vars"; @import "../scss/vars";
.ticket-viewer { .ticket-viewer {
&__header { &__header {
background-color: $primary-blue; background-color: $primary-blue;
border-top-right-radius: 4px; border-top-right-radius: 4px;
@ -20,6 +21,10 @@
display: inline-block; display: inline-block;
} }
&__flag {
margin-left: 10px;
}
&__info-row-header { &__info-row-header {
background-color: $light-grey; background-color: $light-grey;
font-weight: bold; font-weight: bold;

View File

@ -2,12 +2,14 @@ import React from 'react';
import _ from 'lodash'; import _ from 'lodash';
import ReCAPTCHA from 'react-google-recaptcha'; import ReCAPTCHA from 'react-google-recaptcha';
import { browserHistory } from 'react-router'; import { browserHistory } from 'react-router';
import RichTextEditor from 'react-rte-browserify';
import i18n from 'lib-app/i18n'; import i18n from 'lib-app/i18n';
import API from 'lib-app/api-call'; import API from 'lib-app/api-call';
import SessionStore from 'lib-app/session-store'; import SessionStore from 'lib-app/session-store';
import store from 'app/store'; import store from 'app/store';
import SessionActions from 'actions/session-actions'; import SessionActions from 'actions/session-actions';
import LanguageSelector from 'app-components/language-selector';
import Header from 'core-components/header'; import Header from 'core-components/header';
import Form from 'core-components/form'; import Form from 'core-components/form';
@ -25,29 +27,34 @@ class CreateTicketForm extends React.Component {
userLogged: true userLogged: true
}; };
constructor(props) { state = {
super(props);
this.state = {
loading: false, loading: false,
message: null message: null,
}; form: {
title: '',
content: RichTextEditor.createEmptyValue(),
language: 'en'
} }
};
render() { render() {
return ( return (
<div className="create-ticket-form"> <div className="create-ticket-form">
<Header title={i18n('CREATE_TICKET')} description={i18n('CREATE_TICKET_DESCRIPTION')} /> <Header title={i18n('CREATE_TICKET')} description={i18n('CREATE_TICKET_DESCRIPTION')} />
<Form loading={this.state.loading} onSubmit={this.onSubmit.bind(this)}> <Form {...this.getFormProps()}>
{(!this.props.userLogged) ? this.renderEmailAndName() : null} {(!this.props.userLogged) ? this.renderEmailAndName() : null}
<FormField label={i18n('TITLE')} name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}}/>
<div className="row"> <div className="row">
<FormField className="col-md-7" label="Title" name="title" validation="TITLE" required field="input" fieldProps={{size: 'large'}}/> <FormField className="col-md-5" label={i18n('DEPARTMENT')} name="departmentIndex" field="select" fieldProps={{
<FormField className="col-md-5" label="Department" name="departmentIndex" field="select" fieldProps={{
items: SessionStore.getDepartments().map((department) => {return {content: department.name}}), items: SessionStore.getDepartments().map((department) => {return {content: department.name}}),
size: 'medium' size: 'medium'
}} /> }} />
<FormField className="col-md-5" label={i18n('LANGUAGE')} name="language" field="select" decorator={LanguageSelector} fieldProps={{
type: 'supported',
size: 'medium'
}}/>
</div> </div>
<FormField label="Content" name="content" validation="TEXT_AREA" required field="textarea" /> <FormField label={i18n('CONTENT')} name="content" validation="TEXT_AREA" required field="textarea" />
{(!this.props.userLogged) ? this.renderCaptcha() : null} {(!this.props.userLogged) ? this.renderCaptcha() : null}
<SubmitButton>Create Ticket</SubmitButton> <SubmitButton>Create Ticket</SubmitButton>
</Form> </Form>
@ -84,6 +91,15 @@ class CreateTicketForm extends React.Component {
} }
} }
getFormProps() {
return {
loading: this.state.loading,
onSubmit: this.onSubmit.bind(this),
values: this.state.form,
onChange: form => this.setState({form})
};
}
onSubmit(formState) { onSubmit(formState) {
this.setState({ this.setState({
loading: true loading: true

View File

@ -13,7 +13,9 @@ module.exports = [
{id: 1, name: 'Sales Support', owners: 2}, {id: 1, name: 'Sales Support', owners: 2},
{id: 2, name: 'Technical Issues', owners: 5}, {id: 2, name: 'Technical Issues', owners: 5},
{id: 3, name: 'System and Administration', owners: 0} {id: 3, name: 'System and Administration', owners: 0}
] ],
'allowedLanguages': ['en', 'es', 'de', 'fr', 'pt', 'jp', 'ru', 'cn', 'in', 'tr'],
'supportedLanguages': ['en', 'es', 'de']
} }
}; };
} }

View File

@ -1,23 +1,7 @@
import englishLanguage from 'data/languages/en'; import languageList from 'data/language-list';
import spanishLanguage from 'data/languages/en';
import germanLanguage from 'data/languages/en';
import frenchLanguage from 'data/languages/en';
import chineseLanguage from 'data/languages/en';
import turkishLanguage from 'data/languages/en';
import indianLanguage from 'data/languages/en';
const languages = {
'en': englishLanguage,
'es': spanishLanguage,
'de': germanLanguage,
'fr': frenchLanguage,
'cn': chineseLanguage,
'tr': turkishLanguage,
'in': indianLanguage
};
const i18nData = function (key, lang) { const i18nData = function (key, lang) {
return (languages[lang] && languages[lang][key]) || key; return (languageList[lang] && languageList[lang].data[key]) || key;
}; };
export default i18nData export default i18nData

View File

@ -0,0 +1,53 @@
import englishLanguage from 'data/languages/en';
import spanishLanguage from 'data/languages/en';
import germanLanguage from 'data/languages/en';
import frenchLanguage from 'data/languages/en';
import portugueseLanguage from 'data/languages/en';
import japaneseLanguage from 'data/languages/en';
import russianLanguage from 'data/languages/en';
import chineseLanguage from 'data/languages/en';
import turkishLanguage from 'data/languages/en';
import indianLanguage from 'data/languages/en';
export default {
'en': {
name: 'English',
data: englishLanguage
},
'es': {
name: 'Spanish',
data: spanishLanguage
},
'de': {
name: 'German',
data: germanLanguage
},
'fr': {
name: 'French',
data: frenchLanguage
},
'pt': {
name: 'Portuguese',
data: portugueseLanguage
},
'jp': {
name: 'Japanese',
data: japaneseLanguage
},
'ru': {
name: 'Russian',
data: russianLanguage
},
'cn': {
name: 'Chinese',
data: chineseLanguage
},
'tr': {
name: 'Turkish',
data: turkishLanguage
},
'in': {
name: 'Indian',
data: indianLanguage
}
};

View File

@ -56,13 +56,17 @@ class SessionStore {
this.setItem('language', configs.language); this.setItem('language', configs.language);
this.setItem('reCaptchaKey', configs.reCaptchaKey); this.setItem('reCaptchaKey', configs.reCaptchaKey);
this.setItem('departments', JSON.stringify(configs.departments)); this.setItem('departments', JSON.stringify(configs.departments));
this.setItem('allowedLanguages', JSON.stringify(configs.allowedLanguages));
this.setItem('supportedLanguages', JSON.stringify(configs.supportedLanguages));
} }
getConfigs() { getConfigs() {
return { return {
language: this.getItem('language'), language: this.getItem('language'),
reCaptchaKey: this.getItem('reCaptchaKey'), reCaptchaKey: this.getItem('reCaptchaKey'),
departments: this.getDepartments() departments: this.getDepartments(),
allowedLanguages: JSON.parse(this.getItem('allowedLanguages')),
supportedLanguages: JSON.parse(this.getItem('supportedLanguages'))
}; };
} }