Merge branch 'master' into OS-130-create-system-preferences-view

This commit is contained in:
ivan 2017-01-04 15:28:51 -03:00
commit 37ae4b1bfe
15 changed files with 208 additions and 70 deletions

View File

@ -1,22 +1,24 @@
import React from 'react';
import {connect} from 'react-redux';
import classNames from 'classnames';
import languageList from 'data/language-list';
import DropDown from 'core-components/drop-down';
const codeLanguages = {
'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']);
const languageCodes = Object.keys(languageList);
class LanguageSelector extends React.Component {
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() {
@ -28,7 +30,7 @@ class LanguageSelector extends React.Component {
getProps() {
return {
className: this.getClass(),
items: this.getLanguageList(),
items: this.getLanguageItems(),
selectedIndex: this.getSelectedIndex(),
onChange: this.changeLanguage.bind(this),
size: this.props.size
@ -45,37 +47,23 @@ class LanguageSelector extends React.Component {
return classNames(classes);
}
getLanguageList() {
return languages.map((language) => {
getLanguageItems() {
return this.getLanguageList().map((languageKey) => {
return {
content: language,
icon: codeLanguages[language]
content: languageList[languageKey].name,
icon: (languageKey === 'en') ? 'us' : languageKey
};
});
}
getSelectedIndex() {
let selectedIndex = languages.map((key) => codeLanguages[key]).indexOf(this.getPropLanguage());
let selectedIndex = this.getLanguageList().indexOf(this.props.value);
return (selectedIndex != -1) ? selectedIndex : undefined;
}
getPropLanguage() {
let language = this.props.value;
if (language === 'en') {
language = 'us';
}
return language;
return (selectedIndex !== -1) ? selectedIndex : undefined;
}
changeLanguage(event) {
let language = codeLanguages[languages[event.index]];
if (language === 'us') {
language = 'en';
}
let language = this.getLanguageList()[event.index];
if (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 i18n from 'lib-app/i18n';
import Icon from 'core-components/icon';
class TicketInfo extends React.Component {
static propTypes = {
@ -13,10 +14,14 @@ class TicketInfo extends React.Component {
<div className="ticket-info">
<div className="ticket-info__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 className="ticket-info__description">
{this.props.ticket.content}
</div>
<div className="ticket-info__author">
{i18n('AUTHOR')}: {this.props.ticket.author.name}
</div>

View File

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

View File

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

View File

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

View File

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

View File

@ -13,7 +13,9 @@ module.exports = [
{id: 1, name: 'Sales Support', owners: 2},
{id: 2, name: 'Technical Issues', owners: 5},
{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 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
};
import languageList from 'data/language-list';
const i18nData = function (key, lang) {
return (languages[lang] && languages[lang][key]) || key;
return (languageList[lang] && languageList[lang].data[key]) || key;
};
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('reCaptchaKey', configs.reCaptchaKey);
this.setItem('departments', JSON.stringify(configs.departments));
this.setItem('allowedLanguages', JSON.stringify(configs.allowedLanguages));
this.setItem('supportedLanguages', JSON.stringify(configs.supportedLanguages));
}
getConfigs() {
return {
language: this.getItem('language'),
reCaptchaKey: this.getItem('reCaptchaKey'),
departments: this.getDepartments()
departments: this.getDepartments(),
allowedLanguages: JSON.parse(this.getItem('allowedLanguages')),
supportedLanguages: JSON.parse(this.getItem('supportedLanguages'))
};
}

View File

@ -10,6 +10,7 @@ require_once 'staff/add.php';
require_once 'staff/get-all.php';
require_once 'staff/delete.php';
require_once 'staff/edit.php';
require_once 'staff/last-events.php';
$systemControllerGroup = new ControllerGroup();
$systemControllerGroup->setGroupPath('/staff');
@ -25,5 +26,6 @@ $systemControllerGroup->addController(new AddStaffController);
$systemControllerGroup->addController(new GetAllStaffController);
$systemControllerGroup->addController(new DeleteStaffController);
$systemControllerGroup->addController(new EditStaffController);
$systemControllerGroup->addController(new LastEventsStaffController);
$systemControllerGroup->finalize();

View File

@ -0,0 +1,34 @@
<?php
use Respect\Validation\Validator as DataValidator;
class LastEventsStaffController extends Controller {
const PATH = '/last-events';
public function validations() {
return [
'permission' => 'staff_1',
'requestData' => [
'page' => [
'validation' => DataValidator::numeric(),
'error' => ERRORS::INVALID_PAGE
]
]
];
}
public function handler() {
$page = Controller::request('page');
$user = Controller::getLoggedUser();
$query = ' (';
foreach ($user->sharedTicketList as $ticket) {
$query .= 'ticket_id =' . $ticket->id . ' OR ';
}
$query = substr($query,0,-3);
$query .= ') ORDER BY id desc LIMIT ? OFFSET ?' ;
$eventList = Ticketevent::find($query, [10, 10*($page-1)+1]);
Response::respondSuccess($eventList->toArray());
}
}

View File

@ -58,4 +58,17 @@ class Ticketevent extends DataStore {
return new NullDataStore();
}
public function toArray() {
$user = ($this->authorUser instanceof User) ? $this->authorUser : $this->authorStaff;
return [
'type' => $this->type,
'ticketNumber' => $this->ticket->ticketNumber,
'author' => [
'name' => $user->name,
'id' => $user->id
]
];
}
}

View File

@ -47,5 +47,4 @@ require './staff/get-all.rb'
require './system/add-department.rb'
require './system/edit-department.rb'
require './system/delete-department.rb'
require './staff/last-events.rb'

View File

@ -0,0 +1,16 @@
describe '/staff/last-events' do
request('/user/logout')
Scripts.login($staff[:email], $staff[:password], true)
it 'should get last events' do
result = request('/staff/last-events', {
page: 1,
csrf_userid: $csrf_userid,
csrf_token: $csrf_token
})
(result['status']).should.equal('success')
(result['data'].size).should.equal(10)
end
end