mirror of
https://github.com/opensupports/opensupports.git
synced 2025-07-27 07:44:29 +02:00
Merged in 133-language-architecture-frontend (pull request #106)
Ivan - Add supported and allowed languages architecture for frontend
This commit is contained in:
commit
28ae0d1bd1
@ -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);
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
||||||
|
@ -14,7 +14,8 @@ import FormField from 'core-components/form-field';
|
|||||||
import SubmitButton from 'core-components/submit-button';
|
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">
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
loading: false,
|
||||||
|
message: null,
|
||||||
this.state = {
|
form: {
|
||||||
loading: false,
|
title: '',
|
||||||
message: null
|
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
|
||||||
|
@ -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']
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
53
client/src/data/language-list.js
Normal file
53
client/src/data/language-list.js
Normal 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
|
||||||
|
}
|
||||||
|
};
|
@ -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'))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user